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

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

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


#ifndef LIFEOGRAPH_WIDGET_FILTER_HEADER
#define LIFEOGRAPH_WIDGET_FILTER_HEADER


#include <gtkmm.h>

#include "../diarydata.hpp"
#include "../entry.hpp"
#include "../filtering.hpp"
#include "widget_datepicker.hpp"
#include "widget_entrypicker.hpp"
#include "widget_picker.hpp"


namespace LIFEO
{

// UI FOR EDITING A FILTERER STACK =================================================================
class FiltererUI : public Gtk::Box
{
    public:
        FiltererUI( FiltererContainer*, Filterer* );
        virtual ~FiltererUI() {}

        Gtk::Button*            m_B_remove;

    protected:
};

class FiltererStatusUI : public FiltererStatus, public FiltererUI
{
    public:
        FiltererStatusUI( Diary*, FiltererContainer*, ElemStatus = FILTER_STATUS_DEFAULT );

        void                    update_state();

    protected:
        Gtk::ToggleButton*      m_ChB_not_todo;
        Gtk::ToggleButton*      m_ChB_open;
        Gtk::ToggleButton*      m_ChB_progressed;
        Gtk::ToggleButton*      m_ChB_done;
        Gtk::ToggleButton*      m_ChB_canceled;
};

class FiltererFavoriteUI : public FiltererFavorite, public FiltererUI
{
    public:
        FiltererFavoriteUI( Diary*, FiltererContainer*, bool = true );

    protected:
        Gtk::CheckButton*       m_ChB_item;
};

class FiltererTrashedUI : public FiltererTrashed, public FiltererUI
{
    public:
        FiltererTrashedUI( Diary*, FiltererContainer*, bool = false );

    protected:
        Gtk::CheckButton*       m_ChB_item;
};

class FiltererIsUI : public FiltererIs, public FiltererUI
{
    public:
        FiltererIsUI( Diary*, FiltererContainer*, DEID = DEID_UNSET, bool = false );

    protected:
        WidgetEntryPicker*      m_W_entry_picker;
        Gtk::ComboBoxText*      m_CB_type;
};

class FiltererHasTagUI : public FiltererHasTag, public FiltererUI
{
    public:
        FiltererHasTagUI( Diary*, FiltererContainer*, Entry* = nullptr, bool = true );

    protected:
        WidgetEntryPicker*      m_W_entry_picker;
        Gtk::ComboBoxText*      m_CB_type;
};

class FiltererThemeUI : public FiltererTheme, public FiltererUI
{
    public:
        FiltererThemeUI( Diary*, FiltererContainer*, Theme* = nullptr, bool = true );

    protected:
        WidgetPicker< Theme >*  m_W_theme_picker;
        Gtk::ComboBoxText*      m_CB_type;
};

class FiltererBetweenDatesUI : public FiltererBetweenDates, public FiltererUI
{
    public:
        FiltererBetweenDatesUI( Diary*, FiltererContainer*, date_t = Date::NOT_SET, bool = false,
                                                            date_t = Date::NOT_SET, bool = false );

    protected:
        WidgetDatePicker*       m_W_date_picker_b;
        WidgetDatePicker*       m_W_date_picker_e;
        Gtk::Label*             m_L_incl_b;
        Gtk::Label*             m_L_incl_e;
};

class FiltererBetweenEntriesUI : public FiltererBetweenEntries, public FiltererUI
{
    public:
        FiltererBetweenEntriesUI( Diary*, FiltererContainer*, Entry* = nullptr, bool = false,
                                                              Entry* = nullptr, bool = false );

    protected:
        WidgetEntryPicker*      m_WEP_bgn;
        WidgetEntryPicker*      m_WEP_end;
        Gtk::Label*             m_L_incl_b;
        Gtk::Label*             m_L_incl_e;
};

class FiltererCompletionUI : public FiltererCompletion, public FiltererUI
{
    public:
        FiltererCompletionUI( Diary*, FiltererContainer*, double = 0.0, double = 100.0 );

    protected:
        Gtk::Scale*             m_Sc_bgn;
        Gtk::Scale*             m_Sc_end;
};

class WidgetFilter : public FiltererContainer, public FiltererUI
{
    public:
        WidgetFilter( Diary*, FiltererContainer* );
        ~WidgetFilter();

        void                    clear() override;

        void                    set_from_string( const Ustring& str ) override
        {
            m_flag_emit_signals = false;
            FiltererContainer::set_from_string( str );
            m_flag_emit_signals = true;
        }

        void                    toggle_logic() override;
        void                    update_logic_label() override;

        void                    add_filterer_status( ElemStatus es ) override
        { add_filterer< FiltererStatusUI, ElemStatus >( es ); }
        void                    add_filterer_favorite( bool f_favorite ) override
        { add_filterer< FiltererFavoriteUI, bool >( f_favorite ); }
        void                    add_filterer_trashed( bool f_trashed ) override
        { add_filterer< FiltererTrashedUI, bool >( f_trashed ); }
        void                    add_filterer_is( DEID id, bool f_is ) override
        { add_filterer< FiltererIsUI, DEID, bool >( id, f_is ); }
        void                    add_filterer_tagged_by( Entry* tag, bool f_has ) override
        { add_filterer< FiltererHasTagUI, Entry*, bool >( tag, f_has ); }
        void                    add_filterer_theme( Theme* theme, bool f_has ) override
        { add_filterer< FiltererThemeUI, Theme*, bool >( theme, f_has ); }
        void                    add_filterer_between_dates( date_t date_b, bool f_incl_b,
                                                            date_t date_e, bool f_incl_e ) override
        { add_filterer< FiltererBetweenDatesUI, date_t, bool, date_t, bool >(
                date_b, f_incl_b, date_e, f_incl_e ); }
        void                    add_filterer_between_entries( Entry* entry_b, bool f_incl_b,
                                                          Entry* entry_e, bool f_incl_e ) override
        { add_filterer< FiltererBetweenEntriesUI, Entry*, bool, Entry*, bool >(
                entry_b, f_incl_b, entry_e, f_incl_e ); }
        void                    add_filterer_completion( double bgn, double end ) override
        { add_filterer< FiltererCompletionUI, double, double >( bgn, end ); }
        FiltererContainer*      add_filterer_subgroup() override
        { return add_filterer< WidgetFilter >(); }
        void                    remove_filterer( Filterer* ) override;

        void                    update_state() override;

        void                    set_enabled( bool );

        SignalVoid              signal_changed()
        { return m_Sg_changed; }

    protected:
        template< class T >
        void                    declare_filterer( const Ustring& label )
        {
            auto button{ Gtk::manage( new Gtk::ModelButton ) };
            button->property_text() = label;
            m_Bx_add->add( *button );
            button->signal_clicked().connect( [ this ](){ add_filterer< T >(); } );
        }

        template< class T, typename... Args >
        T*                      add_filterer( Args... args )
        {
            T* filterer = Gtk::manage( new T( m_p2diary, this, args... ) );
            m_pipeline.push_back( filterer );
            m_Bx_contents->pack_start( *filterer, Gtk::PACK_SHRINK );
            if( m_flag_emit_signals )
                update_state();
            return filterer;
        }

        Gtk::Box*               m_Bx_header;
        Gtk::Box*               m_Bx_contents;
        Gtk::Label*             m_L_logic;
        Gtk::MenuButton*        m_MB_add;
        Gtk::Popover*           m_Po_add{ nullptr };
        Gtk::Box*               m_Bx_add;

        // this flag is used to disable signals during internal updates
        bool                    m_flag_emit_signals{ true };

        SignalVoid              m_Sg_changed;
};

} // end of namespace LIFEO

#endif
