/*
 * File Name: text_view.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_VIEW_H
#define TEXT_VIEW_H

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

#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H

#include "plugin_render_result.h"

#include "thread.h"
#include "signal_slot.h"
#include "text_base_types.h"
#include "text_model.h"
#include "text_controller.h"

namespace text
{

class PaginationTask;

class TextView
{
public:
    /// @brief Constructors and destructors
    TextView(TextModel *m);
    ~TextView();

public:
    /// @brief Initialize pango related stuff
    void initialize();

    /// @brief Release pango related stuff
    void deinitialize();

    void set_controller(TextController* c)
    {
        ctrl = c;
    }

    TextController* get_controller()
    {
        return ctrl;
    }

    /// @brief Pagination
    /// @param start_pos If page table is empty, then this parameter tells
    ///  the view from which position the pagination starts.
    /// @return True if pagination is complete. else false is returned.
    bool paginate(const Position& start_pos);

    /// @brief Reset the page table.
    void clear_pages()
    {
        pages.clear();
    }

    /// @brief Render a page start with start_pos.
    /// @return The position where render ends.
    Position render(unsigned char *bmp, const Position& start_pos);

    const TextModel* get_model() const
    {
        return model;
    }

    /// @brief Get font size
    int get_font_size() const
    {
        return font_size;
    }

    /// @brief Set font size
    bool set_font_size(int font_size);

    /// @brief Get font family
    const std::string& get_font_family() const
    {
        return font_family;
    }

    /// @brief Set font family
    void set_font_family(const std::string& font_family)
    {
        this->font_family = font_family;
    }

    /// @brief Get output display size
    void get_display_size(int& width, int& height)
    {
        width  = display_width;
        height = display_height;
    }

    /// @brief Set output display size
    void set_display_size(unsigned int width, unsigned int height);

    /// @brief Get dpi settings
    unsigned int get_DPI()
    {
        return dpi;
    }

    /// @brief Set dpi
    void set_DPI(unsigned int dpi)
    {
        this->dpi = dpi;
    }

    /// @brief Get color depth
    unsigned int get_color_depth()
    {
        return color_depth;        
    }

    /// @brief Set color depth
    void set_color_depth(unsigned int color_depth)
    {
        this->color_depth = color_depth;
    }

    int get_page_count()
    {
        if (!pages.empty() && pages.back().end == Position(0, 0))
        {
            return static_cast<int>(pages.size());
        }
        return 0;
    }

    /// @brief Check if re-pagination is needed given by anchor to be rendered.
    void check_page_table(const Position& rendering_pos);

    /// @brief Get start anchor of specified page.
    bool get_anchor_by_page(unsigned int page_index, Position& pos);

    /// @brief Get page index given by anchor.
    /// @return The page index of the specified anchor.
    ///  return 0 if page table is not ready.
    unsigned int get_page_index_by_anchor(const Position& anchor);

    /// @brief Get the start anchor of page which contains the specified anchor.
    Position get_page_anchor_by_anchor(const Position& anchor);

    /// @brief Get current page index.
    /// @return The index of page which has just been rendered.
    ///  return 1 if page table is not ready.
    unsigned int get_current_page_index();

    /// @brief Find view position given by document position (anchor).
    /// @param page_anchor Start anchor of current page.
    /// @param doc_pos Input anchor.
    /// @param view_pos Output view position (x,y).
    /// @return True if successfully mapped, otherwise false is returned.
    bool map_doc_pos_to_view_pos(const Position& page_anchor,
                                 const Position& doc_pos,
                                 ViewPosition&   view_pos,
                                 bool            trailing);

    /// @brief Find doc position (anchor) given by view position.
    /// @param page_anchor Start anchor of current page.
    /// @param view_pos Input view position.
    /// @param doc_pos Output anchor.
    /// @return True if successfully mapped, otherwise false is returned.
    bool map_view_pos_to_doc_pos(const Position&     page_anchor,
                                 const ViewPosition& view_pos,
                                 Position&           doc_pos);

    /// @brief Get bounding rectangels given by range.
    bool get_bounding_rectangles(const Position& page_anchor,
                                 std::vector<Rect>& bounding_rect,
                                 const Range& range);

    /// @brief Calculate start position of next page
    /// @param in_pos start position of current page
    /// @param out_pos start position of next page
    /// @return If no next page, false is returned, otherwise return true.
    bool calculate_next_page_pos(const Position& in_pos, Position& out_pos);

    /// @brief Calculate start position of previous page
    /// @param in_pos start position of current page
    /// @param out_pos start position of previous page
    /// @return If no previous page, false is returned, otherwise return true.
    bool calculate_prev_page_pos(const Position& in_pos, Position& out_pos);

    /// @brief Check if repagination is needed.
    bool need_repagination()
    {
        return is_repagination_needed;
    }

public:
    /// @brief signals.
    utils::Signal<unsigned int>               pagination_start_signal;
    utils::Signal<unsigned int, unsigned int> pagination_end_signal;
    utils::Signal<unsigned int,
                  const Position&,
                  const Position&,
                  void  *> render_done_signal;

private:
    /// @brief Initialize pango stuff
    void pango_context_init();

    /// @brief Finalize pango stuff
    void pango_context_final();

    /// @brief Calculate lines per page.
    void calculate_lines_per_page();

    /// @brief Create a layout with current settings, fill it with specified
    ///  string.
    PangoLayout* create_layout(const char *str, int len);

private:
    /// @brief Text model.
    TextModel *model;

    /// @brief Text controller.
    TextController *ctrl;

    /// @brief Font family
    std::string font_family;

    /// @brief Font size.
    int font_size;

    /// @brief Output display width
    unsigned int display_width;

    /// @brief Output display height
    unsigned int display_height;

    /// @brief dpi
    unsigned int dpi;

    /// @brief Color depth
    unsigned int color_depth;

    /// @brief Line height
    unsigned int line_height;

    /// @brief Lines per page
    unsigned int lines_per_page;

    /// @brief Left margin
    unsigned int left_margin;

    /// @brief Right margin
    unsigned int right_margin;

    /// @brief Top margin
    unsigned int top_margin;

    /// @brief Bottom margin
    unsigned int bottom_margin;

    /// @brief Client width
    unsigned int client_width;

    /// @brief Client height
    unsigned int client_height;

    /// @brief The document position which is just being rendered.
    Position rendering_pos;

    /// @brief Page table definition 
    typedef SafeDeque<PageInfo> PageTable;
    PageTable pages;

    /// @brief Pango stuff
    PangoContext         *pango_context;

    /// @brief Mutex to ensure each time only 1 thread can access pango stuff.
    GMutex               *pango_mutex;
    unsigned int         font_hash_code;

    /// @brief A flag indicating re-pagination is needed.
    bool is_repagination_needed;

    /// @brief Number of blank lines in the first page.
    unsigned int blank_lines;
};

}; // namespace text

#endif // TEXT_VIEW_H


