/*
 *       File name:  aghermann/ui/sf/d/patterns.cc
 *         Project:  Aghermann
 *          Author:  Andrei Zavada <johnhommer@gmail.com>
 * Initial version:  2011-01-14
 *
 *         Purpose:  scoring facility Patterns dialog crazy state machine
 *
 *         License:  GPL
 */

#include "aghermann/ui/misc.hh"
#include "aghermann/ui/sf/channel.hh"
#include "patterns.hh"

using namespace std;
using namespace agh::ui;


SScoringFacility::SPatternsDialog&
SScoringFacility::
patterns_d()
{
        if ( not _patterns_d )
                _patterns_d = new SPatternsDialog(*this);
        return *_patterns_d;
}

SScoringFacility::SPatternsDialog::
SPatternsDialog (SScoringFacility& p_)
      : SDirlevelStorableAdapter<pattern::SPattern<TFloat>> (
              *p_._p.ED, agh::SExpDirLevelId {p_._p.ED->group_of(p_.csubject()), p_.csubject().id, p_.session()},
              mSFFDProfiles, eSFFDProfileList, eSFFDProfileList_changed_cb_handler_id,
              bSFFDProfileSave, bSFFDProfileRevert, bSFFDProfileDiscard,
              wSFFDProfileSave, eSFFDProfileSaveName,
              eSFFDProfileSaveOriginSubject, eSFFDProfileSaveOriginExperiment, eSFFDProfileSaveOriginUser,
              bSFFDProfileSaveOK),
        Pp (Pp2.Pp), criteria (Pp2.criteria),
        increment (.03),
        field_profile_type (metrics::TType::mc),
        suppress_redraw (false),
        draw_details (true),
        draw_match_index (true),
        _p (p_)
{
      // 1. widgets
        builder = gtk_builder_new();
        if ( !gtk_builder_add_from_resource( builder, "/org/gtk/aghermann/sf-patterns.glade", NULL) )
                throw runtime_error( "Failed to load SF::patterns glade resource");
        gtk_builder_connect_signals( builder, NULL);

        mSFFDProfiles =
                gtk_list_store_new( 1, G_TYPE_STRING);
        mSFFDChannels =
                gtk_list_store_new( 1, G_TYPE_STRING);

        AGH_GBGETOBJ (wSFFD);
        AGH_GBGETOBJ (daSFFDThing);
        AGH_GBGETOBJ (swSFFDThing);
        AGH_GBGETOBJ (daSFFDField);
        AGH_GBGETOBJ (iibSFFDMenu);
        AGH_GBGETOBJ (iiSFFDField);
        AGH_GBGETOBJ (iiSFFDFieldProfileTypes);
        AGH_GBGETOBJ (iSFFDFieldDrawMatchIndex);
        AGH_GBGETOBJ (iSFFDFieldProfileTypeRaw);
        AGH_GBGETOBJ (iSFFDFieldProfileTypePSD);
        AGH_GBGETOBJ (iSFFDFieldProfileTypeMC);
        AGH_GBGETOBJ (iSFFDFieldProfileTypeSWU);
        AGH_GBGETOBJ (iSFFDMarkPhasicEventSpindles);
        AGH_GBGETOBJ (iSFFDMarkPhasicEventKComplexes);
        AGH_GBGETOBJ (iSFFDMarkPlain);
        AGH_GBGETOBJ (swSFFDField);
        AGH_GBGETOBJ (cSFFDSearchButton);
        AGH_GBGETOBJ (cSFFDAgainButton);
        AGH_GBGETOBJ (cSFFDSearching);
        AGH_GBGETOBJ (cSFFDParameters);
        AGH_GBGETOBJ (cSFFDCriteria);
        AGH_GBGETOBJ (bSFFDSearch);
        AGH_GBGETOBJ (bSFFDAgain);
        AGH_GBGETOBJ (bSFFDProfileSave);
        AGH_GBGETOBJ (bSFFDProfileDiscard);
        AGH_GBGETOBJ (bSFFDProfileRevert);
        AGH_GBGETOBJ (eSFFDEnvTightness);
        AGH_GBGETOBJ (eSFFDBandPassOrder);
        AGH_GBGETOBJ (eSFFDBandPassFrom);
        AGH_GBGETOBJ (eSFFDBandPassUpto);
        AGH_GBGETOBJ (eSFFDDZCDFStep);
        AGH_GBGETOBJ (eSFFDDZCDFSigma);
        AGH_GBGETOBJ (eSFFDDZCDFSmooth);
        AGH_GBGETOBJ (eSFFDParameterA);
        AGH_GBGETOBJ (eSFFDParameterB);
        AGH_GBGETOBJ (eSFFDParameterC);
        AGH_GBGETOBJ (eSFFDParameterD);
        AGH_GBGETOBJ (eSFFDIncrement);
        AGH_GBGETOBJ (cSFFDLabelBox);
        AGH_GBGETOBJ (lSFFDParametersBrief);
        AGH_GBGETOBJ (lSFFDFoundInfo);
        AGH_GBGETOBJ (eSFFDProfileList);
        AGH_GBGETOBJ (eSFFDChannel);
        AGH_GBGETOBJ (wSFFDProfileSave);
        AGH_GBGETOBJ (eSFFDProfileSaveName);
        AGH_GBGETOBJ (eSFFDProfileSaveOriginSubject);
        AGH_GBGETOBJ (eSFFDProfileSaveOriginExperiment);
        AGH_GBGETOBJ (eSFFDProfileSaveOriginUser);
        AGH_GBGETOBJ (bSFFDProfileSaveOK);

        gtk_combo_box_set_model_properly( eSFFDProfileList, mSFFDProfiles);
        eSFFDProfileList_changed_cb_handler_id =
                G_CONNECT_1 (eSFFDProfileList, changed);

        // filter channels we don't have
        for ( auto &H : _p.channels ) {
                GtkTreeIter iter;
                gtk_list_store_append(
                        mSFFDChannels,
                        &iter);
                gtk_list_store_set(
                        mSFFDChannels, &iter,
                        0, H.name(),
                        -1);
        }
        gtk_combo_box_set_model_properly( eSFFDChannel, mSFFDChannels);
        eSFFDChannel_changed_cb_handler_id =
                G_CONNECT_1 (eSFFDChannel, changed);

        G_CONNECT_2 (wSFFD, configure, event);
        G_CONNECT_1 (daSFFDThing, draw);
        G_CONNECT_3 (daSFFDThing, button, press, event);
        G_CONNECT_2 (daSFFDThing, scroll, event);
        G_CONNECT_1 (daSFFDField, draw);
        G_CONNECT_2 (daSFFDField, scroll, event);
        G_CONNECT_3 (daSFFDField, motion, notify, event);
        G_CONNECT_3 (daSFFDField, button, press, event);
        G_CONNECT_1 (bSFFDProfileSave, clicked);
        G_CONNECT_1 (bSFFDProfileDiscard, clicked);
        G_CONNECT_1 (bSFFDProfileRevert, clicked);
        G_CONNECT_1 (bSFFDSearch, clicked);
        G_CONNECT_1 (bSFFDAgain, clicked);
        G_CONNECT_1 (eSFFDProfileSaveName, changed);
        G_CONNECT_1 (iSFFDFieldDrawMatchIndex, toggled);
        G_CONNECT_1 (iSFFDMarkPhasicEventSpindles, activate);
        G_CONNECT_1 (iSFFDMarkPhasicEventKComplexes, activate);
        G_CONNECT_1 (iSFFDMarkPlain, activate);

        for ( auto& W : {eSFFDEnvTightness,
                         eSFFDBandPassFrom, eSFFDBandPassUpto, eSFFDBandPassOrder,
                         eSFFDDZCDFStep, eSFFDDZCDFSigma, eSFFDDZCDFSmooth} )
                g_signal_connect(
                        W, "value-changed",
                        (GCallback)eSFFD_any_profile_value_changed_cb,
                        this);
        for ( auto& W : {eSFFDParameterA, eSFFDParameterB, eSFFDParameterC, eSFFDParameterD} ) {
                g_signal_connect(
                        W, "value-changed",
                        (GCallback)eSFFD_any_criteria_value_changed_cb,
                        this);
                g_signal_connect(
                        W, "focus-in-event",
                        (GCallback)eSFFD_any_criteria_focus_in_event_cb,
                        this);
        }
        for ( auto& W : {eSFFDProfileSaveOriginUser, eSFFDProfileSaveOriginExperiment, eSFFDProfileSaveOriginSubject} )
                g_signal_connect(
                        W, "toggled",
                        (GCallback)eSFFD_any_profile_origin_toggled_cb,
                        this);
        for ( auto& W : {iSFFDFieldProfileTypeRaw, iSFFDFieldProfileTypePSD, iSFFDFieldProfileTypeMC, iSFFDFieldProfileTypeSWU} )
                g_signal_connect(
                        W, "toggled",
                        (GCallback)iSFFD_any_field_profile_type_toggled_cb,
                        this);

        G_CONNECT_1 (wSFFD, show);
        G_CONNECT_1 (wSFFD, hide);

      // 2. dialog
        W_V.reg( eSFFDEnvTightness,  &Pp.env_scope);
        W_V.reg( eSFFDBandPassOrder, &Pp.bwf_order);
        W_V.reg( eSFFDBandPassFrom,  &Pp.bwf_ffrom);
        W_V.reg( eSFFDBandPassUpto,  &Pp.bwf_fupto);
        W_V.reg( eSFFDDZCDFStep,     &Pp.dzcdf_step);
        W_V.reg( eSFFDDZCDFSigma,    &Pp.dzcdf_sigma);
        W_V.reg( eSFFDDZCDFSmooth,   &Pp.dzcdf_smooth);

        W_V.reg( eSFFDParameterA,    &get<0>(Pp2.criteria));
        W_V.reg( eSFFDParameterB,    &get<1>(Pp2.criteria));
        W_V.reg( eSFFDParameterC,    &get<2>(Pp2.criteria));
        W_V.reg( eSFFDParameterD,    &get<3>(Pp2.criteria));

        W_V.reg( eSFFDIncrement,     &increment);

        atomic_up();
}

SScoringFacility::SPatternsDialog::
~SPatternsDialog ()
{
        // save_profiles(); // saved by ~SPattern
        // destroy toplevels
        gtk_widget_destroy( (GtkWidget*)wSFFDProfileSave);
        gtk_widget_destroy( (GtkWidget*)wSFFD);
        g_object_unref( (GObject*)mSFFDProfiles);
        g_object_unref( (GObject*)mSFFDChannels);
        g_object_unref( (GObject*)builder);
}






void
SScoringFacility::SPatternsDialog::
search()
{
        assert (field_channel and current_profile != profiles.end());

        if ( field_channel != field_channel_saved )
                field_channel_saved = field_channel;

        pattern::CPatternTool<TFloat> cpattern
                ({current_profile->thing, current_profile->samplerate},
                 current_profile->context,
                 Pp); // use this for the case when modiified current_profile changes have not been committed
        diff_line =
                (cpattern.do_search(
                        field_channel->signal_envelope( Pp.env_scope).first,
                        field_channel->signal_envelope( Pp.env_scope).second,
                        field_channel->signal_bandpass( Pp.bwf_ffrom, Pp.bwf_fupto, Pp.bwf_order),
                        field_channel->signal_dzcdf( Pp.dzcdf_step, Pp.dzcdf_sigma, Pp.dzcdf_smooth),
                        increment * current_profile->samplerate),
                 cpattern.diff);
}


size_t
SScoringFacility::SPatternsDialog::
find_occurrences()
{
        if ( unlikely (current_profile == profiles.end()) )
                return 0;

        occurrences.resize(0);
        size_t inc = max((int)(increment * current_profile->samplerate), 1);
        for ( size_t i = 0; i < diff_line.size() - current_profile->thing.size(); i += inc )
                if ( diff_line[i].good_enough( criteria) ) {
                        occurrences.push_back(i);
                        i +=  // avoid overlapping occurrences *and* ensure we hit the stride
                                current_profile->pattern_size_essential()/inc * inc;
                }

        restore_annotations();
        occurrences_to_annotations();
        _p.queue_redraw_all();

        return occurrences.size();
}


void
SScoringFacility::SPatternsDialog::
occurrences_to_annotations( sigfile::SAnnotation::TType t)
{
        for ( size_t o = 0; o < occurrences.size(); ++o )
                sigfile::mark_annotation(
                        &field_channel->annotations,
                        ((double)occurrences[o]) / field_channel->samplerate(),
                        ((double)occurrences[o] + current_profile->pattern_size_essential()) / field_channel->samplerate(),
                        snprintf_buf("%s (%zu)", current_profile->name.c_str(), o+1),
                        t);
}

void
SScoringFacility::SPatternsDialog::
save_annotations()
{
        saved_annotations = field_channel->annotations;
}

void
SScoringFacility::SPatternsDialog::
restore_annotations()
{
        field_channel->annotations = saved_annotations;
        saved_annotations.clear();
}




void
SScoringFacility::SPatternsDialog::
setup_controls_for_find()
{
        bool    have_any = current_profile != profiles.end();

        gtk_widget_set_visible( (GtkWidget*)cSFFDSearchButton, have_any and TRUE);
        gtk_widget_set_visible( (GtkWidget*)cSFFDSearching, FALSE);
        gtk_widget_set_visible( (GtkWidget*)cSFFDAgainButton, FALSE);

        gtk_widget_set_visible( (GtkWidget*)cSFFDParameters, have_any and TRUE);

        gtk_widget_set_visible( (GtkWidget*)swSFFDField, FALSE);
        gtk_widget_set_visible( (GtkWidget*)cSFFDCriteria, FALSE);

        gtk_widget_set_sensitive( (GtkWidget*)eSFFDProfileList, TRUE);

        gtk_widget_set_visible( (GtkWidget*)iibSFFDMenu, FALSE);

        gtk_label_set_markup( lSFFDFoundInfo, "");
}

void
SScoringFacility::SPatternsDialog::
setup_controls_for_wait()
{
        gtk_widget_set_visible( (GtkWidget*)cSFFDSearchButton, FALSE);
        gtk_widget_set_visible( (GtkWidget*)cSFFDSearching, TRUE);
        gtk_widget_set_visible( (GtkWidget*)cSFFDAgainButton, FALSE);

        gtk_widget_set_visible( (GtkWidget*)cSFFDParameters, TRUE);

        gtk_widget_set_visible( (GtkWidget*)swSFFDField, FALSE);
        gtk_widget_set_visible( (GtkWidget*)cSFFDCriteria, FALSE);

        gtk_widget_set_sensitive( (GtkWidget*)eSFFDProfileList, FALSE);

        gtk_widget_set_visible( (GtkWidget*)iibSFFDMenu, FALSE);
}

void
SScoringFacility::SPatternsDialog::
setup_controls_for_tune()
{
        gtk_widget_set_visible( (GtkWidget*)cSFFDSearchButton, FALSE);
        gtk_widget_set_visible( (GtkWidget*)cSFFDSearching, FALSE);
        gtk_widget_set_visible( (GtkWidget*)cSFFDAgainButton, TRUE);

        gtk_widget_set_visible( (GtkWidget*)cSFFDParameters, FALSE);

        gtk_widget_set_visible( (GtkWidget*)swSFFDField, TRUE);
        gtk_widget_set_visible( (GtkWidget*)cSFFDCriteria, TRUE);

        gtk_widget_set_sensitive( (GtkWidget*)eSFFDProfileList, FALSE);

        gtk_widget_set_visible( (GtkWidget*)iibSFFDMenu, TRUE);
}




void
SScoringFacility::SPatternsDialog::
preselect_channel( const int h) const
{
        if ( h < 0 ) {
                gtk_combo_box_set_active( eSFFDChannel, -1);
                return;
        }

        gtk_combo_box_set_active( eSFFDChannel, h);
}



size_t
SScoringFacility::SPatternsDialog::
nearest_occurrence( const double x) const
{
        double shortest = INFINITY;
        size_t found_at = -1;
        for ( size_t o = 0; o < occurrences.size(); ++o ) {
                double d = fabs((double)occurrences[o]/diff_line.size() - x/da_field_wd);
                if ( d < shortest ) {
                        shortest = d;
                        found_at = o;
                }
        }
        return found_at;
}




void
SScoringFacility::SPatternsDialog::
update_field_check_menu_items()
{
        suppress_redraw = true;
        gtk_check_menu_item_set_active( iSFFDFieldDrawMatchIndex, draw_match_index);

        if ( not field_channel->schannel.is_fftable() ) {
                field_profile_type = metrics::TType::raw;
                gtk_widget_set_visible( (GtkWidget*)iiSFFDFieldProfileTypes, FALSE);
        } else
                gtk_widget_set_visible( (GtkWidget*)iiSFFDFieldProfileTypes, TRUE);

        switch ( field_profile_type ) {
        case metrics::TType::raw:
                gtk_check_menu_item_set_active( (GtkCheckMenuItem*)iSFFDFieldProfileTypeRaw, TRUE);
                break;
        case metrics::TType::psd:
                gtk_check_menu_item_set_active( (GtkCheckMenuItem*)iSFFDFieldProfileTypePSD, TRUE);
                break;
        case metrics::TType::mc:
                gtk_check_menu_item_set_active( (GtkCheckMenuItem*)iSFFDFieldProfileTypeMC, TRUE);
                break;
        case metrics::TType::swu:
                gtk_check_menu_item_set_active( (GtkCheckMenuItem*)iSFFDFieldProfileTypeSWU, TRUE);
                break;
        }

        suppress_redraw = false;
}

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