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

    Copyright (C) 2007-2026 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 "../diaryelements/entry.hpp"
#include "../app_window.hpp"
#include "po_entry.hpp"


using namespace LoG;


PoEntry::PoEntry( Mode mode )
: m_mode( mode )
{
    Gtk::Box*      Bx_comment_style;

    try
    {
        m_builder           = Lifeograph::create_gui( Lifeograph::SHAREDIR + "/ui/po_entry.ui" );

        m_Po_entry          = m_builder->get_widget< Gtk::Popover >( "Po_entry" );

        m_Bx_group_editing  = m_builder->get_widget< Gtk::Box >( "Bx_properties" );
        Bx_comment_style    = m_builder->get_widget< Gtk::Box >( "Bx_comment_style" );
        m_Bx_locked         = m_builder->get_widget< Gtk::Box >( "Bx_entry_locked" );

        m_RB_not_todo       = m_builder->get_widget< Gtk::ToggleButton >( "RB_entry_not_todo" );
        m_RB_todo           = m_builder->get_widget< Gtk::ToggleButton >( "RB_entry_todo" );
        m_RB_progressed     = m_builder->get_widget< Gtk::ToggleButton >( "RB_entry_progressed" );
        m_RB_done           = m_builder->get_widget< Gtk::ToggleButton >( "RB_entry_done" );
        m_RB_canceled       = m_builder->get_widget< Gtk::ToggleButton >( "RB_entry_canceled" );
        m_TB_favorite       = m_builder->get_widget< Gtk::ToggleButton >( "TB_entry_favorite" );
        m_ClB_color         = m_builder->get_widget< Gtk::ColorButton >( "ClB_entry_color" );
        m_DD_title_style    = m_builder->get_widget< Gtk::DropDown >( "DD_title_style" );

        m_DD_spellchk       = Gtk::Builder::get_widget_derived< DropDownAdvanced< String > >(
                m_builder, "DD_entry_spellchk" );

        m_Bx_unit           = m_builder->get_widget< Gtk::Box >( "Bx_entry_unit" );
        m_CB_unit           = m_builder->get_widget< Gtk::ComboBoxText >( "CB_tag_unit" );
        m_E_unit            = dynamic_cast< Gtk::Entry* >( m_CB_unit->get_child() );

        m_Sw_locked         = m_builder->get_widget< Gtk::Switch >( "Sw_entry_locked" );

        m_Sw_reg_scripts    = m_builder->get_widget< Gtk::Switch >( "Sw_entry_register_scripts" );

        m_CB_comment_style  = m_builder->get_widget< Gtk::ComboBoxText >( "CB_comment_style" );
    }
    catch( ... )
    {
        throw LoG::Error( "Failed to create the entry popover" );
    }

    m_CB_unit->set_name( "noncustom" );
    m_E_unit->set_placeholder_text( _( "Unitless" ) );

    if( m_mode == Mode::LIST )
        Bx_comment_style->hide();

    m_DD_title_style->set_model( Gtk::StringList::create( Lifeograph::s_entry_name_styles ) );

    // HELP KEYWORDS
    AppWindow::set_help_topic( m_CB_unit, "Unit" );
    AppWindow::set_help_topic( m_ClB_color, "Color" );

    // SIGNALS
    m_RB_not_todo->signal_toggled().connect(
            [ this ]()
            { if( m_RB_not_todo->get_active() )
                do_for_sel_entries( sigc::bind( sigc::mem_fun( *this, &PoEntry::handle_todo ),
                                                ES::NOT_TODO ) ); } );
    m_RB_todo->signal_toggled().connect(
            [ this ]()
            { if( m_RB_todo->get_active() )
                do_for_sel_entries( sigc::bind( sigc::mem_fun( *this, &PoEntry::handle_todo ),
                                                ES::TODO ) ); } );
    m_RB_progressed->signal_toggled().connect(
            [ this ]()
            { if( m_RB_progressed->get_active() )
                do_for_sel_entries( sigc::bind( sigc::mem_fun( *this, &PoEntry::handle_todo ),
                                                ES::PROGRESSED ) ); } );
    m_RB_done->signal_toggled().connect(
            [ this ]()
            { if( m_RB_done->get_active() )
                do_for_sel_entries( sigc::bind( sigc::mem_fun( *this, &PoEntry::handle_todo ),
                                                ES::DONE ) ); } );
    m_RB_canceled->signal_toggled().connect(
            [ this ]()
            { if( m_RB_canceled->get_active() )
                do_for_sel_entries( sigc::bind( sigc::mem_fun( *this, &PoEntry::handle_todo ),
                                                ES::CANCELED ) ); } );

    m_DD_spellchk->Sg_selection_changed.connect(
            [ this ]( String id )
            { do_for_sel_entries( sigc::bind( sigc::mem_fun( *this, &PoEntry::handle_spellchk ),
                                              id ) ); } );

    m_TB_favorite->signal_toggled().connect(
            [ this ]()
            { do_for_sel_entries( sigc::mem_fun( *this, &PoEntry::handle_favorite ) ); } );

    m_DD_title_style->property_selected().signal_changed().connect(
            [ this ]()
            { do_for_sel_entries( sigc::mem_fun( *this, &PoEntry::handle_title_style ) ); } );

    m_CB_unit->signal_changed().connect(
            [ this ]()
            { do_for_sel_entries( sigc::mem_fun( *this, &PoEntry::handle_unit ) ); } );

    m_Sw_locked->property_active().signal_changed().connect(
            [ this ]()
            { do_for_sel_entries( sigc::mem_fun( *this, &PoEntry::handle_lock ) ); } );

    m_Sw_reg_scripts->property_active().signal_changed().connect(
            [ this ]()
            { do_for_sel_entries( sigc::mem_fun( *this, &PoEntry::handle_scripting ) ); } );

    m_CB_comment_style->signal_changed().connect(
            [ this ]()
            { do_for_sel_entries( sigc::mem_fun( *this, &PoEntry::handle_comment_style ) ); } );

    m_ClB_color->signal_color_set().connect(
            [ this ]()
            { do_for_sel_entries( sigc::mem_fun( *this, &PoEntry::handle_color ) ); } );

    AppWindow::p->setup_contextual_help_for_Po( m_Po_entry );
}

inline R2Action
PoEntry::get_action( const Ustring& name )
{
    return std::dynamic_pointer_cast< Gio::SimpleAction >(
            AppWindow::p->UI_diary->m_AG_entry->lookup_action( name ) );
}

void
PoEntry::set_editing_enabled( bool F_enabled )
{
    m_F_update_size = 1;

    m_Bx_group_editing->set_visible( F_enabled );
    if( F_enabled )
        m_Bx_group_editing->set_sensitive( !m_p2entry->is_locked() );
    m_Bx_locked->set_visible( F_enabled );
    get_action( "script_actions" )->set_enabled( F_enabled &&
                                                 !m_p2entry->is_locked() &&
                                                 m_mode == Mode::ENTRY );
    get_action( "script_actions_list" )->set_enabled( F_enabled && m_mode == Mode::LIST );
}

void
PoEntry::handle_logout()
{
    hide(); // hide if is visible

    get_action( "add" )->set_enabled( false );
    get_action( "merge" )->set_enabled( false );
    get_action( "sort_siblings" )->set_enabled( false );
    get_action( "trash" )->set_enabled( false );
    get_action( "restore" )->set_enabled( false );
    get_action( "dismiss" )->set_enabled( false );

    set_editing_enabled( false );

    m_F_update_size = 0;

    m_Po_entry->unparent();

    m_p2entry = nullptr;
}

void
PoEntry::set( Entry* entry, Gtk::Widget& W_parent, Gdk::Rectangle* rect )
{
    m_p2entry = entry;

    m_Po_entry->unparent();
    m_Po_entry->set_parent( W_parent );
    if( rect )
    {
        m_Po_entry->set_pointing_to( *rect );
        delete rect;
    }
}

void
PoEntry::show()
{
    switch( m_mode )
    {
        case Mode::LIST:
            AppWindow::set_entries_getter( sigc::mem_fun( *AppWindow::p->UI_diary,
                                           &UIDiary::get_selected_entries ) );
            break;
        case Mode::ENTRY:
        case Mode::POPUP:
            AppWindow::set_entries_getter( [ this ](){ return EntrySelection( { m_p2entry } ); } );
            break;
    }

    if( m_mode != Mode::LIST || m_p2entry->get_diary()->is_in_edit_mode() )
    {
        refresh();
        Glib::signal_idle().connect_once( [ this ]() { m_Po_entry->popup(); } );
    }
}

void
PoEntry::refresh()
{
    if( m_p2entry->get_diary()->is_in_edit_mode() )
    {
        set_editing_enabled( true );
        update_editing_controls();
    }
    get_action( "unpopup" )->set_enabled( m_mode == Mode::POPUP );
    get_action( "print" )->set_enabled( m_mode == Mode::ENTRY );

    Lifeograph::START_INTERNAL_OPERATIONS();
    m_CB_comment_style->set_active( VT::get_v< VT::CS,
                                               unsigned,
                                               int >( m_p2entry->get_comment_style() ) );
    Lifeograph::FINISH_INTERNAL_OPERATIONS();
}

void
PoEntry::update_editing_controls()
{
    Lifeograph::START_INTERNAL_OPERATIONS();

    get_action( "add" )->set_enabled( m_mode == Mode::LIST );
    get_action( "sort_siblings" )->set_enabled( m_mode == Mode::LIST );

    get_action( "trash" )->set_enabled( !m_p2entry->is_trashed() );
    get_action( "restore" )->set_enabled( m_p2entry->is_trashed() );
    get_action( "dismiss" )->set_enabled( m_p2entry->is_trashed() );

    m_Sw_locked->set_active( m_p2entry->is_locked_raw() );
    m_Sw_locked->set_sensitive( !m_p2entry->is_trashed() );

    get_action( "merge" )->set_enabled( m_mode == Mode::LIST && !has_locked_entry_selected() &&
                                        AppWindow::p->UI_diary->get_selected_count() > 1 );

    switch( m_p2entry->get_todo_status() )
    {
        case ES::TODO:
            m_RB_todo->set_active();
            break;
        case ES::PROGRESSED:
            m_RB_progressed->set_active();
            break;
        case ES::DONE:
            m_RB_done->set_active();
            break;
        case ES::CANCELED:
            m_RB_canceled->set_active();
            break;
        case ES::NOT_TODO:
        default:
            m_RB_not_todo->set_active();
            break;
    }

    m_TB_favorite->set_active( m_p2entry->is_favorite() );
    m_DD_title_style->set_selected( m_p2entry->get_title_style() );
    m_E_unit->set_text( m_p2entry->get_unit() );
    m_ClB_color->set_rgba( Color( m_p2entry->get_color() ) );

    m_Sw_reg_scripts->set_active( m_p2entry->registers_scripts_raw() );

    refresh_DD_spellchecking( m_p2entry, m_DD_spellchk );

    Lifeograph::FINISH_INTERNAL_OPERATIONS();
}

void
PoEntry::refresh_DD_spellchecking( const DiaryElemTag* p2tag, DropDownAdvanced< String >* DD )
{
    Lifeograph::START_INTERNAL_OPERATIONS();

    const auto lang_inh { p2tag->is_entry()
                                ? p2tag->get_diary()->get_lang()
                                : dynamic_cast< const Paragraph* >( p2tag )->m_host->get_lang() };
    DD->clear();
    DD->add( STR::compose( p2tag->is_entry() ? _( "Diary Default" ) : _( "Entry Default" ),
                           "  (", lang_inh.empty() ? get_sstr_i( CSTR::OFF ) : lang_inh, ")" ),

             "" ); // inherit
    DD->add( get_sstr_i( CSTR::OFF ), get_sstr( CSTR::OFF ) );

    for( auto lang : Lifeograph::s_lang_list )
        DD->add( lang, lang );

    // SOME OTHER LANGUAGE THAT IS NOT SUPPORTED BY THE CURRENT SYSTEM
    if( p2tag->has_property( PROP::LANGUAGE ) &&
        Lifeograph::s_lang_list.find( p2tag->get_lang() ) == Lifeograph::s_lang_list.end() )
    {
        DD->add( "⚠ " + p2tag->get_lang(), p2tag->get_lang() );
    }

    DD->select_by_id( p2tag->get_lang() );

    Lifeograph::FINISH_INTERNAL_OPERATIONS();
}

bool
PoEntry::has_locked_entry_selected() const
{
    const auto&&  entries         { AppWindow::get_entries_per_context() };

    for( Entry* e : entries )
        if( e->is_locked() ) return true;

    return false;
}

void
PoEntry::do_for_sel_entries( const FuncBoolEntry& process_entry )
{
    if( Lifeograph::is_internal_operations_ongoing() ) return;

    const auto&&  entries         { AppWindow::get_entries_per_context() };
    bool          F_refresh_extra { false };

    for( Entry* e : entries )
        F_refresh_extra = ( F_refresh_extra || process_entry( e ) );

    if( F_refresh_extra )
        AppWindow::p->UI_extra->refresh_active_panel();
}

bool
PoEntry::handle_favorite( Entry* e )
{
    if( e->is_locked() ) return false;

    const bool F_fav { m_TB_favorite->get_active() };

    if( F_fav != e->is_favorite() )
        e->toggle_favored();

    if( AppWindow::p->UI_entry->is_cur_entry( e ) )
        AppWindow::p->UI_entry->get_textview()->refresh_entry_icons();

    AppWindow::p->UI_diary->refresh_row( e );

    return false; // don't refresh extra panel
}

bool
PoEntry::handle_todo( Entry* e, ElemStatus status )
{
    if( e->is_locked() ) return false;

    if( status == ES::NOT_TODO )
        e->update_todo_status();
    else
        e->set_todo_status( status );

    if( AppWindow::p->UI_entry->is_cur_entry( e ) )
        AppWindow::p->UI_entry->get_textview()->refresh_entry_icons();

    AppWindow::p->UI_diary->refresh_row( e );

    return false; // don't refresh extra panel
}

bool
PoEntry::handle_color( Entry* e )
{
    if( e->is_locked() ) return false;

    e->set_color( convert_gdkcolor_to_html( m_ClB_color->get_rgba() ) );

    if( e->get_title_style() == VT::ETS::MILESTONE::I )
        AppWindow::p->UI_diary->refresh_row( e );

    return true; // update the extra panel
}

bool
PoEntry::handle_spellchk( Entry* e, const String& lang )
{
    if( e->is_locked() ) return false;

    e->set_lang( lang );

    if( AppWindow::p->UI_entry->is_cur_entry( e ) )
        AppWindow::p->UI_entry->get_textview()->update_for_spell_check_change();

    return false; // don't refresh extra panel
}

bool
PoEntry::handle_unit( Entry* e )
{
    if( e->is_locked() ) return false;

    e->set_unit( m_E_unit->get_text() );

    AppWindow::p->UI_diary->refresh_row( e );

    return true; // update the extra panel
}

bool
PoEntry::handle_title_style( Entry* e )
{
    if( e->is_locked() ) return false;

    int type { int( m_DD_title_style->get_selected() ) };
    e->set_title_style( type );

    AppWindow::p->UI_diary->refresh_row( e );
    if( AppWindow::p->UI_entry->is_cur_entry( e ) )
        AppWindow::p->UI_entry->get_textview()->refresh_entry_icons();

    return false; // don't refresh extra panel
}

bool
PoEntry::handle_lock( Entry* e )
{
    bool F_locked { m_Sw_locked->get_state() };

    e->set_locked( F_locked );

    if( e->is_popup_note() )
        AppWindow::p->update_popup_note_editability( e->get_id_full() );

    AppWindow::p->UI_diary->refresh_row( e );

    if( AppWindow::p->UI_entry->is_cur_entry( e ) )
        AppWindow::p->UI_entry->get_textview()->set_editable( !F_locked );

    hide();

    return false; // don't refresh extra panel
}

bool
PoEntry::handle_scripting( Entry* e )
{
    if( e->is_locked() ) return false;

    e->set_property( PROP::REGISTER_SCRIPTS, m_Sw_reg_scripts->get_state() );
    PyBindings::update_for_entry( e, e->registers_scripts() );

    AppWindow::p->UI_diary->refresh_row( e );
    if( AppWindow::p->UI_entry->is_cur_entry( e ) )
        AppWindow::p->UI_entry->get_textview()->refresh_entry_icons();

    return false; // don't refresh extra panel
}

bool
PoEntry::handle_comment_style( Entry* e )
{
    if( e->is_locked() ) return false;

    int i_style { m_CB_comment_style->get_active_row_number() };

    e->set_comment_style( VT::get_v< VT::CS, int, unsigned >( i_style ) );

    if( AppWindow::p->UI_entry->is_cur_entry( e ) )
        AppWindow::p->UI_entry->get_textview()->set_theme( nullptr ); // nullptr means refresh

    return false; // don't refresh extra panel
}

