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

    Copyright (C) 2007-2020 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/>.

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


#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "../helpers.hpp"
#include "../lifeograph.hpp"
#include "../app_window.hpp"
#include "widget_textviewtheme.hpp"


using namespace LIFEO;

// PARSER RECIPES ==================================================================================
static ParserText::Recipe::Contents s_rc_link_theme =
        { CF_ALPHA, { Ch_TAB, &ParserText::junction_link } };

// LINKS ===========================================================================================
LinkTheme::LinkTheme( const Glib::RefPtr< Gtk::TextMark >& start,
                      const Glib::RefPtr< Gtk::TextMark >& end,
                      const char type,
                      TextviewDiaryTheme* TvD )
:   Link( start, end, LT_THEME ), m_type( type ), m_TvD( TvD )
{
}

void
LinkTheme::go()
{
    Theme* theme = const_cast< Theme* >( m_TvD->get_theme() );

    switch( m_type )
    {
        case 'H':   // heading
            launch_color_dialog( theme->color_heading );
            theme->calculate_derived_colors();
            break;
        case 'S':   // subheading
            launch_color_dialog( theme->color_subheading );
            theme->calculate_derived_colors();
            break;
        case 'T':   // text
            launch_color_dialog( theme->color_text );
            theme->calculate_derived_colors();
            break;
        case 'L':   // highlight
            launch_color_dialog( theme->color_highlight );
            theme->calculate_derived_colors();
            break;
        case 'B':
            launch_color_dialog( theme->color_base );
            theme->calculate_derived_colors();
            break;
        case 'I':
            launch_file_dialog( theme->image_bg );
            break;
        case 'i':
            theme->image_bg.clear();
            break;
        case 'F':
            launch_font_dialog( theme->font );
            break;
    }
    m_TvD->set_theme( theme );
    m_TvD->handle_theme_edited();
}

void
LinkTheme::launch_color_dialog( Color& color )
{
    Gtk::ColorChooserDialog *dlg = new Gtk::ColorChooserDialog;
    dlg->set_transient_for( * AppWindow::p );
    dlg->set_use_alpha( false );
    dlg->set_rgba( color );

    if( dlg->run() == Gtk::RESPONSE_OK )
        color = dlg->get_rgba();

    delete dlg;
}

void
LinkTheme::launch_font_dialog( Pango::FontDescription& font )
{
    Gtk::FontChooserDialog *dlg = new Gtk::FontChooserDialog;
    dlg->set_transient_for( * AppWindow::p );
    dlg->set_font_desc( font );

    if( dlg->run() == Gtk::RESPONSE_OK )
        font = dlg->get_font_desc();

    delete dlg;
}

void
LinkTheme::launch_file_dialog( std::string& path )
{
    auto dlg = new Gtk::FileChooserDialog( _( "Select Image" ), Gtk::FILE_CHOOSER_ACTION_OPEN );
    dlg->set_transient_for( * AppWindow::p );
    dlg->add_button( Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL );
    dlg->add_button( Gtk::Stock::OPEN, Gtk::RESPONSE_OK );
    dlg->set_current_folder( Lifeograph::SHAREDIR + "/backgrounds" );

    if( dlg->run() == Gtk::RESPONSE_OK )
        path = dlg->get_filename();

    delete dlg;
}

// TEXTBUFFERDIARYSEARCH ===========================================================================
TextbufferDiaryTheme::TextbufferDiaryTheme()
{
    // TAGS
    // NOTE: order is significant. the later a tag is added the more dominant it is.
    Glib::RefPtr< TagTable > tag_table = get_tag_table();

    m_tag_link_theme = Tag::create( "theme_link" );
    m_tag_link_theme->property_weight() = Pango::WEIGHT_BOLD;
    m_tag_link_theme->property_underline() = Pango::UNDERLINE_SINGLE;
    m_tag_link_theme->property_scale() = 0.7;
    m_tag_link_theme->property_rise() = 3000;
    m_tag_link_theme->property_foreground_rgba() = Gdk::RGBA( "#333333" );
    m_tag_link_theme->property_background_rgba() = Gdk::RGBA( "#cccccc" );
    m_tag_link_theme->property_background_full_height() = false;
    tag_table->add( m_tag_link_theme );

    add_link_protocol( "theme", ParserText::RID_CUSTOM, &s_rc_link_theme );
}

// PARSING
void
TextbufferDiaryTheme::apply_link_hidden()
{
    auto&& it_cur      { get_iter_at_offset( m_pos_cur ) };
    auto&& it_end      { get_iter_at_offset( m_pos_cur + 1 ) };
    auto&& it_uri_bgn  { get_iter_at_offset( m_recipe_cur->m_pos_bgn + 1 ) };
    auto&& it_label    { get_iter_at_offset( m_recipe_cur->m_pos_mid + 1 ) };

    switch( m_recipe_cur->m_id )
    {
        case RID_CUSTOM:
        {
            if( get_char_at( m_recipe_cur->m_pos_bgn + 7 ) == 'x' ) // type of the link
                apply_hidden_link_tags( it_end, m_tag_link_broken );
            else
            {
                m_list_links.push_back( new LinkTheme(
                                            create_mark( it_uri_bgn ), create_mark( it_cur ),
                                            get_char_at( m_recipe_cur->m_pos_mid - 1 ),
                                            m_ptr2TvDT ) );
                // m_tag_link is added just to make mouse hover work:
                apply_hidden_link_tags( it_end, m_tag_link );
                apply_tag( m_tag_link_theme, it_label, it_cur );
            }
            break;
        }
    }
}

void
TextbufferDiaryTheme::apply_link()
{
    auto&& it_bgn   { get_iter_at_offset( m_recipe_cur->m_pos_bgn ) };
    auto&& it_cur   { get_iter_at_offset( m_pos_cur ) };

    switch( m_recipe_cur->m_id )
    {
        case RID_DATE:
            apply_date();
            break;
        case RID_LINK_AT:
        case RID_URI:
        case RID_ID:
            apply_tag( m_tag_link, it_bgn, it_cur );
            break;
    }
}

void
TextbufferDiaryTheme::apply_date()
{
    auto&& it_bgn    { get_iter_at_offset( m_recipe_cur->m_pos_bgn ) };
    auto&& it_end    { get_iter_at_offset( m_pos_cur + 1 ) };

    apply_tag( m_tag_link, it_bgn, it_end );
}

void
TextbufferDiaryTheme::apply_inline_tag()
{
    // m_pos_mid is used to determine if the name part or the value type is being applied
    if( m_recipe_cur->m_pos_mid == 0 )
    {
        auto&& it_bgn{ get_iter_at_offset( m_recipe_cur->m_pos_bgn ) };
        auto&& it_end{ get_iter_at_offset( m_pos_cur ) };
        auto   it_name_bgn = it_bgn;
        auto   it_name_end = it_end;

        apply_tag( m_tag_hidable, it_bgn, ++it_name_bgn );
        apply_tag( m_tag_inline_tag, it_bgn, it_end );
        apply_tag( m_tag_inline_tag_bg, it_name_bgn, --it_name_end );
        apply_tag( m_tag_hidable, it_name_end, it_end );
    }
    else
    {
        auto&& it_bgn{ get_iter_at_offset( m_recipe_cur->m_pos_mid + 1 ) };
        auto&& it_end{ get_iter_at_offset( m_pos_extra_2 + 1 ) };
        apply_tag( m_tag_inline_value, it_bgn, it_end );
    }
}

// TEXTVIEW ========================================================================================
TextviewDiaryTheme::TextviewDiaryTheme()
{
    init();
}

TextviewDiaryTheme::TextviewDiaryTheme( BaseObjectType* obj,
                                          const Glib::RefPtr< Gtk::Builder >& builder )
:   TextviewDiary( obj, builder )
{
    init();
}

inline void
TextviewDiaryTheme::init()
{
    m_buffer = m_buffer_theme = new TextbufferDiaryTheme;
    set_buffer( static_cast< Glib::RefPtr< TextbufferDiary > >( m_buffer ) );
    m_buffer_theme->m_ptr2TvD = m_buffer_theme->m_ptr2TvDT = this;

    set_editable( false );

    m_cursor_default = Gdk::ARROW;
}

void
TextviewDiaryTheme::set_text_preview()
{
    set_static_text( m_buffer_theme->m_p2theme->get_name() +
                     "\nSample text\n Sample Subheading\n#Highlighted#" );

    m_flag_in_edit_mode = false;
}

void
TextviewDiaryTheme::set_text_edit()
{
    const auto theme{ m_buffer_theme->m_p2theme };

    set_static_text(
            theme->get_name() + " <theme:H\tEDIT COLOR>\n" +
            " Subheading <theme:S\tEDIT COLOR>\n"
            "  Subsubheading\n"
            "Lorem ipsum dolor sit amet, consectetur adipiscing elit, "
                "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. "
            "<theme:T\tEDIT COLOR>\n\n"
            "#Highlighted Text# <theme:L\tEDIT COLOR>\n\n"
            ":Sample Tag:=10/10    Link: http://lifeograph.sf.net   |   <theme:x\tBroken Link>"
                "   [[Commented text]]\n\n"
            " Background Color\n" +
                convert_gdkcolor_to_html( theme->color_base ) + " <theme:B\tCHANGE>\n\n"
            " Background Image\n" +
                ( theme->image_bg.empty() ?
                    "none <theme:I\tSET IMAGE>\n\n" :
                    theme->image_bg + " <theme:I\tCHANGE> | <theme:i\tCLEAR>\n\n" ) +
            " Font\n" +
            theme->font.to_string() + " <theme:F\tEDIT>\n\n"
            " Todo List\n"
            "\t[ ] Open Item\n"
            "\t[~] In Progress Item\n"
            "\t[+] Done Item\n"
            "\t[X] Canceled Item\n" );

    m_flag_in_edit_mode = true;
}

bool
TextviewDiaryTheme::on_draw( const Cairo::RefPtr< Cairo::Context >& cr )
{
    TextviewDiary::on_draw( cr );

    if( m_buffer_theme->m_p2theme && m_buffer_theme->m_p2theme->is_default() )
    {
        using namespace Cairo;
        Icon buf{ Gtk::IconTheme::get_default()->load_icon( "default-32", 32 ) };
        const int w_buf      { buf->get_width() };
        const int h_buf      { buf->get_height() };
        auto&&    IS_img     { ImageSurface::create( FORMAT_ARGB32, w_buf, h_buf ) };
        auto&&    IC_img     { Context::create( IS_img ) };
        Gdk::Rectangle rect_Tv;

        get_visible_rect( rect_Tv );

        Gdk::Cairo::set_source_pixbuf( IC_img, buf, 0.0, 0.0 );
        IC_img->paint();

        cr->set_source( IS_img,rect_Tv.get_width() - w_buf, 0 );
        cr->rectangle( rect_Tv.get_width() - w_buf, 0, w_buf, h_buf );
        cr->clip();
        cr->paint();
        cr->reset_clip();
    }

    return true;

}

bool
TextviewDiaryTheme::handle_query_tooltip( int x, int y, bool keyboard_mode,
                                           const Glib::RefPtr< Gtk::Tooltip >& tooltip )
{
    if( m_link_hovered != nullptr )
        tooltip->set_text( _( "Click to edit the theme" ) );
    else
        return false;

    return true;
}
