/*
 *       File name:  aghermann/ui/mw/mainmenu_cb.cc
 *         Project:  Aghermann
 *          Author:  Andrei Zavada <johnhommer@gmail.com>
 * Initial version:  2012-11-12
 *
 *         Purpose:  SExpDesignUI widget callbacks (main menu items)
 *
 *         License:  GPL
 */

#include "aghermann/artifact-detection/3in1.hh"
#include "aghermann/rk1968/rk1968.hh"
#include "aghermann/ui/misc.hh"
#include "aghermann/ui/sm/sm.hh"
#include "aghermann/ui/sf/sf.hh"
#include "aghermann/ui/sf/channel.hh"
#include "mw.hh"
#include "mw_cb.hh"

using namespace std;
using namespace agh::ui;

extern "C" {

void
iExpRefresh_activate_cb(
        GtkMenuItem*,
        const gpointer userdata)
{
        auto& ED = *(SExpDesignUI*)userdata;
        ED.do_rescan_tree( true);
}

void
iExpPurgeComputed_activate_cb(
        GtkMenuItem*,
        const gpointer userdata)
{
        auto& ED = *(SExpDesignUI*)userdata;
        ED.do_purge_computed();
}



void
iExpSubjectSortAny_toggled_cb(
        GtkCheckMenuItem* mi,
        const gpointer userdata)
{
        auto& ED = *(SExpDesignUI*)userdata;
        if ( ED.suppress_redraw )
                return;

        // only set ON
        if ( gtk_check_menu_item_get_active( mi) == FALSE )
                return;

        if      ( mi == (GtkCheckMenuItem*)ED.iExpSubjectSortName )
                ED.sort_by = SExpDesignUI::TSubjectSortBy::name;
        else if ( mi == (GtkCheckMenuItem*)ED.iExpSubjectSortAge )
                ED.sort_by = SExpDesignUI::TSubjectSortBy::age;
        else if ( mi == (GtkCheckMenuItem*)ED.iExpSubjectSortAdmissionDate )
                ED.sort_by = SExpDesignUI::TSubjectSortBy::admission_date;
        else if ( mi == (GtkCheckMenuItem*)ED.iExpSubjectSortAvgPower )
                ED.sort_by = SExpDesignUI::TSubjectSortBy::avg_profile_power;

        ED.populate_1();
}


void
iExpSubjectSortAscending_toggled_cb(
        GtkCheckMenuItem*,
        const gpointer userdata)
{
        auto& ED = *(SExpDesignUI*)userdata;
        if ( ED.suppress_redraw )
                return;

        ED.sort_ascending = !ED.sort_ascending;
        ED.populate_1();
}

void
iExpSubjectSortSegregate_toggled_cb(
        GtkCheckMenuItem*,
        const gpointer userdata)
{
        auto& ED = *(SExpDesignUI*)userdata;
        if ( ED.suppress_redraw )
                return;

        ED.sort_segregate = !ED.sort_segregate;
        ED.populate_1();
}






void
iExpAnnotations_activate_cb(
        GtkMenuItem*,
        const gpointer userdata)
{
        auto& ED = *(SExpDesignUI*)userdata;
        ED.suppress_redraw = true;
        gtk_toggle_button_set_active( (GtkToggleButton*)ED.eGlobalAnnotationsShowPhasicEvents,  ED.only_plain_global_annotations);
        gtk_toggle_button_set_active( (GtkToggleButton*)ED.eGlobalAnnotationsShowPhasicEvents, !ED.only_plain_global_annotations);
        ED.suppress_redraw = false;
        gtk_dialog_run( ED.wGlobalAnnotations);
}

// annotations dialog

void
tvGlobalAnnotations_row_activated_cb(
        GtkTreeView*,
        GtkTreePath* path,
        GtkTreeViewColumn*,
        const gpointer userdata)
{
        auto& ED = *(SExpDesignUI*)userdata;
        SExpDesignUI::SAnnotation *ann;
        GtkTreeIter iter;
        gtk_tree_model_get_iter( (GtkTreeModel*)ED.mGlobalAnnotations, &iter, path);
        gtk_tree_model_get( (GtkTreeModel*)ED.mGlobalAnnotations, &iter,
                            ED.mannotations_ref_col, &ann,
                            -1);
        if ( ann == nullptr )
                return;

        gtk_widget_hide( (GtkWidget*)ED.wGlobalAnnotations);
        SScoringFacility* found = nullptr;
        for ( auto &F : ED.open_scoring_facilities )
                if ( &F->csubject() == &ann->csubject
                     && F->session() == ann->session
                     && &F->sepisode() == &ann->sepisode ) {
                        found = F;
                        break;
                }
        if ( found ) {
                auto pages = ann->page_span( found->vpagesize());
                gtk_widget_show( (GtkWidget*)found->wSF);
                found->set_cur_vpage( pages.a, true);
        } else {
                ED.using_subject = ED.subject_presentation_by_csubject( ann->csubject);
                auto SF = new SScoringFacility( ann->csubject, ann->session, ann->sepisode.name(), ED);
                auto pages = ann->page_span( SF->vpagesize());
                SF->set_cur_vpage( pages.a, true);
        }
}



void
eGlobalAnnotationsShowPhasicEvents_toggled_cb(
        GtkToggleButton* b,
        const gpointer userdata)
{
        auto& ED = *(SExpDesignUI*)userdata;
        if ( ED.suppress_redraw )
                return;
        ED.only_plain_global_annotations = not gtk_toggle_button_get_active( b);
        ED.populate_mGlobalAnnotations();
}





void
iExpBasicSADetectUltradianCycles_activate_cb(
        GtkMenuItem*,
        const gpointer userdata)
{
        auto& ED = *(SExpDesignUI*)userdata;

        SBusyBlock bb (ED.wMainWindow);

        using namespace agh;
        CExpDesign::TEpisodeFilterFun filter =
                [&ED]( agh::SEpisode& E) -> bool
                {
                        return E.recordings.find( *ED._AghHi) != E.recordings.end();
                };
        CExpDesign::TEpisodeOpFun F =
                [&ED]( agh::SEpisode& E)
                {
                        ED.do_detect_ultradian_cycle( E.recordings.at( *ED._AghHi));
                };
        CExpDesign::TEpisodeReportFun reporter =
                [&ED]( const agh::CJGroup&, const agh::CSubject& J, const string&, const agh::SEpisode& E,
                       size_t i, size_t n)
                {
                        ED.sb_main_progress_indicator(
                                snprintf_buf(
                                        "Detect ultradian cycle %s/%s/%s",
                                        ED.ED->group_of(J), J.id.c_str(), E.name()),
                                n, i, TGtkRefreshMode::gtk);
                        gtk_widget_queue_draw( (GtkWidget*)ED.cMeasurements);
                };

        ED.ED->for_all_episodes( F, reporter, filter);

        ED.sb_clear();
}

} // extern "C"



template <class C>
void
populate_combo( const list<C>& profiles, GtkListStore* mProfiles)
{
        gtk_list_store_clear( mProfiles);

        if ( not profiles.empty() ) {
                GtkTreeIter iter;
                for ( auto I = profiles.begin(); I != profiles.end(); ++I ) {
                        gtk_list_store_append(
                                mProfiles, &iter);
                        gtk_list_store_set(
                                mProfiles, &iter,
                                0, I->name.c_str(),
                                -1);
                }
        }
}


template <class C>
typename list<C>::iterator
profile_by_idx( list<C>& profiles, size_t idx)
{
        size_t i = 0;
        for ( auto I = profiles.begin(); I != profiles.end(); ++I )
                if ( i == idx )
                        return I;
                else
                        ++i;
        throw invalid_argument ("Current profile index invalid");
}


extern "C" {

void
iExpGloballyScore_activate_cb(
        GtkMenuItem*,
        const gpointer userdata)
{
        auto& ED = *(SExpDesignUI*)userdata;

        using namespace agh;
        using agh::rk1968::CScoreAssistant;
        list<CScoreAssistant> profiles;

        for ( auto A : {TExpDirLevel::system, TExpDirLevel::user, TExpDirLevel::experiment} )
                profiles.splice(
                        profiles.end(),
                        load_profiles_from_location<CScoreAssistant>(
                                CScoreAssistant::common_subdir,
                                A, *ED.ED, agh::SExpDirLevelId {"", "", ""}));

        bool must_select_first = false;
        if ( profiles.empty() ) {
                profiles.emplace_back( *ED.ED, agh::SExpDirLevelId {"", "", ""}), must_select_first = true;
        } else {
                int previously_selected = gtk_combo_box_get_active( ED.eGlobalADProfiles);
                if ( (int)profiles.size() < previously_selected ||  // user has deleted some since we last displayed
                     previously_selected == -1 )
                must_select_first = true;
        }

        populate_combo( profiles, ED.mGlobalRKProfiles);
        gtk_combo_box_set_model( ED.eGlobalRKProfiles, (GtkTreeModel*)ED.mGlobalRKProfiles);
        if ( must_select_first )
                gtk_combo_box_set_active( ED.eGlobalRKProfiles, 0);

        gtk_label_set_markup(
                ED.lGlobalRKHint,
                (profiles.size() < 2)
                ? "<small>You can create a custom profile in Scoring Facility,\n"
                  "after tuning parameters on a real recording.</small>"
                : ""); // good boy


        auto response = gtk_dialog_run( ED.wGlobalScore);
        if ( GTK_RESPONSE_CANCEL == response ||
             GTK_RESPONSE_DELETE_EVENT == response )
                return; // just to save on indents in those lambdas below

        auto P =
                profile_by_idx(
                        profiles,
                        gtk_combo_box_get_active( ED.eGlobalRKProfiles));
        bool keep_existing = gtk_toggle_button_get_active( (GtkToggleButton*)ED.eGlobalRKKeepExisting);

        SBusyBlock bb (ED.wMainWindow);

        using namespace agh;
        CExpDesign::TEpisodeOpFun op;
        CExpDesign::TEpisodeFilterFun filter;
        CExpDesign::TEpisodeReportFun reporter =
                [&]( const CJGroup&, const CSubject& J, const string& D, const SEpisode& E,
                     size_t i, size_t total)
                {
                        ED.sb_main_progress_indicator(
                                snprintf_buf(
                                        "Score %s/%s/%s/%s",
                                        ED.ED->group_of(J), J.id.c_str(), D.c_str(), E.name()),
                                total, i, TGtkRefreshMode::gtk);
                };
        switch ( response ) {
        case GTK_RESPONSE_OK:
                op =
                [&]( SEpisode& E)
                {
                        if ( not keep_existing )
                                for ( auto& F : E.sources )
                                        F.CHypnogram::clear();
// // lua uses global state, is not reentrant
// #ifdef _OPENMP
// #pragma omp critical
// #endif
// #ifdef _OPENMP
                        CScoreAssistant (*P) // new instance for each run
                                . score( E, nullptr);
                };
                filter =
                [&]( SEpisode& E)
                {
                        return true;  // R.signal_type() == sigfile::SChannel::TType::eeg;
                };
            break;

        case 1:  // "Clear All"
                op =
                [&]( SEpisode& E)
                {
                        for ( auto& F : E.sources )
                                F.CHypnogram::clear();
                };
                filter =
                [&]( SEpisode&)
                {
                        return true; // clear in all channels (mark in EEG channels only)
                };
            break;
        default:
                throw runtime_error ("Fix dialog response?");
        }

        forward_list<SBusyBlock*> bbl;
        for ( auto& SFp : ED.open_scoring_facilities )
                bbl.push_front( new SBusyBlock (SFp->wSF));

        ED.ED -> for_all_episodes( op, reporter, filter);
        ED.sb_clear();

        for ( auto& SF : ED.open_scoring_facilities ) {
                for ( auto& H : SF->channels )
                        if ( H.type() == sigfile::SChannel::TType::eeg )
                                H.get_signal_filtered();
                SF->queue_redraw_all();
        }

        ED.populate_1();

        for ( auto& bb : bbl )
                delete bb;
}



void
iExpGloballyDetectArtifacts_activate_cb(
        GtkMenuItem*,
        const gpointer userdata)
{
        auto& ED = *(SExpDesignUI*)userdata;

        using namespace agh;
        using agh::ad::CComprehensiveArtifactDetector;
        list<CComprehensiveArtifactDetector> profiles;

        for ( auto A : {TExpDirLevel::system, TExpDirLevel::user, TExpDirLevel::experiment} )
                profiles.splice(
                        profiles.end(),
                        load_profiles_from_location<CComprehensiveArtifactDetector>(
                                CComprehensiveArtifactDetector::common_subdir,
                                A, *ED.ED, agh::SExpDirLevelId {"", "", ""}));

        bool must_select_first = false;
        if ( profiles.empty() ) {
                profiles.emplace_back( *ED.ED, agh::SExpDirLevelId {"", "", ""}), must_select_first = true;
        } else {
                int previously_selected = gtk_combo_box_get_active( ED.eGlobalADProfiles);
                if ( (int)profiles.size() < previously_selected ||  // user has deleted some since we last displayed
                     previously_selected == -1 )
                must_select_first = true;
        }

        populate_combo( profiles, ED.mGlobalADProfiles);
        gtk_combo_box_set_model( ED.eGlobalADProfiles, (GtkTreeModel*)ED.mGlobalADProfiles);
        if ( must_select_first )
                gtk_combo_box_set_active( ED.eGlobalADProfiles, 0);

        gtk_label_set_markup(
                ED.lGlobalADHint,
                (profiles.size() < 2)
                ? "<small>You can create a custom profile in Scoring Facility,\n"
                  "after tuning parameters on a real recording.</small>"
                : ""); // good boy


        auto response = gtk_dialog_run( ED.wGlobalArtifactDetection);
        if ( GTK_RESPONSE_CANCEL == response ||
             GTK_RESPONSE_DELETE_EVENT == response )
                return; // just to save on indents in those lambdas below

        auto& P =
                profile_by_idx(
                        profiles,
                        gtk_combo_box_get_active( ED.eGlobalADProfiles))
                ->Pp;
        bool keep_existing = gtk_toggle_button_get_active( (GtkToggleButton*)ED.eGlobalADKeepExisting);

        SBusyBlock bb (ED.wMainWindow);

        using namespace agh;
        CExpDesign::TEpisodeOpFun op;
        CExpDesign::TEpisodeFilterFun filter;
        CExpDesign::TEpisodeReportFun reporter =
                [&]( const CJGroup&, const CSubject& J, const string& D, const SEpisode& E,
                     size_t i, size_t total)
                {
                        ED.sb_main_progress_indicator(
                                snprintf_buf(
                                        "Detect artifacts in %s/%s/%s/%s",
                                        ED.ED->group_of(J), J.id.c_str(), D.c_str(), E.name()),
                                total, i, TGtkRefreshMode::gtk);
                };
        switch ( response ) {
        case GTK_RESPONSE_OK:
                op = [&]( SEpisode& E)
                {
                        if ( not keep_existing ) {
                                for ( auto& F : E.sources ) {
                                        for ( size_t h = 0; h < F().n_channels(); ++h )
                                                F().artifacts((int)h).clear_all();
                                        ad::detect_artifacts( F(), P);
                                }
                                ED.ED->sync();
                        }
                };
                filter = [&]( SEpisode& E)
                {
                        return true;  // R.signal_type() == sigfile::SChannel::TType::eeg;
                };
            break;

        case 1:  // "Clear All"
                op = [&]( SEpisode& E)
                {
                        for ( auto& F : E.sources )
                                for ( size_t h = 0; h < F().n_channels(); ++h )
                                        F().artifacts((int)h).clear_all();
                        ED.ED->sync();
                };
                filter = [&]( SEpisode&)
                {
                        return true; // clear in all channels (mark in EEG channels only)
                };
            break;
        default:
                throw runtime_error ("Fix dialog response?");
        }

        forward_list<SBusyBlock*> bbl;
        for ( auto& SFp : ED.open_scoring_facilities )
                bbl.push_front( new SBusyBlock (SFp->wSF));

        ED.ED -> for_all_episodes( op, reporter, filter);
        ED.sb_clear();

        for ( auto& SF : ED.open_scoring_facilities ) {
                for ( auto& H : SF->channels )
                        if ( H.type() == sigfile::SChannel::TType::eeg )
                                H.get_signal_filtered();
                SF->queue_redraw_all();
        }

        ED.populate_1();

        for ( auto& bb : bbl )
                delete bb;
}

void
eGlobalRKProfiles_changed_cb(
        GtkComboBox*,
        const gpointer userdata)
{
        auto& ED = *(SExpDesignUI*)userdata;

        gtk_widget_set_sensitive(
                (GtkWidget*)ED.bGlobalRKOK,
                gtk_combo_box_get_active( ED.eGlobalRKProfiles) != -1);
}

void
eGlobalADProfiles_changed_cb(
        GtkComboBox*,
        const gpointer userdata)
{
        auto& ED = *(SExpDesignUI*)userdata;

        gtk_widget_set_sensitive(
                (GtkWidget*)ED.bGlobalADOK,
                gtk_combo_box_get_active( ED.eGlobalADProfiles) != -1);
}




void
iExpGloballySetFilters_activate_cb(
        GtkMenuItem*,
        const gpointer userdata)
{
        auto& ED = *(SExpDesignUI*)userdata;

        int LPO, HPO, NF;
        double LPC, HPC;
        SUIVarCollection W_V;
        W_V.reg( ED.eGlobalFiltersLowPassCutoff, &LPC);
        W_V.reg( ED.eGlobalFiltersLowPassOrder, &LPO);
        W_V.reg( ED.eGlobalFiltersHighPassCutoff, &HPC);
        W_V.reg( ED.eGlobalFiltersHighPassOrder, &HPO);
        W_V.reg( ED.eGlobalFiltersNotchFilter, &NF);

        if ( GTK_RESPONSE_OK ==
             gtk_dialog_run( ED.wGlobalFilters) ) {
                SBusyBlock bb (ED.wMainWindow);
                forward_list<SBusyBlock*> bbl;
                for ( auto& SFp : ED.open_scoring_facilities )
                        bbl.push_front( new SBusyBlock (SFp->wSF));
                W_V.down();
                for ( auto &G : ED.ED->groups )
                        for ( auto &J : G.second )
                                for ( auto &D : J.measurements )
                                        for ( auto &E : D.second.episodes )
                                                for ( auto &F : E.sources )
                                                        for ( auto &H : F().channel_list() ) {
                                                                auto& ff = F().filters(F().channel_id(H));
                                                                ff.low_pass_cutoff = LPC;
                                                                ff.low_pass_order = LPO;
                                                                ff.high_pass_cutoff = HPC;
                                                                ff.high_pass_order = HPO;
                                                                ff.notch_filter = (sigfile::SFilterPack::TNotchFilter)NF;
                                                        }
                ED.ED->sync();

                for ( auto& SF : ED.open_scoring_facilities ) {
                        for ( auto& H : SF->channels )
                                if ( H.type() == sigfile::SChannel::TType::eeg )
                                        H.get_signal_filtered();
                        SF->queue_redraw_all();
                }
                ED.populate_1();
                for ( auto& bb : bbl )
                        delete bb;
        }
}

void
bGlobalMontageResetAll_clicked_cb(
        GtkButton*,
        const gpointer userdata)
{
        auto& ED = *(SExpDesignUI*)userdata;

        ED.do_delete_all_montages();
}



void
iHelpAbout_activate_cb(
        GtkMenuItem*,
        const gpointer userdata)
{
        auto& ED = *(SExpDesignUI*)userdata;
        // auto w = gtk_widget_get_window( (GtkWidget*)ED.lAboutVersion);
        // gdk_window_set_composited( w, TRUE);
        // gdk_window_set_opacity( w, .7);
        gtk_widget_show_all( (GtkWidget*)ED.wAbout);
}

void
iHelpUsage_activate_cb(
        GtkMenuItem*,
        const gpointer)
{
        gtk_show_uri( NULL,
                      "http://johnhommer.com/academic/code/aghermann/usage/",
                      GDK_CURRENT_TIME, NULL);
}


namespace {

void
before_ED_close( SExpDesignUI& ED)
{
        gtk_window_get_position( ED.wMainWindow, &ED.geometry.x, &ED.geometry.y);
        gtk_window_get_size( ED.wMainWindow, &ED.geometry.w, &ED.geometry.h);
}

} // namespace

void
iExpClose_activate_cb(
        GtkMenuItem*,
        const gpointer userdata)
{
        auto& ED = *(SExpDesignUI*)userdata;

        before_ED_close( ED);
        g_signal_emit_by_name( ED._p->bSessionChooserClose, "clicked");
}

void
iExpQuit_activate_cb(
        GtkMenuItem*,
        const gpointer userdata)
{
        auto& ED = *(SExpDesignUI*)userdata;

        before_ED_close( ED);
        g_signal_emit_by_name( ED._p->bSessionChooserQuit, "clicked");
}



} // extern "C"

// Local Variables:
// Mode: c++
// indent-tabs-mode: nil
// tab-width: 8
// c-basic-offset: 8
// End:
