/*
 *       File name:  aghermann/ui/sf/sf.hh
 *         Project:  Aghermann
 *          Author:  Andrei Zavada <johnhommer@gmail.com>
 * Initial version:  2011-01-14
 *
 *         Purpose:  scoring facility class
 *
 *         License:  GPL
 */

#ifndef AGH_AGHERMANN_UI_SF_SF_H_
#define AGH_AGHERMANN_UI_SF_SF_H_

#include <forward_list>
#include <list>
#include <string>

#include <cairo/cairo.h>
#include <itpp/base/mat.h>

#include "common/alg.hh"
#include "common/config-validate.hh"
#include "libsigfile/page.hh"
#include "libsigfile/forward-decls.hh"
#include "aghermann/expdesign/forward-decls.hh"
#include "aghermann/ica/forward-decls.hh"
#include "aghermann/ui/globals.hh"
#include "aghermann/ui/ui++.hh"

#include "widgets.hh"

#if HAVE_CONFIG_H && !defined(VERSION)
#  include "config.h"
#endif


using namespace std;

namespace agh {
namespace ui {

class SExpDesignUI;

class SScoringFacility
  : public SScoringFacilityWidgets {

        DELETE_DEFAULT_METHODS (SScoringFacility);

    public:
      // ctor, dtor
        SScoringFacility (agh::CSubject&, const string& d, const string& e,
                          SExpDesignUI& parent);
       ~SScoringFacility ();

      // link to parent
        SExpDesignUI&
                _p;
        void redraw_ssubject_timeline() const;

    private:
        agh::CSubject&
                _csubject;
        string  _session;
        agh::SEpisode&
                _sepisode;
    public:
        agh::CSubject&  csubject() const { return _csubject; }
        agh::SEpisode&  sepisode() const { return _sepisode; }
        const string&   session()  const { return _session;  }

      // channels
        struct SChannel;
        list<SChannel>
                channels;
        size_t  n_eeg_channels;

        SChannel&
        operator[]( const string&);

        SChannel&
        operator[]( size_t i)
                { return channel_by_idx(i); }

        SChannel&
        channel_by_idx( size_t);

        void
        update_all_channels_profile_display_scale();

      // collected common annotations
        list<pair<const sigfile::CSource*, const sigfile::SAnnotation*>>
                common_annotations;

      // timeline
        time_t
        start_time() const;

        vector<char>
                hypnogram;
        size_t
        total_pages() const
                { return hypnogram.size(); }

        size_t
        total_vpages() const
                { return p2ap( total_pages()); }

        void get_hypnogram();
        void put_hypnogram();

        float   scored_percent,
                scored_percent_nrem,
                scored_percent_rem,
                scored_percent_wake;

        void calculate_scored_percent();

      // state and flags
        // volatile
        bool    suppress_redraw:1,
                hypnogram_button_down:1,
                artifacts_dialog_shown:1,
                rk1968_dialog_shown:1;
        enum class TMode {
                scoring,
                marking,
                shuffling_channels,
                moving_selection,
                separating,
                showing_ics,
                showing_remixed
        };
        TMode   mode;
        size_t  crosshair_at;
        double  crosshair_at_time;
        // persistent
        bool    show_cur_pos_time_relative,
                draw_crosshair,
                alt_hypnogram;

      // page and vpage index
        size_t cur_page() const         { return _cur_page;  }
        size_t cur_vpage() const        { return _cur_vpage; }
        void set_cur_vpage( size_t p, bool touch_self = true);
        void set_cur_page( size_t p, bool touch_self = true)
                { set_cur_vpage( p2ap(p), touch_self); }

        size_t
        cur_page_start() const // in seconds
                { return _cur_page * pagesize(); }

        size_t
        cur_page_end() const // in seconds
                { return (_cur_page + 1) * pagesize(); }

        sigfile::SPage::TScore
        cur_page_score() const
                { return sigfile::SPage::char2score( hypnogram[_cur_page]); }

        bool vpage_has_artifacts( size_t, bool check_all_channels = true) const;
        bool vpage_has_annotations( size_t, const SChannel&) const;

      // pagesize
        size_t pagesize() const;

        static const array<unsigned, 9>
                DisplayPageSizeValues;
        static size_t
        figure_display_pagesize_item( size_t seconds);
    private:
        int     pagesize_item;

    public:
        size_t vpagesize() const;
        bool pagesize_is_right() const;
        size_t cur_vpage_start() const; // in seconds
        size_t cur_vpage_end() const; // in seconds
        size_t p2ap( size_t p) const; // page to visible_page
        size_t ap2p( size_t p) const;
    private:
        size_t  _cur_page,
                _cur_vpage;

    public:
      // page location adjusted for pre- and post margins
        float   skirting_run_per1;
        float xvpagesize() const;
        double cur_xvpage_start() const;
        double cur_xvpage_end() const;
        double time_at_click( double) const;

        void set_vpagesize_item( int item, bool touch_self = true); // touches a few wisgets
        void set_vpagesize( size_t seconds, bool touch_self = true);

    public:
      // ICA support
        ica::CFastICA
                *ica;
        itpp::mat  // looks like it has to be double
                ica_components;
        size_t n_ics() const;

        enum TICMapFlags : int { apply_normally = 0, dont_apply = 1 };
        struct SICMapOptions { int m; };
        vector<SICMapOptions>
                ica_map;
        typedef function<valarray<TFloat>()> TICASetupFun;
        int setup_ica();
        int run_ica();
        enum class TICARemixMode { map, punch, zero };
        TICARemixMode remix_mode;
        static const char
                *ica_unmapped_menu_item_label;
        int remix_ics();
        int restore_ics();
        int ic_near( double y) const;
        int ic_of( const SChannel*) const;
        int using_ic;
        int apply_remix( bool do_backup);

    public:
      // channel slots
        template <class T>
        float channel_y0( const T& h) const;

        SChannel*
        channel_near( int y);

        float   interchannel_gap;
        void estimate_montage_height();
        int find_free_space();
        void space_evenly();
        void expand_by_factor( double);

        int     n_hidden;

        // things to remember bwtween button_press_event_cb and motion_cb
        double  event_y_when_shuffling;
        float   zeroy_before_shuffling;
        float   moving_selection_handle_offset;

  // montage
  // load/save/reset
    public:
        agh::confval::CConfigKeys
                config;
        void load_montage();
        void save_montage(); // using libconfig
        void reset_montage();

  // draw
    public:
        void draw_montage( cairo_t*);
        void draw_montage( const string& fname); // to a file (uses da_wd and da_ht
    private:
        template <class T>
        void _draw_matrix_to_montage( cairo_t*, const itpp::Mat<T>&);
        void _draw_hour_ticks( cairo_t*, int, int, bool with_cursor = true);
    public:
        void draw_hypnogram( cairo_t*);
        void draw_score_stats() const;
        void draw_current_pos( double) const;
        void queue_redraw_all() const;

  // main montage menu
    public:
        void update_main_menu_items();
        void do_dialog_import_hypnogram();
        void do_dialog_export_hypnogram() const;
        void do_clear_hypnogram();

        void do_score_forward( char score_ch);
        void do_score_back( char score_ch);

      // status bar
        void sb_message( const string&) const;
        void sb_clear() const;

      // tips
        enum TTipIdx : size_t {
                scoring_mode,
                ica_mode
        };
        void set_tooltip( TTipIdx i) const;

      // child dialogs:
    public:
        struct SPatternsDialog;
        SPatternsDialog&
        patterns_d();

        struct SFiltersDialog;
        SFiltersDialog&
        filters_d();

        struct SPhasediffDialog;
        SPhasediffDialog&
        phasediff_d();

        struct SArtifactsDialog;
        SArtifactsDialog&
        artifacts_d();

        struct SRK1968Dialog;
        SRK1968Dialog&
        rk1968_d();

    private:
        SPatternsDialog
                *_patterns_d;
        SFiltersDialog
                *_filters_d;
        SPhasediffDialog
                *_phasediff_d;
        SArtifactsDialog
                *_artifacts_d;
        SRK1968Dialog
                *_rk1968_d;

    public:
      // menu support
        SChannel
                *using_channel;
        int
        channel_idx( SChannel* h) const;
        int
        using_channel_idx() const
                { return channel_idx( using_channel); }

        list<sigfile::SAnnotation*>
                over_annotations;
        sigfile::SAnnotation*
        interactively_choose_annotation() const;

    private:
        static const char* const tooltips[2];

    public:
        // SGeometry
        // geometry;

        static size_t
                IntersignalSpace,
                HypnogramHeight,
                EMGProfileHeight;

    public:
        // here's hoping configure-event comes before expose-event
        gint    da_wd;
        float   da_ht;  // not subject to window resize, this, but should withstand / 3 * 3
};



inline size_t
SScoringFacility::
vpagesize() const
{
        return DisplayPageSizeValues[pagesize_item];
}
inline bool
SScoringFacility::
pagesize_is_right() const
{
        return pagesize() == vpagesize();
}

inline size_t
SScoringFacility::
cur_vpage_start() const // in seconds
{
        return _cur_vpage * vpagesize();
}
inline size_t
SScoringFacility::
cur_vpage_end() const // in seconds
{
        return (_cur_vpage + 1) * vpagesize();
}
inline size_t
SScoringFacility::
p2ap( size_t p) const // page to visible_page
{
        return (size_t)(p * (float)pagesize() / vpagesize());
}

inline size_t
SScoringFacility::
ap2p( size_t p) const
{
        return (size_t)((p) * (float)vpagesize() / pagesize());
}



inline float
SScoringFacility::
xvpagesize() const
{
        return (1. + 2*skirting_run_per1) * vpagesize();
}

inline double
SScoringFacility::
cur_xvpage_start() const
{
        return cur_vpage_start() - skirting_run_per1 * vpagesize();
}

inline double
SScoringFacility::
cur_xvpage_end() const
{
        return cur_vpage_end() + skirting_run_per1 * vpagesize();
}

inline double
SScoringFacility::
time_at_click( double x) const
{
        return cur_xvpage_start() + x/da_wd * xvpagesize();
}

inline void
SScoringFacility::
set_vpagesize( size_t seconds, bool touch_self)
{
        set_vpagesize_item( figure_display_pagesize_item( seconds), touch_self);
}




template <class T>
float
SScoringFacility::
channel_y0( const T& h) const
{
        auto H = find( channels.begin(), channels.end(), h);
        return ( H != channels.end() ) ? H->zeroy : NAN;
}


}
} // namespace agh::ui

#endif

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