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

    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_ENTRY_HEADER
#define LIFEOGRAPH_ENTRY_HEADER


#include "diarydata.hpp"


namespace LIFEO
{

typedef sigc::signal< void, LIFEO::Entry* > SignalVoidEntry;

static const char LANG_INHERIT_DIARY[] = "d";

// ENTRY ===========================================================================================
class Entry : public DiaryElement
{
    public:
                                Entry( Diary* const, const date_t, ElemStatus = ES::ENTRY_DEFAULT );
        virtual                 ~Entry() {}

        virtual SKVVec          get_as_skvvec() const override;

        Entry*                  get_parent() const;
        int                     get_child_count() const;
        VecEntries              get_descendants() const;
        int                     get_descendant_depth() const;

        bool                    is_empty() const
        {
            switch( m_paragraphs.size() )
            {
                case 0:  return true;
                case 1:  return m_paragraphs.front()->is_empty();
                default: return false;
            }
        }
        Ustring                 get_text() const;
        void                    clear_text();
        void                    set_text( const Ustring& );
        void                    insert_text( UstringSize, const Ustring& );
        void                    erase_text( const UstringSize, const UstringSize );

        ListParagraphs*         get_paragraphs()
        { return &m_paragraphs; }
        const ListParagraphs*   get_paragraphs() const
        { return &m_paragraphs; }
        bool                    get_paragraph( UstringSize, Paragraph*&, UstringSize& ) const;
        Paragraph*              get_paragraph( UstringSize ) const;
        Paragraph*              add_paragraph( const Ustring& );
        Paragraph*              add_paragraph( const Ustring&, unsigned int );
        void                    add_paragraph( Paragraph* para )
        {
            m_paragraphs.push_back( para );
            if( m_paragraphs.size() == 1 ) // first paragraph
                m_name = para->get_text();
        }
        void                    clear_paragraph_data( UstringSize, UstringSize );

        Ustring                 get_description() const; // returns 2nd non-empty paragraph

        Date                    get_date() const override { return m_date; }
        virtual void            set_date( date_t date )
        { m_date.m_date = date; }

        time_t                  get_date_created() const { return m_date_created; }
        Ustring                 get_date_created_str() const;

        time_t                  get_date_edited() const { return m_date_edited; }
        Ustring                 get_date_edited_str() const;
        void                    set_date_edited( time_t d ) { m_date_edited = d; }

        time_t                  get_date_status() const { return m_date_status; }
        Ustring                 get_date_status_str() const;
        void                    set_date_status( date_t d ) { m_date_status = d; }

        bool                    is_ordinal() const
        { return( m_date.is_ordinal() ); }
        bool                    is_ordinal_hidden() const
        { return( m_date.is_hidden() ); }

        int                     get_size() const override
        {
            int size{ 0 };
            for( auto& para : m_paragraphs )
                size += ( para->get_size() + 1 );

            return( size > 0 ? size - 1 : 0 );
        }
        virtual Type            get_type() const override
        { return ET_ENTRY; }

        virtual const Icon&     get_icon() const override;
        virtual const Icon&     get_icon32() const override;

        bool                    has_name() const
        { if( m_paragraphs.empty() ) return false;
          return( not( m_paragraphs.front()->is_empty() ) ); }
        Ustring                 get_name_pure() const
        { return( m_paragraphs.empty() ? "" : m_paragraphs.front()->get_text() ); }
        void                    set_name( const Ustring& ) override;
        void                    update_name();
        // NOTE: for some reason Glib::ustring::at() is rather slow. so we are switching to
        // std::string here, thanks to the fact that no UTF-8 characters are used in formatting:
        static ElemStatus       calculate_todo_status( const std::string& );
        bool                    update_todo_status();
        double                  get_completion() const;
        double                  get_completed() const;
        double                  get_workload() const;

        Ustring                 get_list_str( bool = true ) const;
        Ustring                 get_title_str() const;

        bool                    is_favored() const { return( m_status & ES::FAVORED ); }
        void                    set_favored( bool favored )
        {
            m_status -= ( m_status & ES::FILTER_FAVORED );
            m_status |= ( favored ? ES::FAVORED : ES::NOT_FAVORED );
        }
        void                    toggle_favored()
        { m_status ^= ES::FILTER_FAVORED; }

        std::string             get_lang() const { return m_option_lang; }
        std::string             get_lang_final() const;
        void                    set_lang( const std::string& lang ) { m_option_lang = lang; }

        bool                    is_trashed() const { return( m_status & ES::TRASHED ); }
        void                    set_trashed( bool trashed )
        {
            m_status -= ( m_status & ES::FILTER_TRASHED );
            m_status |= ( trashed ? ES::TRASHED : ES::NOT_TRASHED );
        }

        // TAGS
        bool                    has_tag( const Entry* ) const;
        bool                    has_tag_broad( const Entry* ) const;
        SetUstrings             get_tags() const;
        Value                   get_value_for_tag( const Entry*, bool ) const;
        Value                   get_value_planned_for_tag( const Entry*, bool ) const;
        Value                   get_value_remaining_for_tag( const Entry*, bool ) const;
        Entry*                  get_sub_tag_first( const Entry* ) const;
        Entry*                  get_sub_tag_last( const Entry* ) const;
        Entry*                  get_sub_tag_lowest( const Entry* ) const;
        Entry*                  get_sub_tag_highest( const Entry* ) const;
        Value                   get_value_for_tag( const ChartData* ) const;
        Value                   get_value_planned_for_tag( const ChartData* ) const;
        void                    add_tag( Entry*, Value = 1.0 );

        // UNITS
        Ustring                 get_unit() const
        { return m_unit; }
        void                    set_unit( Ustring unit )
        { m_unit = unit; }

        // THEMES
        void                    set_theme( const Theme* theme )
        { m_p2theme = theme; }
        const Theme*            get_theme() const;
        bool                    is_theme_set() const
        { return( m_p2theme != nullptr ); }
        bool                    has_theme( const Ustring& name ) const
        { return( get_theme()->get_name() == name ); }

        // LOCATION
        Coords                  get_location()
        { return m_location; }
        void                    set_location( double lat, double lon )
        { m_location.latitude = lat; m_location.longitude = lon; }
        void                    remove_location()
        { m_location.unset(); }
        bool                    is_location_set() const
        { return m_location.is_set(); }
        const ListLocations&    get_map_path()
        { return m_map_path; }
        bool                    is_map_path_set() const
        { return( m_map_path.empty() == false ); }
        void                    clear_map_path()
        { m_map_path.clear(); }
        void                    add_map_path_point( double lat, double lon )
        { m_map_path.push_back( { lat, lon } ); }
        void                    remove_last_map_path_point()
        { m_map_path.pop_back(); }
        void                    remove_map_path_point( const Coords& );
        Coords&                 get_map_path_end()
        { return m_map_path.back(); }
        double                  get_map_path_length() const;

        // SCROLLING
        double                  get_scroll_pos() const
        { return m_scroll_pos; }
        void                    set_scroll_pos( double pos )
        { m_scroll_pos = pos; }

    protected:
        void                    digest_text( const Ustring& );

        Date                    m_date;
        time_t                  m_date_created;
        time_t                  m_date_edited;
        time_t                  m_date_status;
        ListParagraphs          m_paragraphs;
        Coords                  m_location;
        ListLocations           m_map_path;
        const Theme*            m_p2theme{ nullptr }; // nullptr means theme is not set
        Ustring                 m_unit;

        double                  m_scroll_pos{ 0.0 };

        std::string             m_option_lang = LANG_INHERIT_DIARY;  // empty means off

    friend class Diary; // TODO: remove this friendship too??
};

// ENTRY SET ===================================================================
class PoolEntries : public std::map< date_t, Entry*, FuncCompareDates >
{
    public:
                                PoolEntries()
    :   std::map< date_t, Entry*, FuncCompareDates >( compare_dates ) {}
                                ~PoolEntries();

        void                    clear();
};

typedef PoolEntries::iterator               EntryIter;
typedef PoolEntries::reverse_iterator       EntryIterReverse;
typedef PoolEntries::const_iterator         EntryIterConst;
typedef PoolEntries::const_reverse_iterator EntryIterConstRev;

typedef std::multimap< Ustring, Entry* >    PoolEntryNames;

} // end of namespace LIFEO

#endif

