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

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

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


#include <cassert>

#include "chapter.hpp"
#include "lifeograph.hpp"
#include "strings.hpp"


using namespace LIFEO;

// CHAPTER =========================================================================================
// STATIC MEMBERS
Chapter::Chapter( Diary* const d, date_t date, ElemStatus status )
:   Entry( d, date, status )
{
}

SKVVec
Chapter::get_as_skvvec() const
{
    SKVVec&& sv{ Entry::get_as_skvvec() };

    sv.push_back( { SI::COLOR, convert_gdkrgba_to_string( m_color ) } );

    return sv;
}

const Icon&
Chapter::get_icon() const
{
    if( m_status & ES::FILTER_TODO_PURE )
        return Lifeograph::get_todo_icon( m_status & ES::FILTER_TODO_PURE );
    else
        return Lifeograph::icons->chapter_16;
}
const Icon&
Chapter::get_icon32() const
{
    if( ( m_status & ES::FILTER_TODO_PURE ) )
        return Lifeograph::get_todo_icon32( m_status & ES::FILTER_TODO_PURE );
    else
        return Lifeograph::icons->chapter_32;
}

Ustring
Chapter::get_list_str() const
{
    static const Ustring tpl[] = { "<b>%2</b>", "<b>%1 -  %2</b>",
                                   "<b><s>%2</s></b>", "<b><s>%1 -  %2</s></b>" };
    int i{ m_date.is_hidden() ? 0 : 1 };
    if( m_status & ES::CANCELED ) i += 2;


    return Glib::ustring::compose( tpl[ i ], m_date.format_string(),
                                   Glib::Markup::escape_text( m_name ) );
}

Ustring
Chapter::get_date_str() const
{
    return m_date.format_string();
}

date_t
Chapter::get_free_order() const
{
    return m_p2diary->get_available_order_sub( m_date.m_date );
}

void
Chapter::recalculate_span( const Chapter* next )
{
    if( next == nullptr )
        m_time_span = 0;    // unlimited
    else if( next->m_date.is_ordinal() )
        m_time_span = 0;    // last temporal chapter: unlimited
    else
        m_time_span = m_date.calculate_days_between( next->m_date );
}

// CHAPTER CATEGORY ================================================================================
CategoryChapters::CategoryChapters( Diary* const d, const Ustring& name )
:   DiaryElement( d, name ),
    std::map< date_t, Chapter*, FuncCompareDates >( compare_dates )
{
}

CategoryChapters::~CategoryChapters()
{
    for( CategoryChapters::iterator iter = begin(); iter != end(); ++iter )
        delete iter->second;
}

date_t
CategoryChapters::get_date_t() const
{
    if( size() > 0 )
        return rbegin()->second->get_date_t();
    else
        return Date::NOT_SET;
}

Chapter*
CategoryChapters::get_chapter_at( const date_t date ) const
{
    const_iterator iter( find( date ) );
    return( iter == end() ? nullptr : iter->second );
}

Chapter*
CategoryChapters::get_chapter_around( const date_t date ) const
{
    for( auto& kv_chapter : *this )
    {
        if( kv_chapter.first <= date )
            return kv_chapter.second;
    }

    return nullptr;
}

Chapter*
CategoryChapters::create_chapter( date_t date, bool F_favorite, bool F_trashed, bool F_expanded )
{
    ElemStatus status{ ES::NOT_TODO |
                       ( F_favorite ? ES::FAVORED : ES::NOT_FAVORED ) |
                       ( F_trashed ? ES::TRASHED : ES::NOT_TRASHED ) };
    if( F_expanded ) status |= ES::EXPANDED;

    Chapter* chapter{ new Chapter( m_p2diary, Date::get_pure( date ), status ) };

    add( chapter );

    return chapter;
}

bool
CategoryChapters::set_chapter_date( Chapter* chapter, date_t date )
{
    assert( Date::is_ordinal( date ) == false );

    if( find( date ) != end() )
        return false;

    if( chapter->m_date.m_date != Date::NOT_SET )
    {
        iterator iter( find( chapter->m_date.m_date ) );
        if( iter == end() )
            return false; // chapter is not a member of this category

        if( ( ++iter ) != end() ) // fix time span
        {
            Chapter* chapter_earlier( iter->second );
            if( chapter->m_time_span > 0 )
                chapter_earlier->m_time_span += chapter->m_time_span;
            else
                chapter_earlier->m_time_span = 0;
        }

        erase( chapter->m_date.m_date );
        chapter->set_ctg( nullptr );
    }

    chapter->set_date( date );

    add( chapter );

    return true;
}

bool
CategoryChapters::add( Chapter* chapter )
{
    iterator iter = insert( CategoryChapters::value_type(
            chapter->m_date.m_date, chapter ) ).first;

    if( iter == begin() ) // latest
        chapter->recalculate_span( nullptr );
    else
    {
        iterator iter_next( iter );
        iter_next--;
        chapter->recalculate_span( iter_next->second );
    }

    if( ( ++iter ) != end() ) // if not earliest fix previous
        iter->second->recalculate_span( chapter );

    chapter->set_ctg( this );

    return true; // reserved
}

void
CategoryChapters::clear()
{
    for( CategoryChapters::iterator iter = begin(); iter != end(); ++iter )
        delete iter->second;

    std::map< date_t, Chapter*, FuncCompareDates >::clear();
}

PoolCategoriesChapters::~PoolCategoriesChapters()
{
    for( PoolCategoriesChapters::iterator iter = begin(); iter != end(); ++iter )
        delete( iter->second );
}

void
PoolCategoriesChapters::clear()
{
    for( PoolCategoriesChapters::iterator iter = begin(); iter != end(); ++iter )
        delete( iter->second );

    std::map< Ustring, CategoryChapters*, FuncCmpStrings >::clear();
}

Chapter*
PoolCategoriesChapters::get_chapter( const Ustring& ch, const date_t date ) const
{
    auto kv = find( ch );

    if( kv == end() )
        return nullptr;

    return kv->second->get_chapter_at( date );
}

CategoryChapters*
PoolCategoriesChapters::get_ctg( const Ustring& name ) const
{
    auto iter = find( name );
    if( iter != end() )
        return( iter->second );
    else
        return nullptr;
}
