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

    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/>.

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


#include "dialog_scriptprompt.hpp"
#include "../app_window.hpp"
#include "../helpers.hpp"
#include "../python/py_bindings.hpp"
#include "../widgets/widget_datepicker.hpp"
#include "../widgets/widget_entrypicker.hpp"
#include "../widgets/widget_filterpicker.hpp"

namespace LoG
{

// STATIC MEMBERS
DialogScriptPrompt *DialogScriptPrompt::ptr;

void
DialogScriptPrompt::show( const String& info,
                          const String& script_name,
                          const String& callback_name,
                          const py::list& fields_list )
{
    if( !Diary::d->is_in_edit_mode() ) return;

    if( ptr )
    {
        ptr->hide();
        delete ptr;
        ptr = nullptr;
    }

    auto&& builder { Lifeograph::create_gui(
            Lifeograph::SHAREDIR + "/ui/dlg_scriptprompt.ui" ) };
    ptr = Gtk::Builder::get_widget_derived< DialogScriptPrompt >( builder, "D_scriptprompt" );
    ptr->m_callback_name = callback_name;
    ptr->m_L_script_name->set_text( STR::compose( _( "Script" ), ": ", script_name ) );
    ptr->m_script_name = script_name;
    ptr->m_L_info->set_text( info );

    ptr->populate_controls( fields_list );

    ptr->set_transient_for( *AppWindow::p );
    ptr->Gtk::Window::show();
}

DialogScriptPrompt::DialogScriptPrompt( BaseObjectType* cobject,
                                        const Glib::RefPtr< Gtk::Builder >& builder )
:   Gtk::Window( cobject )
{
    m_L_script_name = builder->get_widget< Gtk::Label >( "L_script_name" );
    m_L_info        = builder->get_widget< Gtk::Label >( "L_info" );
    m_G_controls    = builder->get_widget< Gtk::Grid >( "G_fields" );
    auto B_cancel   = builder->get_widget< Gtk::Button >( "B_cancel" );
    auto B_go       = builder->get_widget< Gtk::Button >( "B_go" );

    B_cancel->signal_clicked().connect( [ this ]() { hide(); } );
    B_go->signal_clicked().connect( [ this ]() { handle_apply(); } );
}

bool
DialogScriptPrompt::on_key_press_event( guint keyval, guint, Gdk::ModifierType state )
{
    switch( int( state ) & CTRL_ALT_SHIFT_MASK )
    {
        case 0:
            switch( keyval )
            {
                case GDK_KEY_Escape:
                    hide();
                    return true;
            }
            break;
    }
    return false;
}

void
DialogScriptPrompt::populate_controls( const py::list& fields_list )
{
    for( auto item : fields_list )
    {
        py::dict    field     { item.cast< py::dict >() };
        FieldType   type      { field[ "type" ].cast< FieldType >() };
        String      name      { field[ "name" ].cast< String >() };
        VecOptions  options;

        // if( !field["default"].is_none())
        // {
        //     def.default_value = field["default"];
        // }

        if( field.contains( "opts" ) )
        {
            py::list options_list = field[ "opts" ].cast< py::list >();
            for( auto opt : options_list )
            {
                if( py::isinstance< py::str >( opt ) )
                {
                    String      label { opt.cast< String >() };
                    py::object  value { py::reinterpret_borrow< py::object >( opt ) };
                    options.push_back( { label, value } );
                }
                else if( py::isinstance< py::tuple >( opt ) )
                {
                    py::tuple t { opt.cast< py::tuple >() };

                    if( t.size() != 2 )
                        throw std::runtime_error( "Option tuple must have 2 elements" );

                    String      label { t[ 0 ].cast< String >() };
                    options.push_back( { label, t[ 1 ] } );
                }
                else
                {
                    throw std::runtime_error( "Options must be strings or (string, value) tuples" );
                }
            }
        }

        if( type == FieldType::HIDDEN )
            m_hidden_options[ name ] = field[ "value" ];
        else
            add_control( type, name, std::move( options ) );
    }
}

void
DialogScriptPrompt::add_control( FieldType type, const String& name, VecOptions&& options )
{
    auto            L_label { Gtk::make_managed< Gtk::Label >( name, Gtk::Align::START ) };
    Gtk::Widget*    W_ctrl  { nullptr };

    switch( type )
    {
        case FieldType::CHECK:
            W_ctrl = Gtk::make_managed< Gtk::Switch >();
            break;
        case FieldType::OPTION:
        {
            auto DD_option = Gtk::make_managed< DropDownPyObj >();
            for( auto opt : options ) DD_option->add( opt.label, opt.id );
            W_ctrl = DD_option;
            break;
        }
        case FieldType::TEXT:
        {
            auto E_text = Gtk::make_managed< EntryClear >();
            E_text->set_width_chars( 30 );
            W_ctrl = E_text;
            break;
        }
        case FieldType::DATE:
            W_ctrl = Gtk::make_managed< WidgetDatePicker >();
            break;
        case FieldType::FILE:
        {
            auto FCB = Gtk::make_managed< FileChooserButton >( _( "Pick File" ) );
            W_ctrl = FCB;
            break;
        }
        case FieldType::FOLDER:
        {
            auto FCB = Gtk::make_managed< FileChooserButton >( _( "Pick File" ) );
            FCB->set_pick_folder_mode();
            W_ctrl = FCB;
            break;
        }
        case FieldType::TAG:
        {
            auto WTP = Gtk::make_managed< WidgetTagPicker >( false );
            WTP->set_diary( Diary::d );
            W_ctrl = WTP;
            break;
        }
        case FieldType::FILTER:
        {
            auto WFP = Gtk::make_managed< WidgetFilterPicker >();
            WFP->set_show_name( true );
            WFP->set_show_edit_button( true );
            W_ctrl = WFP;
            break;
        }
        case FieldType::HIDDEN: // never occurs
            break;
    }

    if( W_ctrl )
    {
        L_label->set_ellipsize( Pango::EllipsizeMode::END );

        W_ctrl->set_hexpand( true );
        W_ctrl->set_halign( Gtk::Align::END );

        m_G_controls->attach( *L_label, 0, m_row_last );
        m_G_controls->attach( *W_ctrl, 1, m_row_last );

        m_fields.push_back( { type, name, W_ctrl } );

        ++m_row_last;
    }
}

void
DialogScriptPrompt::handle_apply()
{
    if( !m_fields.empty() )
    {
        py::dict result; // extract values from widgets into dict

        for( const auto& field : m_fields )
        {
            switch( field.type )
            {
                case FieldType::TEXT:
                {
                    auto entry = dynamic_cast< EntryClear* >( field.W_ctrl );
                    result[ py::str( field.name ) ] = entry->get_text().raw();
                    break;
                }
                case FieldType::OPTION:
                {
                    auto DD = dynamic_cast< DropDownPyObj* >( field.W_ctrl );
                    result[ py::str( field.name ) ] = DD->get_selected_id();
                    break;
                }
                case FieldType::DATE:
                {
                    auto WDP = dynamic_cast< WidgetDatePicker* >( field.W_ctrl );
                    result[ py::str( field.name ) ] = WDP->get_date();
                    break;
                }
                case FieldType::CHECK:
                {
                    auto Sw = dynamic_cast< Gtk::Switch* >( field.W_ctrl );
                    result[ py::str( field.name ) ] = Sw->get_active();
                    break;
                }
                case FieldType::FILE:
                case FieldType::FOLDER:
                {
                    auto FCB = dynamic_cast< FileChooserButton* >( field.W_ctrl );
                    result[ py::str( field.name ) ] = FCB->get_uri();
                    break;
                }
                case FieldType::TAG:
                {
                    auto WTP = dynamic_cast< WidgetTagPicker* >( field.W_ctrl );
                    result[ py::str( field.name ) ] = WTP->get_tag_id().get_raw();
                    break;
                }
                case FieldType::FILTER:
                {
                    auto WFP    { dynamic_cast< WidgetFilterPicker* >( field.W_ctrl ) };
                    auto filter { WFP->get_active() };
                    if( filter )
                        result[ py::str( field.name ) ] = filter->get_id();
                    break;
                }
                case FieldType::HIDDEN: // never occurs
                    break;
            }
        }

        for( auto kv_opt : m_hidden_options )
            result[ py::str( kv_opt.first ) ] = kv_opt.second;

        PyBindings::run_script_name_func( m_script_name, m_callback_name,
                                          const_cast< const py::dict& >( result ) );
    }

    hide();
}

} // end of namespace LoG
