/***********************************************************************************

    Copyright (C) 2007-2018 Ahmet Öztürk (aoz_2@yahoo.com)

    This file is part of Lifeograph.

    Lifeograph 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 3 of the License, or
    (at your option) any later version.

    Lifeograph 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 Lifeograph.  If not, see <http://www.gnu.org/licenses/>.

***********************************************************************************/


#ifndef LIFEOGRAPH_PRINTING_HEADER
#define LIFEOGRAPH_PRINTING_HEADER


#include <pangomm.h>
#include <gtkmm.h>
#include <vector>

#include "entry.hpp"
#include "entry_parser.hpp"


namespace LIFEO
{

class Printable
{
    public:
        enum Type { PT_PAGE_BREAK, PT_TEXT, PT_IMAGE, PT_ENTRY_BREAK, PT_END };

        virtual ~Printable() {}

        virtual Type            get_type() const = 0;
        virtual void            render( const Glib::RefPtr< Gtk::PrintContext >& ) {}
};

class PrintablePageBreak : public Printable
{
    public:
        Type                    get_type() const { return PT_PAGE_BREAK; }
};

class PrintableEntryBreak : public Printable
{
    public:
        Type                    get_type() const { return PT_ENTRY_BREAK; }
};

class PrintableText : public Printable
{
    public:
        PrintableText( const Glib::RefPtr< Pango::Layout > &layout, int o, int b, int e )
        : m_v_offset( o ), m_layout( layout ), m_line_begin( b ), m_line_end( e ) { }

        Type                    get_type() const { return PT_TEXT; }
        void                    render( const Glib::RefPtr< Gtk::PrintContext >& );

        int                     m_v_offset;
        Glib::RefPtr< Pango::Layout >
                                m_layout;
        int                     m_line_begin, m_line_end;
};

class PrintableImage : public Printable
{
    public:
        PrintableImage( Glib::RefPtr< Gdk::Pixbuf > pixbuf, int o )
        : m_v_offset( o ), m_pixbuf( pixbuf ) { }

        Type                    get_type() const { return PT_IMAGE; }
        void                    render( const Glib::RefPtr< Gtk::PrintContext >& );

        int                     m_v_offset;
        Glib::RefPtr< Gdk::Pixbuf > m_pixbuf;
};


class EntryParserPango : public EntryParser
{
    public:
                                EntryParserPango()
        {
            m_flag_handle_word = false; // used in spell checking
        }

        void                    set( const Entry* );
        Printable::Type         parse();

        Glib::ustring           m_pango_text;
        Glib::ustring::size_type    m_pango_begin{ 0 };
        Glib::RefPtr< Gdk::Pixbuf > m_pango_pixbuf;
        Printable::Type         m_pango_return{ Printable::PT_TEXT };

        bool                    option_use_theme_font{ false };
        bool                    option_hide_comments{ true };
        int                     m_max_thumbnail_width{ 0 };

    protected:
        void                    apply_regular();
        void                    apply_markup( const Glib::ustring&, const Glib::ustring&,
                                              int = 0, int = 1 );
        void                    apply_heading_end();
        void                    apply_subheading();
        void                    apply_subheading_end();
        void                    apply_bold();
        void                    apply_italic();
        void                    apply_strikethrough();
        void                    apply_comment();
        void                    apply_highlight();
        void                    apply_check_ccl();
        void                    apply_check_ccl_end();
        void                    apply_check_unf() override;
        void                    apply_check_prg() override;
        void                    apply_check_fin();
        void                    apply_check_fin_end();
        void                    apply_link();
        void                    apply_link_id() { apply_link(); }
        void                    apply_link_date() { apply_link(); }

        Wchar                   get_char_at( int );

        const Entry*            m_ptr2entry{ nullptr };
};


class PrintOpr : public Gtk::PrintOperation
{
    public:
        typedef std::list< Printable* > Printables;

        virtual                 ~PrintOpr();

        static Glib::RefPtr< PrintOpr >
                                create();

        void                    show_page_setup();

        // PRINTING
        void                    print_or_preview( Gtk::PrintOperationAction );
        void                    set_hide_comments( bool );

        void                    handle_text( const Glib::RefPtr< Gtk::PrintContext >& );
        void                    handle_entry_break( const Glib::RefPtr< Gtk::PrintContext >& );
        void                    handle_image( const Glib::RefPtr< Gtk::PrintContext >& );

        // Printing-related objects:
        Glib::RefPtr< Gtk::PageSetup >
                                m_refPageSetup;
        Glib::RefPtr< Gtk::PrintSettings >
                                m_refSettings;

    protected:
        PrintOpr();
        Printable::Type         get_more();
        void                    clear_content();
        void                    init_variables( const Glib::RefPtr< Gtk::PrintContext >& );

        // PrintOperation default signal handler overrides:
        void                    on_status_changed();
        void                    on_done( Gtk::PrintOperationResult );
        void                    on_begin_print(
                const Glib::RefPtr< Gtk::PrintContext >& );
        void                    on_draw_page(
                const Glib::RefPtr< Gtk::PrintContext >&, int );
        Gtk::Widget*            on_create_custom_widget();
        void                    on_custom_widget_apply( Gtk::Widget* );

        // HELPERS
        void                    add_page();

        Gtk::RadioButton*       m_rbutton_entry{ nullptr };
        Gtk::RadioButton*       m_rbutton_filtered{ nullptr };
        Gtk::RadioButton*       m_rbutton_diary{ nullptr };

        Gtk::FontButton*        m_fontbutton{ nullptr };
        Gtk::CheckButton*       m_check_use_entry_font{ nullptr };
        Gtk::CheckButton*       m_check_justify{ nullptr };

        // PARSING
        EntryParserPango        m_parser;
        int                     m_n_pages{ 0 };
        Printables              m_content;
        Glib::ustring           m_marked_up_text;
        Date::date_t            m_last_chapter_date{ 0 };
        double                  m_v_offset{ 0.0 };
        const CategoryChapters* m_chapters{ nullptr };
        CategoryChapters::const_reverse_iterator m_iter_chapter;
        EntryIterReverse        m_iter_entry;
        EntryIterReverse        m_iter_entry_end;
        // chapter type of the current round:
        DiaryElement::Type      m_flag_chapter_round{ DiaryElement::ET_NONE };
        bool                    m_flag_align_center{ false };
        Glib::RefPtr< Pango::Layout >
                                m_layout;
        Pango::FontDescription  m_font;
        Cairo::RefPtr< Cairo::Context >
                                m_image_context;
        Cairo::RefPtr< Cairo::ImageSurface >
                                m_image_surface;

        bool                    m_opt_entire_diary{ false }; // not filtered
        bool                    m_opt_one_entry{ false };    // print only the current entry
        bool                    m_opt_justify{ false };
};

}   // end of namespace LIFEO

#endif
