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

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

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

#include "lifeograph.hpp"
#include "dialog_export.hpp"
#include "app_window.hpp"

using namespace LIFEO;

// DIALOG SAVE GENERIC =============================================================================
DialogSaveGeneric::DialogSaveGeneric( const Glib::ustring& title )
:   Gtk::FileChooserDialog( title, Gtk::FILE_CHOOSER_ACTION_SAVE )
{
    add_button( _( "_Cancel" ), Gtk::RESPONSE_CANCEL );
    add_button( _( "_Save" ), Gtk::RESPONSE_ACCEPT );
    set_do_overwrite_confirmation( true );
    set_transient_for( * AppWindow::p );
}

DialogSaveGeneric::DialogSaveGeneric( BaseObjectType* cobject,
                                      const Glib::RefPtr< Gtk::Builder > &refbuilder )
:   Gtk::FileChooserDialog( cobject )
{
    add_button( _( "_Cancel" ), Gtk::RESPONSE_CANCEL );
    add_button( _( "_Save" ), Gtk::RESPONSE_ACCEPT );
    set_do_overwrite_confirmation( true );
}

std::string
DialogSaveGeneric::get_filename( const std::string& extension )
{
    std::string fname( FileChooserDialog::get_filename() );

    if( ! extension.empty() && extension != "." )
        if( ! STR::ends_with( fname, extension ) )
            fname.append( extension );

    return fname;
}

// DIALOG SAVE DIARY ===============================================================================
DialogSaveDiary *DialogSaveDiary::ptr = nullptr;

DialogSaveDiary::DialogSaveDiary( BaseObjectType* cobject,
                                  const Glib::RefPtr< Gtk::Builder > &refbuilder )
:   DialogSaveGeneric( cobject, refbuilder )
{
    try
    {
        auto builder{ Lifeograph::get_builder2() };
        builder->get_widget( "check_save_encrypt", m_check_encrypt );
        builder->get_widget( "grid_save_password", m_grid_password );
        builder->get_widget( "entry_save_password", m_entry_password );
        builder->get_widget( "entry_save_confirm", m_entry_confirm );
    }
    catch( ... )
    {
        throw LIFEO::Error( "creation of save dialog failed" );
    }

    m_check_encrypt->signal_toggled().connect(
            sigc::mem_fun( this, &DialogSaveDiary::handle_encryption_toggled ) );
    m_entry_password->signal_changed().connect(
            sigc::mem_fun( this, &DialogSaveDiary::handle_password_changed ) );
    m_entry_confirm->signal_changed().connect(
            sigc::mem_fun( this, &DialogSaveDiary::handle_password_changed ) );
}

int
DialogSaveDiary::launch( std::string& path, std::string& passphrase )
{
    if( ptr  == nullptr )
        Lifeograph::get_builder2()->get_widget_derived( "FD_save", ptr );

    ptr->set_transient_for( * AppWindow::p );
    ptr->set_current_folder( Glib::path_get_dirname( path ) );
    ptr->set_current_name( Glib::path_get_basename( path ) );
    ptr->m_check_encrypt->set_active( false );

    int ret_value( ptr->run() );
    path = ptr->get_filename( Lifeograph::settings.diary_extension );
    passphrase = ptr->m_entry_password->get_text();
    ptr->hide();
    ptr->set_current_folder( "/" ); // this prevents gtkrbtree core dumps, details not known

    return ret_value;
}

void
DialogSaveDiary::handle_encryption_toggled()
{
    bool active( m_check_encrypt->get_active() );
    m_grid_password->set_visible( active );
    if( !active )
    {
        m_entry_password->set_text( "" );
        m_entry_confirm->set_text( "" );
    }
    else
    {
        m_entry_password->grab_focus();
    }
    set_response_sensitive( Gtk::RESPONSE_ACCEPT, !active );
}

void
DialogSaveDiary::handle_password_changed()
{
    Glib::ustring passphrase( m_entry_password->get_text() );
    bool pass_ok( passphrase.size() >= LIFEO::PASSPHRASE_MIN_SIZE &&
                  passphrase == m_entry_confirm->get_text() );

    set_response_sensitive( Gtk::RESPONSE_ACCEPT, pass_ok );
}

// DIALOG OPEN DIARY ===============================================================================
bool DialogOpenDiary::flag_filters_created = false;
Glib::RefPtr< Gtk::FileFilter > DialogOpenDiary::filter_any;
Glib::RefPtr< Gtk::FileFilter > DialogOpenDiary::filter_diary;

DialogOpenDiary::DialogOpenDiary()
    // TRANSLATORS: this is the title of file chooser dialog for selecting a new diary
:   Gtk::FileChooserDialog( _( "Select a Diary" ), Gtk::FILE_CHOOSER_ACTION_OPEN )
{
    // Gtk gets confused if cancel is not stock:
    add_button( Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL );
    add_button( Gtk::Stock::OPEN, Gtk::RESPONSE_OK );

    init_filters();
    add_filter( filter_any );
    add_filter( filter_diary );
    set_filter( filter_diary );

    signal_selection_changed().connect(
            sigc::mem_fun( this, &DialogOpenDiary::handle_selection_changed ) );

    set_transient_for( * AppWindow::p );

    set_default_response( Gtk::RESPONSE_OK );
}

void
DialogOpenDiary::init_filters()
{
    if( flag_filters_created )
        return;

    filter_any = Gtk::FileFilter::create();
    filter_diary = Gtk::FileFilter::create();

    filter_any->set_name( _( "All Files" ) );
    filter_any->add_pattern( "*" );
    filter_diary->set_name( _( "Diary Files" ) );
#ifdef _WIN32
    filter_diary->add_pattern( "*.diary" );
#else
    filter_diary->add_mime_type( "application/x-lifeographdiary" );
#endif

    flag_filters_created = true;
}

void
DialogOpenDiary::handle_selection_changed()
{
    bool flag_enable( get_filename().size() > 0 );  // check if anything is selected
    if( flag_enable )
        flag_enable = ( Glib::file_test( get_filename(), Glib::FILE_TEST_IS_DIR ) == false );

    set_response_sensitive( Gtk::RESPONSE_OK, flag_enable );
}

// DIALOG EXPORT ===================================================================================
DialogExport::DialogExport( BaseObjectType* cobject,
        const Glib::RefPtr< Gtk::Builder >& refbuilder )
:   DialogEvent( cobject, refbuilder ), m_ptr2widget_options( nullptr ), m_ptr2diary( Diary::d )
{
    Gtk::Button* B_cancel;

    auto builder{ Lifeograph::get_builder2() };
    builder->get_widget( "CB_export_type", m_combo_type );
    builder->get_widget( "CB_export_extent", m_CB_extent );
    builder->get_widget( "F_export_options", m_F_options );
    builder->get_widget( "A_export_options", m_align_options );
    builder->get_widget( "B_export_cancel", B_cancel );
    builder->get_widget( "B_export_go", m_B_export );

    add_saver( new ExporterLifeograph( m_ptr2diary ) );
    add_saver( new ExporterText( m_ptr2diary ) );

    m_CB_extent->append( _( "All Entries" ) );
    m_CB_extent->append( _( "Only Filtered Entries" ) );
    m_CB_extent->set_active( 0 );

    m_combo_type->signal_changed().connect( [ this ](){ handle_savetype_changed(); } );
    m_B_export->signal_clicked().connect( [ this ](){ DialogEvent::response( Result::OK ); } );
    B_cancel->signal_clicked().connect( [ this ](){ DialogEvent::response( Result::ABORTED ); } );

    m_combo_type->set_active( 0 );    // defaults to lifeograph diary
}

void
DialogExport::add_saver( Diarysaver* saver )
{
    m_savers.push_back( saver );
    m_combo_type->append( saver->get_name() );
    saver->signal_updated().connect(
            sigc::mem_fun( this, &DialogExport::handle_saver_updated ) );
}

void
DialogExport::handle_saver_updated( bool ready )
{
    m_flag_saverready = ready;
    m_B_export->set_sensitive( ready );
}

void
DialogExport::handle_savetype_changed()
{
    m_saver_cur = m_savers[ m_combo_type->get_active_row_number() ];

    if( m_ptr2widget_options )
    {
        m_align_options->remove();
        delete m_ptr2widget_options;
    }

    m_ptr2widget_options = m_saver_cur->draw_options();

    if( m_ptr2widget_options )
    {
        m_align_options->add( *m_ptr2widget_options );
        m_F_options->show();
    }
    else
        m_F_options->hide();

    m_flag_saverready = m_saver_cur->is_ready();

    m_B_export->set_sensitive( m_flag_saverready );
}

void
DialogExport::on_response( int response )
{
    if( response == Result::OK )
    {
        // TODO: handle result
        m_saver_cur->save( m_CB_extent->get_active_row_number() );
    }

    Gtk::Dialog::on_response( response );
}

// EXPORTERS =======================================================================================
Diarysaver::Diarysaver( const Glib::ustring& name, Diary* diary )
:   m_name( name ), m_ptr2diary( diary )
{
}

ExporterLifeograph::ExporterLifeograph( Diary* diary )
:   Diarysaver( _( "Lifeograph Diary File" ), diary )
{
}

Result
ExporterLifeograph::save( bool filtered )
{
    std::string path( m_ptr2diary->get_path() + ".backup" );
    std::string pass;

    if( DialogSaveDiary::launch( path, pass ) != Gtk::RESPONSE_ACCEPT )
        return LIFEO::ABORTED;

    if( pass.size() >= LIFEO::PASSPHRASE_MIN_SIZE )
        return m_ptr2diary->write_copy( path, pass, filtered );
    else
        return m_ptr2diary->write_copy( path, "", filtered );
}

ExporterText::ExporterText( Diary* diary )
: Diarysaver( _( "Plain Text File" ), diary )
{

}

Result
ExporterText::save( bool filtered )
{
    // TRANSLATORS: this is the title of file chooser dialog for saving a new diary
    DialogSaveGeneric *ds( new DialogSaveGeneric( _( "Where to Save the Text File?" ) ) );
    ds->set_current_folder( Glib::path_get_dirname( m_ptr2diary->get_path() ) );
    ds->set_current_name( Glib::path_get_basename( m_ptr2diary->get_path() ) + ".txt" );
    if( ds->run() != Gtk::RESPONSE_ACCEPT )
    {
        delete ds;  // also removes the dialog from the screen
        return LIFEO::ABORTED;
    }

    std::string path( ds->get_filename() );
    delete ds;
    return m_ptr2diary->write_txt( path, filtered );
}
