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

    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 "entry.hpp"
#include "diary.hpp"
#include "lifeograph.hpp"
#include "strings.hpp"


using namespace LIFEO;


// STATIC MEMBERS
Entry::Entry( Diary* const d, const date_t date, ElemStatus status )
:   DiaryElement( d, _( STRING::EMPTY_ENTRY_TITLE ), status ),
    m_date( date ),
    m_date_created( time( nullptr ) ),
    m_date_edited( m_date_created ), m_date_status( m_date_created )
{
}

SKVVec
Entry::get_as_skvvec() const
{
    SKVVec sv;
    sv.push_back( { SI::TYPE_NAME, get_type_name() } );
    sv.push_back( { SI::DATE,      m_date.format_string() } );
    sv.push_back( { SI::EDIT_DATE, get_date_edited_str() } );
    sv.push_back( { SI::TODO,      STR0/get_todo_status_si() } );
    sv.push_back( { SI::THEME,     is_theme_set() ? m_p2theme->get_name() : "" } );
    sv.push_back( { SI::UNIT,      m_unit } );
    sv.push_back( { SI::TRASHED,   is_trashed() ? STR0/SI::YES : STR0/SI::NO } );
    sv.push_back( { SI::FAVORED,   is_favored() ? STR0/SI::YES : STR0/SI::NO } );
    sv.push_back( { SI::LANGUAGE,  get_lang() } );
    sv.push_back( { SI::LOCATION,  m_location.is_set() ? m_location.to_string() : "" } );

    std::string path_str;
    for( auto& point : m_map_path )
        path_str += STR::compose( "(", point.to_string(), ")  " );
    sv.push_back( { SI::MAP_PATH, path_str } );

    for( auto& para : m_paragraphs )
        sv.push_back( { SI::PARAGRAPH, para->get_text() } );

    return sv;
}

Entry*
Entry::get_parent() const
{
    return( m_ptr2diary->get_entry_by_date( m_date.get_parent(), true ) );
}

int
Entry::get_child_count() const
{
    int count{ 0 };

    if( m_date.get_level() < 3 )
    {
        auto&  entries  { m_ptr2diary->get_entries() };
        auto&& it_entry { entries.find( this->get_date_t() ) };

        if( it_entry == entries.begin() || it_entry == entries.end() )
            return 0;

        do
        {
            it_entry--;

            if( it_entry->second->get_filtered_out() ) // this may be optional in the future
                continue;
            else
            if( Date::is_child_of( it_entry->second->m_date.m_date, m_date.m_date ) )
                count++;
            else
            if( Date::is_descendant_of( it_entry->second->m_date.m_date, m_date.m_date ) == false )
                break; // all descendants are consecutive

        }
        while( it_entry != entries.begin() );
    }

    return count;
}

VecEntries
Entry::get_descendants() const
{
    // NOTE: also returns grand-children
    VecEntries descendants;

    if( is_ordinal() && m_date.get_level() < 3 )
    {
        auto&  entries  { m_ptr2diary->get_entries() };
        auto&& it_entry { entries.find( this->get_date_t() ) };

        if( it_entry == entries.begin() || it_entry == entries.end() )
            return descendants;

        it_entry--;

        for( ; ; it_entry-- )
        {
            if( Date::is_descendant_of( it_entry->second->m_date.m_date, m_date.m_date ) )
                descendants.push_back( it_entry->second );
            else
                break; // all descendants are consecutive

            if( it_entry == entries.begin() )
                break;
        }
    }

    return descendants;
}

int
Entry::get_descendant_depth() const
{
    int    level{  m_date.get_level() };

    if( level >= 3 ) // covers temporal entries, too
        return 0;

    int    count{ 0 };
    auto&  entries{ m_ptr2diary->get_entries() };
    auto&& it_entry{ entries.find( this->get_date_t() ) };

    if( it_entry == entries.begin() || it_entry == entries.end() )
        return 0;

    it_entry--;

    for( ; ; it_entry-- )
    {
        if( Date::is_descendant_of( it_entry->second->m_date.m_date, m_date.m_date ) )
        {
            const int diff{ it_entry->second->m_date.get_level() - level };
            if( diff > count )
                count = diff;
        }
        else
            break; // all descendants are consecutive

        if( ( count + level ) >= 3 || it_entry == entries.begin() )
            break;
    }

    return count;
}

const Icon&
Entry::get_icon() const
{
    if( m_status & ES::FILTER_TODO_PURE )
        return Lifeograph::get_todo_icon( m_status & ES::FILTER_TODO_PURE );
    else
        return( m_unit.empty() ?
                ( m_date.get_order_3rd() == 0 ?
                  Lifeograph::icons->entry_parent_16 : Lifeograph::icons->entry_16 ) :
                  Lifeograph::icons->tag_16 );
}
const Icon&
Entry::get_icon32() const
{
    if( m_status & ES::FILTER_TODO_PURE )
        return Lifeograph::get_todo_icon32( m_status & ES::FILTER_TODO_PURE );
    else
        return( m_unit.empty() ?
                ( m_date.get_order_3rd() == 0 ?
                  Lifeograph::icons->entry_parent_32 : Lifeograph::icons->entry_32 ) :
                  Lifeograph::icons->tag_32 );
}

Ustring
Entry::get_list_str( bool f_child_count ) const
{
    static const std::string completion_colors[] =
            { "BB0000", "C06000", "B69300", "90B000", "449000", "00B000" };
    const int                child_count{ f_child_count ? get_child_count() : 0 };
    Ustring                  str;

    // GRAYED-OUT
    if( is_trashed() )
    {
        str += "<span color=\"";
        str += Lifeograph::get_color_insensitive();
        str += "\">";
    }

    // BOLD
    if( child_count > 0 )
        str += "<b>";
    // NUMBER
    if( m_date.is_hidden() == false )
    {
        str += m_date.format_string();
        str += " -  ";
    }

    // STRIKE-THROUGH
    if( m_status & ES::CANCELED )
        str += "<s>";

    // NAME ITSELF
    if( m_name.empty() == false )
        str += Glib::Markup::escape_text( m_name );

    // STRIKE-THROUGH
    if( m_status & ES::CANCELED )
        str += "</s>";
    // BOLD
    if( child_count > 0 )
        str += "</b>";
    // GRAYED-OUT
    if( is_trashed() )
        str += "</span>";

    // EXTRA INFO
    // COMPLETION
    if( get_workload() > 0.0 )
    {
        double completion{ get_completion() };
        str += "  <span color=\"#FFFFFF\" bgcolor=\"#";
            // TODO migrate to std::clamp after C++17 upgrade:
        str += completion_colors[ std::max( 0, std::min( 5, int( completion * 5 ) ) ) ];
        str += "\"> ";
        str += STR::format_percentage( completion );
        str += " </span>";
    }
    // CHILD COUNT
    if( child_count > 0 )
    {
        str += "  <span color=\"";
        str += Lifeograph::get_color_insensitive();
        str += "\">(";
        str += std::to_string( child_count );
        str += ")</span>";
    }

    return str;
}

Ustring
Entry::get_title_str() const
 {
     Ustring title;

     if( ! m_date.is_hidden() )
     {
         title = m_date.format_string();
         title += "  ";
     }

     if( m_date.is_ordinal() )
         title += get_name();
     else
         title += m_date.get_weekday_str();

     return title;
 }

void
Entry::set_name( const Ustring& new_name )
{
    if( new_name.empty() )
        m_name = _( STRING::EMPTY_ENTRY_TITLE );
    else
        m_name = new_name;
}

void
Entry::update_name()
{
    if( m_paragraphs.empty() )
        m_name = _( STRING::EMPTY_ENTRY_TITLE );
    else
        m_name = m_paragraphs.front()->get_text();
}

inline bool
is_status_ready( const ElemStatus& s )
{
    return( ( s & ES::PROGRESSED ) || ( ( s & ES::TODO ) && ( s & ES::DONE ) ) );
}
inline ElemStatus
convert_status( const ElemStatus& s )
{
    if( is_status_ready( s ) )
        return( ES::NOT_TODO | ES::PROGRESSED );

    switch( s )
    {
        case ES::CANCELED:
            return( ES::NOT_TODO|ES::CANCELED );
        case ES::TODO:
        case ES::TODO|ES::CANCELED:
            return( ES::NOT_TODO|ES::TODO );
        case ES::DONE:
        case ES::DONE|ES::CANCELED:
            return( ES::NOT_TODO|ES::DONE );
        default:
            return ES::NOT_TODO;
    }
}

ElemStatus
Entry::calculate_todo_status( const std::string& text )
{
    UstringSize pos_current = 0;
    UstringSize pos_end = text.size();
    Wchar ch;
    Wchar lf = '\t';
    ElemStatus status = 0;
    ElemStatus status2add;

    // lambda expression
    auto process_status_char = [ & ]( int s2a )
    {
        if( lf == 's' )
        {
            lf = ']';
            status2add = s2a;
        }
        else
            lf = '\t';
    };

    for( ; pos_current < pos_end; ++pos_current )
    {
        ch = text[ pos_current ];

        // MARKUP PARSING
        switch( ch )
        {
            case '\t':
                if( lf == ch )
                    lf = '[';
                else if( lf != '[' )
                    lf = '\t';
                break;
            case '[':
                if( lf == ch )
                    lf = 's';
                else
                    lf = '\t';
                break;
            case ' ':
                if( lf == ' ' )
                    status |= status2add;
                else
                    process_status_char( ES::TODO );
                break;
            case '~':
                process_status_char( ES::PROGRESSED );
                break;
            case '+':
                process_status_char( ES::DONE );
                break;
            case 'x':
            case 'X':
                process_status_char( ES::CANCELED );
                break;
            case ']':
                if( lf == ch )
                    lf = ' ';
                else
                    lf = '\t';
                break;
            default:
                lf = '\t';
                break;
        }

        if( is_status_ready( status ) )
            break;
    }

    return status;
}

bool
Entry::update_todo_status()
{
    ElemStatus es{ 0 };

    for( auto& para : m_paragraphs )
    {
        es |= calculate_todo_status( para->get_text() );

        if( is_status_ready( es ) )
            break;
    }
    es = convert_status( es );

    if( es != get_todo_status() )
    {
        set_todo_status( es );
        set_date_status( time( nullptr ) );
        return true;
    }

    return false;
}

double
Entry::get_completion() const
{
    const double wl{ get_workload() };

    if( wl == 0.0 )
        return 0.0;

    return( get_completed() / wl );
}

double
Entry::get_completed() const
{
    Entry* tag_comp{ m_ptr2diary->get_completion_tag() };

    if( tag_comp == nullptr )
        return 0.0;

    return get_value_for_tag( tag_comp, false );
}

double
Entry::get_workload() const
{
    Entry* tag_comp{ m_ptr2diary->get_completion_tag() };

    if( tag_comp == nullptr )
        return 0.0;

    return get_value_planned_for_tag( tag_comp, false );
}

Ustring
Entry::get_text() const
{
    Ustring text;
    for( auto& para : m_paragraphs )
    {
        text += para->get_text();
        text += '\n';
    }

    if( text.empty() == false )
        text.erase( text.length() - 1, 1 );

    return text;
}

void
Entry::clear_text()
{
    for( auto& para : m_paragraphs )
        delete para;
    m_paragraphs.clear();
}

void
Entry::set_text( const Ustring& text )
{
    clear_text();
    digest_text( text );

    update_name();
    update_todo_status();

    if( m_ptr2diary && get_type() != ET_CHAPTER )
        m_ptr2diary->update_entry_name( this );
}

void
Entry::insert_text( UstringSize pos, const Ustring& text )
{
    UstringSize para_offset{ UstringSize( get_size() ) };
    Paragraph*  para{ nullptr };

    if( text.empty() )
        return;
    else
    if( pos == Ustring::npos  ) // append text mode
    {
        para = add_paragraph( "" );
        pos = para_offset;
    }
    else
    if( m_paragraphs.empty() )
        para = add_paragraph( "" );
    else
    if( !get_paragraph( pos, para, para_offset = 0 ) )
        throw LIFEO::Error( "Text cannot be inserted!" );

    UstringSize pos_end{ 0 };
    Ustring     para_text;
    auto calculate_next_para = [ & ]( UstringSize pos_bgn ) -> bool
    {
        pos_end = text.find( '\n', pos_bgn );
        if( pos_end == Ustring::npos )
        {
            pos_end = text.length();
            para_text = text.substr( pos_bgn, pos_end - pos_bgn );
            return false;
        }

        para_text = text.substr( pos_bgn, pos_end - pos_bgn );
        return true;
    };

    const Ustring&& remnant_text{ para->get_substr_after( pos - para_offset ) };

    if( calculate_next_para( 0 ) )
    {
        // split paragraph
        if( not( remnant_text.empty() ) )
            para->erase_text( pos - para_offset, remnant_text.length() );

        if( not( para_text.empty() ) ) // i.e. the first char of text is not \n
            para->insert_text( pos - para_offset, para_text );

        while( calculate_next_para( pos_end + 1 ) )
            para = add_paragraph( para_text, para->m_para_no + 1 );

        add_paragraph( para_text + remnant_text, para->m_para_no + 1 );
    }
    else
        para->insert_text( pos - para_offset, text );

    set_date_edited( time( nullptr ) );
    update_name();
}

void
Entry::erase_text( const UstringSize pos_bgn, const UstringSize pos_end )
{
    UstringSize para_offset_bgn{ 0 };
    Paragraph* para_bgn{ nullptr };

    if( !get_paragraph( pos_bgn, para_bgn, para_offset_bgn ) )
        return;

    // merge paragraphs into the first one, if necessary:
    UstringSize para_offset_end{ para_offset_bgn + para_bgn->get_size() + 1 };
    Paragraph* para_end{ nullptr };

    int num_of_deleted_paras{ 0 };

    auto&& iter{ m_paragraphs.begin() };
    std::advance( iter, para_bgn->m_para_no + 1 );
    for( ; iter != m_paragraphs.end(); ++iter )
    {
        if( para_offset_end <= pos_end )
        {
            para_end = *iter;
            para_bgn->append( "\n" + para_end->get_text() );
            para_offset_end += ( para_end->get_size() + 1 );    // +1 to account for the \n
            num_of_deleted_paras++;
            delete para_end;
        }
        else
            break;
    }

    // erase the paragraphs merged into the first one:
    if( para_end )
    {
        auto iter_bgn = m_paragraphs.begin();
        std::advance( iter_bgn, para_bgn->m_para_no + 1 );
        auto iter_end = m_paragraphs.begin();
        std::advance( iter_end, para_bgn->m_para_no + num_of_deleted_paras + 1 );
        iter_end = m_paragraphs.erase( iter_bgn, iter_end );

        // fix paragraph nos of subsequent paragraphs
        for( ; iter_end != m_paragraphs.end(); ++iter_end )
            ( *iter_end )->m_para_no -= num_of_deleted_paras;
    }

    // actually erase the text:
    para_bgn->erase_text( pos_bgn - para_offset_bgn, pos_end - pos_bgn );

    set_date_edited( time( nullptr ) );
}

void
Entry::digest_text( const Ustring& text )
{
    if( text.empty() )
        return;

    int i{ 0 };
    UstringSize pt_bgn{ 0 }, pt_end{ 0 };
    bool flag_terminate_loop{ false };

    while( true )
    {
        pt_end = text.find( '\n', pt_bgn );
        if( pt_end == Ustring::npos )
        {
            pt_end = text.size();
            flag_terminate_loop = true;
        }

        m_paragraphs.push_back(
                new Paragraph( text.substr( pt_bgn, pt_end - pt_bgn), this, i++ ) );

        if( flag_terminate_loop )
            break; // end of while( true )

        pt_bgn = pt_end + 1;
    }
}

bool
Entry::get_paragraph( UstringSize pos, Paragraph*& para, UstringSize& para_offset ) const
{
    for( auto iter = m_paragraphs.begin(); iter != m_paragraphs.end(); ++iter )
    {
        if( pos <= para_offset + ( *iter )->get_size() )
        {
            para = *iter;
            return true;
        }
        else
            para_offset += ( ( *iter )->get_size() + 1 );  // +1 is for \n
    }

    return false;
}
Paragraph*
Entry::get_paragraph( UstringSize pos ) const
{
    UstringSize para_offset{ 0 };

    for( auto& para : m_paragraphs )
    {
        if( pos <= para_offset + para->get_size() )
            return para;
        else
            para_offset += ( para->get_size() + 1 );  // +1 is for \n
    }

    return nullptr;
}

Paragraph*
Entry::add_paragraph( const Ustring& text )
{
    Paragraph* para{ new Paragraph( text, this, m_paragraphs.size() ) };
    m_paragraphs.push_back( para );

    if( m_paragraphs.size() == 1 ) // first paragraph
        m_name = text;

    return para;
}

Paragraph*
Entry::add_paragraph( const Ustring& text, unsigned int pos )
{
    if( pos > m_paragraphs.size() )
        pos = m_paragraphs.size();

    Paragraph* para{ new Paragraph( text, this, pos ) };

    auto iter = m_paragraphs.begin();
    if( pos > 0 )
        std::advance( iter, pos );
    iter = m_paragraphs.insert( iter, para );

    for( ++iter; iter != m_paragraphs.end(); ++iter )
        ( *iter )->m_para_no++;

    if( pos == 0 ) // first paragraph
        m_name = text;

    return para;
}

void
Entry::clear_paragraph_data( UstringSize pos_b, UstringSize pos_e )
{
    UstringSize para_offset{ 0 };

    for( auto& para : m_paragraphs )
    {
        if( pos_b <= para_offset )
        {
            if( pos_e >= ( para_offset + para->get_size() ) )
            {
                para->clear_tags();
                para->set_date( Date::NOT_SET );
            }
            else
                break;
        }
        para_offset += ( para->get_size() + 1 );  // +1 is for \n
    }
}

Ustring
Entry::get_description() const
{
    int i = 0;
    for( auto& para : m_paragraphs )
    {
        if( i > 0 && not( para->is_empty() ) )
            return para->get_text();
        i++;
    }

    return "";
}

Ustring
Entry::get_date_created_str() const
{
    return Date::format_string_dt( m_date_created );
}

Ustring
Entry::get_date_edited_str() const
{
    return Date::format_string_dt( m_date_edited );
}

Ustring
Entry::get_date_status_str() const
{
    return Date::format_string_dt( m_date_status );
}

std::string
Entry::get_lang_final() const
{
    return m_option_lang == LANG_INHERIT_DIARY ?
            m_ptr2diary->get_lang() : m_option_lang;
}

const Theme*
Entry::get_theme() const
{
    return( m_p2theme ? m_p2theme : m_ptr2diary->get_theme_default() );
}

bool
Entry::has_tag( const Entry* tag ) const
{
    for( auto& para : m_paragraphs )
    {
        if( para->has_tag( tag ) )
            return true;
    }

    return false;
}

bool
Entry::has_tag_broad( const Entry* tag ) const
{
    for( auto& para : m_paragraphs )
    {
        if( para->has_tag_broad( tag ) )
            return true;
    }

    return false;
}

SetUstrings
Entry::get_tags() const
{
    SetUstrings set;

    // values should not be needed here
//    for( auto& para : m_paragraphs )
//    {
//        for( auto& kv_tag : para->get_tags() )
//        {
//            auto&& tags = m_ptr2diary->get_entry_by_name( kv_tag.first );
//
//            if( tags.empty() )
//                continue;
//
//            if( map.find( kv_tag.first ) == map.end() )
//                map[ kv_tag.first ] = kv_tag.second;
//            else if( tags[ 0 ]->is_boolean() == false )
//                map[ kv_tag.first ] += kv_tag.second;
//        }
//    }

    for( auto& para : m_paragraphs )
    {
        for( auto& kv_tag : para->get_tags() )
            set.insert( kv_tag.first );
    }

    return set;
}

Value
Entry::get_value_for_tag( const Entry* tag, bool f_average ) const
{
    Value   value{ 0.0 };
    int     count{ 0 };
    for( auto& para : m_paragraphs )
        value += para->get_value_for_tag( tag, count );

    return( f_average ? value/count : value );
}

Value
Entry::get_value_planned_for_tag( const Entry* tag, bool f_average ) const
{
    Value   value{ 0.0 };
    int     count{ 0 };
    for( auto& para : m_paragraphs )
        value += para->get_value_planned_for_tag( tag, count );

    return( f_average ? value/count : value );
}

Value
Entry::get_value_remaining_for_tag( const Entry* tag, bool f_average ) const
{
    Value   value{ 0.0 };
    int     count{ 0 };
    for( auto& para : m_paragraphs )
        value += para->get_value_remaining_for_tag( tag, count );

    return( f_average ? value/count : value );
}

Entry*
Entry::get_sub_tag_first( const Entry* tag ) const
{
    for( auto& para : m_paragraphs )
    {
        auto sub_tag{ para->get_sub_tag_first( tag ) };
        if( sub_tag )
            return sub_tag;
    }

    return nullptr;
}

Entry*
Entry::get_sub_tag_last( const Entry* tag ) const
{
    if( tag != nullptr )
    {
        for( auto&& iter = m_paragraphs.rbegin(); iter != m_paragraphs.rend(); iter++ )
        {
            auto sub_tag{ ( *iter )->get_sub_tag_last( tag ) };
            if( sub_tag )
                return sub_tag;
        }
    }

    return nullptr;
}

Entry*
Entry::get_sub_tag_lowest( const Entry* tag ) const
{
    Entry* sub_tag_lowest{ nullptr };

    if( tag != nullptr )
    {
        for( auto& para : m_paragraphs )
        {
            auto sub_tag{ para->get_sub_tag_lowest( tag ) };
            if( sub_tag )
            {
                if( sub_tag_lowest )
                {
                    if( sub_tag->get_date_t() < sub_tag_lowest->get_date_t() )
                        sub_tag_lowest = sub_tag;
                }
                else
                    sub_tag_lowest = sub_tag;
            }
        }
    }

    return sub_tag_lowest;
}

Entry*
Entry::get_sub_tag_highest( const Entry* tag ) const
{
    Entry* sub_tag_highest{ nullptr };

    if( tag != nullptr )
    {
        for( auto& para : m_paragraphs )
        {
            auto sub_tag{ para->get_sub_tag_highest( tag ) };
            if( sub_tag )
            {
                if( sub_tag_highest )
                {
                    if( sub_tag->get_date_t() > sub_tag_highest->get_date_t() )
                        sub_tag_highest = sub_tag;
                }
                else
                    sub_tag_highest = sub_tag;
            }
        }
    }

    return sub_tag_highest;
}

Value
Entry::get_value_for_tag( const ChartData* chart_data ) const
{
    Value   value{ 0.0 };
    int     count{ 0 };
    for( auto& para : m_paragraphs )
        value += para->get_value_for_tag( chart_data, count );

    return( chart_data->is_average() ? value/count : value );
}

Value
Entry::get_value_planned_for_tag( const ChartData* chart_data ) const
{
    Value   value{ 0.0 };
    int     count{ 0 };
    for( auto& para : m_paragraphs )
        value += para->get_value_planned_for_tag( chart_data, count );

    return( chart_data->is_average() ? value/count : value );
}

void
Entry::add_tag( Entry* tag, Value value )
{
    if( is_empty() )
        add_paragraph( "" );

    if( value == 1.0 )
        add_paragraph( ":" + tag->get_name() + ":" );
    else
        add_paragraph( STR::compose( ":", tag->get_name(), ":=", value ) );
}

void
Entry::remove_map_path_point( const Coords& pt )
{
    for( ListLocations::iterator it = m_map_path.begin(); it != m_map_path.end(); it++ )
    {
        if( ( *it ).is_equal_to( pt ) )
        {
            m_map_path.erase( it );
            return;
        }
    }
}

double
Entry::get_map_path_length() const
{
    double dist{ 0.0 };
    Coords pt_prev;
    bool   flag_after_first{ false };

    for( auto& pt : m_map_path )
    {
        if( flag_after_first )
            dist += Coords::get_distance( pt_prev, pt );
        else
            flag_after_first = true;

        pt_prev = pt;
    }

    return( Lifeograph::settings.use_imperial_units ? dist / LIFEO::MI_TO_KM_RATIO : dist );
}

// ENTRY SET =======================================================================================
PoolEntries::~PoolEntries()
{
    for( iterator iter = begin(); iter != end(); ++iter )
        delete iter->second;
}

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

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