/*
 * File Name: text_base_types.h
 */

/*
 * This file is part of uds-plugin-plaintext.
 *
 * uds-plugin-plaintext is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * uds-plugin-plaintext is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * Copyright (C) 2008 iRex Technologies B.V.
 * All rights reserved.
 */

#ifndef TEXT_BASE_TYPES_H
#define TEXT_BASE_TYPES_H

#include <string>
#include <deque>
#include <pango/pango.h>

namespace text
{

using namespace std;

struct ViewPosition
{
    int x;              ///< X coordinate, in pixels.
    int y;              ///< Y coordinate, in pixels.
};

struct Rect
{
public:
    Rect(int _x, int _y, int _width, int _height)
    : x(_x), y(_y), width(_width), height(_height)
    {
    }
public:
    int x;
    int y;
    int width;
    int height;
};

/// @brief Position type for text plugin.
class Position
{
public:
    /// @brief Constructors and destructors
    explicit Position(unsigned int paragraph_index = 0, unsigned int offset = 0);
    explicit Position(const std::string& from_string);

    Position(const Position& r)
    {
        if (this != &r)
        {
            this->paragraph = r.paragraph;
            this->offset    = r.offset;
        }
    }

    Position& operator=(const Position& r)
    {
        if (this != &r)
        {
            this->paragraph = r.paragraph;
            this->offset    = r.offset;
        }
        return *this;
    }

    friend bool operator==(const Position& l, const Position& r)
    {
        return (l.paragraph == r.paragraph) && (l.offset == r.offset);
    }

    friend bool operator!=(const Position& l, const Position& r)
    {
        return (l.paragraph != r.paragraph) || (l.offset != r.offset);
    }

    friend bool operator<(const Position& l, const Position& r)
    {
        return (l.paragraph < r.paragraph) ||
               (l.paragraph == r.paragraph && l.offset < r.offset);
    }

    friend bool operator>(const Position& l, const Position& r)
    {
        return (l.paragraph > r.paragraph) ||
               (l.paragraph == r.paragraph && l.offset > r.offset);
    }

    friend bool operator<=(const Position& l, const Position& r)
    {
        return (l.paragraph < r.paragraph) ||
               (l.paragraph == r.paragraph && l.offset <= r.offset);
    }

    friend bool operator>=(const Position& l, const Position& r)
    {
        return (l.paragraph > r.paragraph) ||
               (l.paragraph == r.paragraph && l.offset >= r.offset);
    }

    ~Position();

public:
    /// @brief Convert internel position type to string.
    ///  The string-based position can be used as anchors.
    std::string to_string() const;

public:
    unsigned int paragraph;
    unsigned int offset;
};

/// @brief Information about one page, used for pagination.
struct Range
{
public:
    Range()
    : start(Position(0, 0)), end(Position(0, 0))
    {
    }

    Range(const Position& start_pos, const Position& end_pos)
    : start(start_pos), end(end_pos)
    {
    }

public:
    Position start;
    Position end;
};

typedef Range PageInfo;

enum SearchType
{
    SEARCH_ALL,
    SEARCH_NEXT
};

struct SearchContext
{
public:
    SearchContext()
    : search_id(0),
      search_type(SEARCH_ALL),
      from(0, 0),
      pattern(""),
      case_sensitive(true),
      forward(true),
      match_whole_word(true)
    {
    }

    SearchContext(unsigned int     _id,
                  SearchType       _type, 
                  const Position&  _from,
                  const string&    _pattern,
                  bool             _case_sensitive,
                  bool             _forward,
                  bool             _match_whole_word)
    : search_id(_id),
      search_type(_type),
      from(_from),
      pattern(_pattern),
      case_sensitive(_case_sensitive),
      forward(_forward),
      match_whole_word(_match_whole_word)
    {
    }

    SearchContext(const SearchContext& r)
    {
        search_id = r.search_id;
        search_type = r.search_type;
        from = r.from;
        pattern = r.pattern;
        case_sensitive = r.case_sensitive;
        forward = r.forward;
        match_whole_word = r.match_whole_word;
    }

    SearchContext& operator=(const SearchContext& r)
    {
        if (this != &r)
        {
            search_id = r.search_id;
            search_type = r.search_type;
            from = r.from;
            pattern = r.pattern;
            case_sensitive = r.case_sensitive;
            forward = r.forward;
            match_whole_word = r.match_whole_word;
        }

        return *this;
    }

public:
    unsigned int search_id;     ///< Search id, to identify a specific search
    SearchType search_type;     ///< Search type, search all or search next
    Position from;              ///< From which position the search starts
    std::string pattern;        ///< Search pattern
    bool case_sensitive;        ///< Case sensitive
    bool forward;               ///< Search previous or next occurrence
    bool match_whole_word;      ///< Match the whole word
};

template <typename T>
class SafeDeque
{
public:
    /// @brief Constructors and destructors.
    SafeDeque()
    {
        g_static_rw_lock_init(&rw_lock);
    }

    ~SafeDeque()
    {
        g_static_rw_lock_free(&rw_lock);
    }

    // Read operations.
    size_t size()
    {
        g_static_rw_lock_reader_lock(&rw_lock);
        size_t size = data.size();
        g_static_rw_lock_reader_unlock(&rw_lock);
        return size;
    }

    bool empty()
    {
        g_static_rw_lock_reader_lock(&rw_lock);
        bool is_empty = data.empty();
        g_static_rw_lock_reader_unlock(&rw_lock);
        return is_empty;
    }

    T& back()
    {
        g_static_rw_lock_reader_lock(&rw_lock);
        T& back = data.back();
        g_static_rw_lock_reader_unlock(&rw_lock);
        return back;
    }

    T& front()
    {
        g_static_rw_lock_reader_lock(&rw_lock);
        T& front = data.front();
        g_static_rw_lock_reader_unlock(&rw_lock);
        return front;
    }

    T& operator[](unsigned int idx)
    {
        g_static_rw_lock_reader_lock(&rw_lock);
        T& value = data[idx];
        g_static_rw_lock_reader_unlock(&rw_lock);
        return value;
    }

    // Write operations.
    void push_back(const T& value)
    {
        g_static_rw_lock_writer_lock(&rw_lock);
        data.push_back(value);
        g_static_rw_lock_writer_unlock(&rw_lock);
    }

    void push_front(const T& value)
    {
        g_static_rw_lock_writer_lock(&rw_lock);
        data.push_front(value);
        g_static_rw_lock_writer_unlock(&rw_lock);
    }

    void clear()
    {
        g_static_rw_lock_writer_lock(&rw_lock);
        data.clear();
        g_static_rw_lock_writer_unlock(&rw_lock);
    }

private:
    std::deque<T> data;
    GStaticRWLock rw_lock;
};

}; // namespace text

#endif // TEXT_BASE_TYPES_H


