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

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

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


#include "lifeograph.hpp"
#include "app_window.hpp"
#include "dialog_preferences.hpp"
#include "helpers.hpp"

#if LIFEOGRAPH_DEBUG_BUILD || defined( LIFEOGRAPH_BETA_RELEASE )
#include <build_time.h>
#endif


using namespace LIFEO;

// STATIC MEMBERS
const char                          Lifeograph::PROGRAM_VERSION_STRING[] = "1.5.1"
#if LIFEOGRAPH_DEBUG_BUILD || defined( LIFEOGRAPH_BETA_RELEASE )
        "\n" BUILD_TIMESTAMP
#endif
        "\n\"" LIFEOGRAPH_RELEASE_CODENAME "\"";


Lifeograph*                         Lifeograph::p;
Settings                            Lifeograph::settings;
Icons*                              Lifeograph::icons = nullptr;
Glib::RefPtr< Gtk::Builder >        Lifeograph::builder;
bool                                Lifeograph::m_flag_ui2_loaded = false;
Glib::RefPtr< Gtk::ActionGroup >    Lifeograph::m_actions_read;
Glib::RefPtr< Gtk::ActionGroup >    Lifeograph::m_actions_edit;
int                                 Lifeograph::s_internaloperation = 0;
SignalVoid                          Lifeograph::s_signal_logout;
Lifeograph::LoginStatus             Lifeograph::loginstatus = Lifeograph::LOGGED_OUT;
std::set< std::string >             Lifeograph::stock_diaries;
DiaryElement*                       Lifeograph::s_elem_dragged = nullptr;
bool                                Lifeograph::s_flag_dragging = false;
LanguageList                        Lifeograph::s_lang_list;
EnchantBroker*                      Lifeograph::s_enchant_broker = nullptr;
std::string                         Lifeograph::s_color_insensitive;

std::string                         Lifeograph::UIDIR;
std::string                         Lifeograph::ICONDIR;
std::string                         Lifeograph::DIARYDIR;
std::string                         Lifeograph::PIXMAPDIR;
std::string                         Lifeograph::LOCALEDIR;


// CONSTRUCTOR
Lifeograph::Lifeograph()
:   Gtk::Application( "lifeograph.app",
                      Gio::APPLICATION_NON_UNIQUE | Gio::APPLICATION_HANDLES_COMMAND_LINE )
{
    p = this;

    Glib::set_application_name( LIFEO::PROGRAM_NAME );
    if( Glib::file_test( PIXMAPDIR + "/lifeograph.png", Glib::FILE_TEST_EXISTS ) )
        Gtk::Window::set_default_icon_from_file( PIXMAPDIR + "/lifeograph.png" );

    m_actions_read = Gtk::ActionGroup::create( "read actions" );
    m_actions_edit = Gtk::ActionGroup::create( "edit actions" );

    m_actions_read->set_sensitive( false );
    m_actions_edit->set_sensitive( false );

    icons = new Icons;

    drag_targets_tag.push_back( Gtk::TargetEntry( TARGET_ENTRY_TAG,
                                                  Gtk::TARGET_SAME_APP,
                                                  DiaryElement::ET_TAG ) );
    drag_targets_entry.push_back( Gtk::TargetEntry( TARGET_ENTRY_ENTRY,
                                                    Gtk::TARGET_SAME_APP,
                                                    DiaryElement::ET_ENTRY ) );
}

Lifeograph::~Lifeograph()
{
    if( s_enchant_broker )
    {
        enchant_broker_free( s_enchant_broker );
        s_enchant_broker = nullptr;
    }
}

Glib::RefPtr< Lifeograph >
Lifeograph::create()
{
    return Glib::RefPtr< Lifeograph >( new Lifeograph );
}

bool
Lifeograph::quit()
{
    PRINT_DEBUG( "Lifeograph::quit()" );

    if( loginstatus == LOGGED_IN )
        if( ! AppWindow::p->finish_editing() )
            return false;

    // SAVE SETTINGS
    AppWindow::p->get_size( settings.width, settings.height );
    AppWindow::p->get_position( settings.position_x, settings.position_y );
    settings.state_maximized = (
            AppWindow::p->get_window()->get_state() & Gdk::WINDOW_STATE_MAXIMIZED );

    settings.write();

    return true;
}

// APP MENU COMMAND REDIRECTION FUNCTIONS
void appmenu_preferences()
{
    DialogPreferences::create();
}
void appmenu_about()
{
    AppWindow::p->show_about();
}
void appmenu_quit()
{
    if( Lifeograph::quit() )
        AppWindow::p->hide();
}

void
Lifeograph::init_paths()
{
#ifdef _WIN32
    std::string exe_path{ HELPERS::get_exec_path() + "/" };
    UIDIR = exe_path + "../share/lifeograph/ui";
    ICONDIR = exe_path + "../share/lifeograph/icons";
    DIARYDIR = exe_path + "../share/lifeograph/diaries";
    PIXMAPDIR = exe_path + "../share/pixmaps";
    LOCALEDIR = exe_path + "../share/locale";
PRINT_DEBUG( "UIDIR: ", UIDIR );
#else
    UIDIR = DEF_UIDIR;
    ICONDIR = DEF_ICONDIR;
    DIARYDIR = DEF_DIARYDIR;
    PIXMAPDIR = DEF_PIXMAPDIR;
    LOCALEDIR = DEF_LOCALEDIR;
#endif
}

void
Lifeograph::on_startup()
{
    Gtk::Application::on_startup();

    // SETTINGS
    Lifeograph::settings.read();

    // ENCHANT
    s_enchant_broker = enchant_broker_init();
    enchant_broker_list_dicts( s_enchant_broker, fill_lang_list_cb, nullptr );

    // APP MENU
    add_action( "preferences", sigc::ptr_fun( &appmenu_preferences ) );
    add_action( "about", sigc::ptr_fun( &appmenu_about ) );
    add_action( "quit", sigc::ptr_fun( &appmenu_quit ) );

    m_menu = Gio::Menu::create();
    m_menu->append( _( "Preferences" ), "app.preferences" );
    m_menu->append( _( "About Lifeograph" ), "app.about" );
    m_menu->append( _( "Quit" ), "app.quit" );

    set_app_menu( m_menu );

    builder = Gtk::Builder::create();
    load_gui( UIDIR + "/lifeograph.ui" );
}

void
Lifeograph::on_activate()
{
    builder->get_widget_derived( "W_main", AppWindow::p );
    add_window( * AppWindow::p );
    AppWindow::p->show();
}

int
Lifeograph::on_command_line( const Glib::RefPtr< Gio::ApplicationCommandLine >& cmd_line )
{
    int argc;
    char** argv( cmd_line->get_arguments( argc ) );

    for( int i = 1; i < argc; i++ )
    {
        if( ! strcmp( argv[ i ], "--open" ) || ! strcmp( argv[ i ], "-o" ) )
        {
            if( ( i + 1 ) < argc )
            {
                if( access( argv[ ++i ], F_OK ) == 0 ) // check existance
                {
                    if( ! is_dir( argv[ i ] ) )
                    {
                        ViewLogin::m_path_cur = argv[ i ];
                        m_flag_open_directly = true;
                    }
                }
                if( ! m_flag_open_directly )
                    Error( "File cannot be opened" );
            }
            else
                Error( "No path provided" );
        }
        else
        if( ! strcmp( argv[ i ], "--force-welcome" ) )
        {
            m_flag_force_welcome = true;
        }
        else
        if( ! strcmp( argv[ i ], "--ignore-locks" ) )
        {
            Diary::s_flag_ignore_locks = true;
        }
        else
        if( ! strcmp( argv[ i ], "--read-only" ) || ! strcmp( argv[ i ], "-r" ) )
        {
            m_flag_read_only = true;
        }
        else
        if( access( argv[ i ], F_OK ) == 0 && ! is_dir( argv[ i ] ) )
        {
            ViewLogin::m_path_cur = argv[ i ];
            m_flag_open_directly = true;
        }
        else
        {
            print_info( "Unrecognized argument: ", argv[ i ] );
        }
    }

    activate();

    return 0;
}

bool
Lifeograph::load_gui( const std::string& path )
{
    try
    {
        builder->add_from_file( path );
    }
    catch( const Glib::FileError& ex )
    {
        print_error( "FileError: ", ex.what() );
        return false;
    }
    catch( const Gtk::BuilderError& ex )
    {
        print_error( "BuilderError: ", ex.what() );
        return false;
    }

    return true;
}

Glib::RefPtr< Gtk::Builder >&
Lifeograph::get_builder2()
{
    if( !m_flag_ui2_loaded )
        if( load_gui( UIDIR + "/lifeograph-2.ui" ) )
            m_flag_ui2_loaded = true;

    return builder;
}

// ACTIONS
void
Lifeograph::create_action( bool                             flag_edit,
                           Glib::RefPtr< Gtk::Action >&     action,
                           const Glib::ustring&             name,
                           const Glib::ustring&             label,
                           const Glib::ustring&             tooltip,
                           const Gtk::AccelKey&             accelkey,
                           const Glib::SignalProxy0< void >::SlotType&
                                                            slot,
                           Gtk::Activatable*                widget_proxy )
{
    action = Gtk::Action::create( name, label, tooltip );

    if( flag_edit )
        m_actions_edit->add( action, accelkey );
    else
        m_actions_read->add( action, accelkey );
    action->set_accel_group( AppWindow::p->get_accel_group() );
    action->connect_accelerator();
    if( widget_proxy != nullptr )
        widget_proxy->set_related_action( action );
    action->signal_activate().connect( slot );
}

void
Lifeograph::create_toggle_action( bool                                  flag_edit,
                                  Glib::RefPtr< Gtk::ToggleAction >&    action,
                                  const Glib::ustring&                  name,
                                  const Glib::ustring&                  label,
                                  const Glib::ustring&                  tooltip,
                                  const Gtk::AccelKey&                  accelkey,
                                  const Glib::SignalProxy0< void >::SlotType&
                                                                        slot,
                                 Gtk::Activatable*                      widget_proxy )
{
    action = Gtk::ToggleAction::create( name, label, tooltip );

    if( flag_edit )
        m_actions_edit->add( action, accelkey );
    else
        m_actions_read->add( action, accelkey );
    action->set_accel_group( AppWindow::p->get_accel_group() );
    action->connect_accelerator();
    if( widget_proxy != nullptr )
        widget_proxy->set_related_action( action );
    action->signal_activate().connect( slot );
}

Glib::RefPtr< Gdk::Pixbuf >
Lifeograph::get_thumbnail( const std::string& path, int width )
{
    Glib::RefPtr< Gdk::Pixbuf > buf;
    MapPixbufs::iterator iter( p->map_thumbnails.find( path ) );

    if( iter == p->map_thumbnails.end() )
    {
        buf = Gdk::Pixbuf::create_from_file( path );
        p->map_thumbnails[ path ] = buf;
    }
    else
        buf = iter->second;

    if( buf->get_width() > width )
        return buf->scale_simple( width,
                                  ( buf->get_height() * width ) / buf->get_width(),
                                  Gdk::INTERP_BILINEAR );
    else
        return buf;
}

// DIALOGEVENT =====================================================================================
HELPERS::DialogEvent::DialogEvent( const Glib::ustring& title )
:   Gtk::Dialog( title )
{
    set_transient_for( *AppWindow::p );
    Lifeograph::signal_logout().connect( sigc::mem_fun( this, &DialogEvent::handle_logout ) );
}

HELPERS::DialogEvent::DialogEvent( BaseObjectType* cobject,
                                   const Glib::RefPtr< Gtk::Builder >& )
:   Gtk::Dialog( cobject )
{
    set_transient_for( * AppWindow::p );
    //Lifeograph::signal_logout().connect( sigc::mem_fun( this, &DialogEvent::handle_logout ) );
}

bool
HELPERS::DialogEvent::on_event( GdkEvent* e )
{
    AppWindow::p->on_event( e );
    return false;
}
