/*
  This file is part of the kcalcore library.

  Copyright (c) 1998 Preston Brown <pbrown@kde.org>
  Copyright (c) 2001,2003,2004 Cornelius Schumacher <schumacher@kde.org>
  Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Library General Public
  License as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This library 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
  Library General Public License for more details.

  You should have received a copy of the GNU Library General Public License
  along with this library; see the file COPYING.LIB.  If not, write to
  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  Boston, MA 02110-1301, USA.
*/
/**
  @file
  This file is part of the API for handling calendar data and
  defines the MemoryCalendar class.

  @brief
  This class provides a calendar stored as a local file.

  @author Preston Brown \<pbrown@kde.org\>
  @author Cornelius Schumacher \<schumacher@kde.org\>
 */

#include "memorycalendar.h"

#include <KDebug>

using namespace KCalCore;

/**
  Private class that helps to provide binary compatibility between releases.
  @internal
*/
//@cond PRIVATE
class KCalCore::MemoryCalendar::Private
{
  public:
    Private( MemoryCalendar *qq ) : q( qq )
    {
    }
    ~Private()
    {
    }

    MemoryCalendar *q;
    QString mFileName;                     // filename where calendar is stored
    CalFormat *mFormat;                    // calendar format

    /**
     * List of all incidences.
     * First indexed by incidence->type(), then by incidence->uid();
     */
    QMap<IncidenceBase::IncidenceType, QMultiHash<QString, Incidence::Ptr> > mIncidences;

    /**
     * List of all deleted incidences.
     * First indexed by incidence->type(), then by incidence->uid();
     */
    QMap<IncidenceBase::IncidenceType, QMultiHash<QString, Incidence::Ptr> > mDeletedIncidences;

    /**
     * Contains incidences ( to-dos; non-recurring, non-multiday events; journals; )
     * indexed by start/due date.
     *
     * The QMap key is the incidence->type().
     * The QMultiHash key is the dtStart/dtDue().toString()
     *
     * Note: We had 3 variables, mJournalsForDate, mTodosForDate and mEventsForDate
     * but i merged them into one (indexed by type) because it simplifies code using
     * it. No need to if else based on type.
     */
    QMap<IncidenceBase::IncidenceType, QMultiHash<QString, IncidenceBase::Ptr> > mIncidencesForDate;

    void insertIncidence( Incidence::Ptr incidence );

    Incidence::Ptr incidence( const QString &uid,
                              const IncidenceBase::IncidenceType type,
                              const KDateTime &recurrenceId = KDateTime() ) const;

    Incidence::Ptr deletedIncidence( const QString &uid,
                                     const KDateTime &recurrenceId,
                                     const IncidenceBase::IncidenceType type ) const;

    void deleteAllIncidences( const IncidenceBase::IncidenceType type );

};

MemoryCalendar::MemoryCalendar( const KDateTime::Spec &timeSpec )
  : Calendar( timeSpec ),
  d( new KCalCore::MemoryCalendar::Private( this ) )
{
}

MemoryCalendar::MemoryCalendar( const QString &timeZoneId )
  : Calendar( timeZoneId ),
    d( new KCalCore::MemoryCalendar::Private( this ) )
{
}

MemoryCalendar::~MemoryCalendar()
{
  close();
  delete d;
}

void MemoryCalendar::close()
{
  setObserversEnabled( false );
  d->mFileName.clear();

  deleteAllEvents();
  deleteAllTodos();
  deleteAllJournals();

  d->mDeletedIncidences.clear();

  setModified( false );

  setObserversEnabled( true );
}

bool MemoryCalendar::deleteIncidence( const Incidence::Ptr &incidence )
{
  // Handle orphaned children
  // relations is an Incidence's property, not a Todo's, so
  // we remove relations in deleteIncidence, not in deleteTodo.
  removeRelations( incidence );
  const Incidence::IncidenceType type = incidence->type();
  const QString uid = incidence->uid();
  if ( d->mIncidences[type].remove( uid, incidence ) ) {
    setModified( true );
    notifyIncidenceDeleted( incidence );
    d->mDeletedIncidences[type].insert( uid, incidence );

    const KDateTime dt = incidence->dateTime( Incidence::RoleCalendarHashing );
    if ( dt.isValid() ) {
      d->mIncidencesForDate[type].remove( dt.date().toString(), incidence );
    }
    // Delete child-incidences.
    if ( !incidence->hasRecurrenceId() ) {
      deleteIncidenceInstances( incidence );
    }
    return true;
  } else {
    kWarning() << incidence->typeStr() << " not found.";
    return false;
  }
}

bool MemoryCalendar::deleteIncidenceInstances( const Incidence::Ptr &incidence )
{
  const Incidence::IncidenceType type = incidence->type();
  QList<Incidence::Ptr> values = d->mIncidences[type].values( incidence->uid() );
  QList<Incidence::Ptr>::const_iterator it;
  for ( it = values.constBegin(); it != values.constEnd(); ++it ) {
    Incidence::Ptr i = *it;
    if ( i->hasRecurrenceId() ) {
      kDebug() << "deleting child"
               << ", type=" << int( type )
               << ", uid=" << i->uid()
               << ", start=" << i->dtStart()
               << " from calendar";
      deleteIncidence( i );
    }
  }

  return true;
}

void MemoryCalendar::Private::deleteAllIncidences( const Incidence::IncidenceType incidenceType )
{
  QHashIterator<QString, Incidence::Ptr>i( mIncidences[incidenceType] );
  while ( i.hasNext() ) {
    i.next();
    q->notifyIncidenceDeleted( i.value() );
    // suppress update notifications for the relation removal triggered
    // by the following deletions
    i.value()->startUpdates();
  }
  mIncidences[incidenceType].clear();
  mIncidencesForDate[incidenceType].clear();
}

Incidence::Ptr MemoryCalendar::Private::incidence( const QString &uid,
                                                   const Incidence::IncidenceType type,
                                                   const KDateTime &recurrenceId ) const
{
  QList<Incidence::Ptr> values = mIncidences[type].values( uid );
  QList<Incidence::Ptr>::const_iterator it;
  for ( it = values.constBegin(); it != values.constEnd(); ++it ) {
    Incidence::Ptr i = *it;
    if ( recurrenceId.isNull() ) {
      if ( !i->hasRecurrenceId() ) {
        return i;
      }
    } else {
      if ( i->hasRecurrenceId() && i->recurrenceId() == recurrenceId ) {
        return i;
      }
    }
  }
  return Incidence::Ptr();
}

Incidence::Ptr
MemoryCalendar::Private::deletedIncidence( const QString &uid,
                                           const KDateTime &recurrenceId,
                                           const IncidenceBase::IncidenceType type ) const
{
  QList<Incidence::Ptr> values = mDeletedIncidences[type].values( uid );
  QList<Incidence::Ptr>::const_iterator it;
  for ( it = values.constBegin(); it != values.constEnd(); ++it ) {
    Incidence::Ptr i = *it;
    if ( recurrenceId.isNull() ) {
      if ( !i->hasRecurrenceId() ) {
        return i;
      }
    } else {
      if ( i->hasRecurrenceId() && i->recurrenceId() == recurrenceId ) {
        return i;
      }
    }
  }
  return Incidence::Ptr();
}

//@cond PRIVATE
void MemoryCalendar::Private::insertIncidence( Incidence::Ptr incidence )
{
  const QString uid = incidence->uid();
  const Incidence::IncidenceType type = incidence->type();
  if ( !mIncidences[type].contains( uid, incidence ) ) {
    mIncidences[type].insert( uid, incidence );
    const KDateTime dt = incidence->dateTime( Incidence::RoleCalendarHashing );
    if ( dt.isValid() ) {
      mIncidencesForDate[type].insert( dt.date().toString(), incidence );
    }

  } else {
#ifndef NDEBUG
    // if we already have an to-do with this UID, it must be the same incidence,
    // otherwise something's really broken
    Q_ASSERT( mIncidences[type].value( uid ) == incidence );
#endif
  }
}
//@endcond

bool MemoryCalendar::addIncidence( const Incidence::Ptr &incidence )
{
  notifyIncidenceAdded( incidence );

  d->insertIncidence( incidence );

  incidence->registerObserver( this );

  setupRelations( incidence );

  setModified( true );

  return true;
}

bool MemoryCalendar::addEvent( const Event::Ptr &event )
{
  return addIncidence( event );
}

bool MemoryCalendar::deleteEvent( const Event::Ptr &event )
{
  return deleteIncidence( event );
}

bool MemoryCalendar::deleteEventInstances( const Event::Ptr &event )
{
  return deleteIncidenceInstances( event );
}

void MemoryCalendar::deleteAllEvents()
{
  d->deleteAllIncidences( Incidence::TypeEvent );
}

Event::Ptr MemoryCalendar::event( const QString &uid,
                                  const KDateTime &recurrenceId ) const
{
  return d->incidence( uid, Incidence::TypeEvent, recurrenceId ).staticCast<Event>();
}

Event::Ptr MemoryCalendar::deletedEvent( const QString &uid, const KDateTime &recurrenceId ) const
{
  return d->deletedIncidence( uid, recurrenceId, Incidence::TypeEvent ).staticCast<Event>();
}

bool MemoryCalendar::addTodo( const Todo::Ptr &todo )
{
  return addIncidence( todo );
}

bool MemoryCalendar::deleteTodo( const Todo::Ptr &todo )
{
  return deleteIncidence( todo );
}

bool MemoryCalendar::deleteTodoInstances( const Todo::Ptr &todo )
{
  return deleteIncidenceInstances( todo );
}

void MemoryCalendar::deleteAllTodos()
{
  d->deleteAllIncidences( Incidence::TypeTodo );
}

Todo::Ptr MemoryCalendar::todo( const QString &uid,
                                const KDateTime &recurrenceId ) const
{
  return d->incidence( uid, Incidence::TypeTodo, recurrenceId ).staticCast<Todo>();
}

Todo::Ptr MemoryCalendar::deletedTodo( const QString &uid,
                                       const KDateTime &recurrenceId ) const
{
  return d->deletedIncidence( uid, recurrenceId, Incidence::TypeTodo ).staticCast<Todo>();
}

Todo::List MemoryCalendar::rawTodos( TodoSortField sortField,
                                     SortDirection sortDirection ) const
{
  Todo::List todoList;
  QHashIterator<QString, Incidence::Ptr>i( d->mIncidences[Incidence::TypeTodo] );
  while ( i.hasNext() ) {
    i.next();
    todoList.append( i.value().staticCast<Todo>() );
  }
  return Calendar::sortTodos( todoList, sortField, sortDirection );
}

Todo::List MemoryCalendar::deletedTodos( TodoSortField sortField,
                                         SortDirection sortDirection ) const
{
  Todo::List todoList;
  QHashIterator<QString, Incidence::Ptr >i( d->mDeletedIncidences[Incidence::TypeTodo] );
  while ( i.hasNext() ) {
    i.next();
    todoList.append( i.value().staticCast<Todo>() );
  }
  return Calendar::sortTodos( todoList, sortField, sortDirection );
}

Todo::List MemoryCalendar::todoInstances( const Incidence::Ptr &todo,
                                          TodoSortField sortField,
                                          SortDirection sortDirection ) const
{
  Todo::List list;

  QList<Incidence::Ptr > values = d->mIncidences[Incidence::TypeTodo].values( todo->uid() );
  QList<Incidence::Ptr>::const_iterator it;
  for ( it = values.constBegin(); it != values.constEnd(); ++it ) {
    Todo::Ptr t = ( *it ).staticCast<Todo>();
    if ( t->hasRecurrenceId() ) {
      list.append( t );
    }
  }
  return Calendar::sortTodos( list, sortField, sortDirection );
}

Todo::List MemoryCalendar::rawTodosForDate( const QDate &date ) const
{
  Todo::List todoList;
  Todo::Ptr t;

  KDateTime::Spec ts = timeSpec();
  const QString dateStr = date.toString();
  QMultiHash<QString, IncidenceBase::Ptr >::const_iterator it =
    d->mIncidencesForDate[Incidence::TypeTodo].constFind( dateStr );
  while ( it != d->mIncidencesForDate[Incidence::TypeTodo].constEnd() && it.key() == dateStr ) {
    t = it.value().staticCast<Todo>();
    todoList.append( t );
    ++it;
  }

  // Iterate over all todos. Look for recurring todoss that occur on this date
  QHashIterator<QString, Incidence::Ptr >i( d->mIncidences[Incidence::TypeTodo] );
  while ( i.hasNext() ) {
    i.next();
    t = i.value().staticCast<Todo>();
    if ( t->recurs() ) {
      if ( t->recursOn( date, ts ) ) {
        todoList.append( t );
      }
    }
  }

  return todoList;
}

Todo::List MemoryCalendar::rawTodos( const QDate &start,
                                     const QDate &end,
                                     const KDateTime::Spec &timespec,
                                     bool inclusive ) const
{
  Q_UNUSED( inclusive ); // use only exact dtDue/dtStart, not dtStart and dtEnd

  Todo::List todoList;
  KDateTime::Spec ts = timespec.isValid() ? timespec : timeSpec();
  KDateTime st( start, ts );
  KDateTime nd( end, ts );

  // Get todos
  QHashIterator<QString, Incidence::Ptr >i( d->mIncidences[Incidence::TypeTodo] );
  Todo::Ptr todo;
  while ( i.hasNext() ) {
    i.next();
    todo = i.value().staticCast<Todo>();
    if ( !isVisible( todo ) ) {
      continue;
    }

    KDateTime rStart = todo->hasDueDate() ? todo->dtDue() :
                       todo->hasStartDate() ? todo->dtStart() : KDateTime();
    if ( !rStart.isValid() ) {
      continue;
    }

    if ( !todo->recurs() ) { // non-recurring todos
      if ( nd.isValid() && nd < rStart ) {
        continue;
      }
      if ( st.isValid() && rStart < st ) {
        continue;
      }
    } else { // recurring events
      switch( todo->recurrence()->duration() ) {
      case -1: // infinite
        break;
      case 0: // end date given
      default: // count given
        KDateTime rEnd( todo->recurrence()->endDate(), ts );
        if ( !rEnd.isValid() ) {
          continue;
        }
        if ( st.isValid() && rEnd < st ) {
          continue;
        }
        break;
      } // switch(duration)
    } //if(recurs)

    todoList.append( todo );
  }

  return todoList;
}

Alarm::List MemoryCalendar::alarmsTo( const KDateTime &to ) const
{
  return alarms( KDateTime( QDate( 1900, 1, 1 ) ), to );
}

Alarm::List MemoryCalendar::alarms( const KDateTime &from, const KDateTime &to ) const
{
  Alarm::List alarmList;
  QHashIterator<QString, Incidence::Ptr>ie( d->mIncidences[Incidence::TypeEvent] );
  Event::Ptr e;
  while ( ie.hasNext() ) {
    ie.next();
    e = ie.value().staticCast<Event>();
    if ( e->recurs() ) {
      appendRecurringAlarms( alarmList, e, from, to );
    } else {
      appendAlarms( alarmList, e, from, to );
    }
  }

  QHashIterator<QString, Incidence::Ptr>it( d->mIncidences[Incidence::TypeTodo] );
  Todo::Ptr t;
  while ( it.hasNext() ) {
    it.next();
    t = it.value().staticCast<Todo>();

    if ( !t->isCompleted() ) {
      appendAlarms( alarmList, t, from, to );
      if ( t->recurs() ) {
        appendRecurringAlarms( alarmList, t, from, to );
      } else {
        appendAlarms( alarmList, t, from, to );
      }
    }
  }

  return alarmList;
}

void MemoryCalendar::incidenceUpdate( const QString &uid, const KDateTime &recurrenceId )
{
  Incidence::Ptr inc = incidence( uid, recurrenceId );

  if ( inc ) {
    const Incidence::IncidenceType type = inc->type();
    const KDateTime dt = inc->dateTime( Incidence::RoleCalendarHashing );

    if ( dt.isValid() ) {
      d->mIncidencesForDate[type].remove( dt.date().toString(), inc );
    }
  }
}

void MemoryCalendar::incidenceUpdated( const QString &uid, const KDateTime &recurrenceId )
{
  Incidence::Ptr inc = incidence( uid, recurrenceId );

  if ( inc ) {
    KDateTime nowUTC = KDateTime::currentUtcDateTime();
    inc->setLastModified( nowUTC );
    // we should probably update the revision number here,
    // or internally in the Event itself when certain things change.
    // need to verify with ical documentation.

    const Incidence::IncidenceType type = inc->type();
    const KDateTime dt = inc->dateTime( Incidence::RoleCalendarHashing );

    if ( dt.isValid() ) {
      d->mIncidencesForDate[type].insert( dt.date().toString(), inc );
    }

    notifyIncidenceChanged( inc );

    setModified( true );
  }
}

Event::List MemoryCalendar::rawEventsForDate( const QDate &date,
                                              const KDateTime::Spec &timespec,
                                              EventSortField sortField,
                                              SortDirection sortDirection ) const
{
  Event::List eventList;
  Event::Ptr ev;

  // Find the hash for the specified date
  const QString dateStr = date.toString();
  QMultiHash<QString, IncidenceBase::Ptr >::const_iterator it =
    d->mIncidencesForDate[Incidence::TypeEvent].constFind( dateStr );
  // Iterate over all non-recurring, single-day events that start on this date
  KDateTime::Spec ts = timespec.isValid() ? timespec : timeSpec();
  KDateTime kdt( date, ts );
  while ( it != d->mIncidencesForDate[Incidence::TypeEvent].constEnd() && it.key() == dateStr ) {
    ev = it.value().staticCast<Event>();
    KDateTime end( ev->dtEnd().toTimeSpec( ev->dtStart() ) );
    if ( ev->allDay() ) {
      end.setDateOnly( true );
    } else {
      end = end.addSecs( -1 );
    }
    if ( end >= kdt ) {
      eventList.append( ev );
    }
    ++it;
  }

  // Iterate over all events. Look for recurring events that occur on this date
  QHashIterator<QString, Incidence::Ptr>i( d->mIncidences[Incidence::TypeEvent] );
  while ( i.hasNext() ) {
    i.next();
    ev = i.value().staticCast<Event>();
    if ( ev->recurs() ) {
      if ( ev->isMultiDay() ) {
        int extraDays = ev->dtStart().date().daysTo( ev->dtEnd().date() );
        for ( int i = 0; i <= extraDays; ++i ) {
          if ( ev->recursOn( date.addDays( -i ), ts ) ) {
            eventList.append( ev );
            break;
          }
        }
      } else {
        if ( ev->recursOn( date, ts ) ) {
          eventList.append( ev );
        }
      }
    } else {
      if ( ev->isMultiDay() ) {
        if ( ev->dtStart().date() <= date && ev->dtEnd().date() >= date ) {
          eventList.append( ev );
        }
      }
    }
  }

  return Calendar::sortEvents( eventList, sortField, sortDirection );
}

Event::List MemoryCalendar::rawEvents( const QDate &start,
                                       const QDate &end,
                                       const KDateTime::Spec &timespec,
                                       bool inclusive ) const
{
  Event::List eventList;
  KDateTime::Spec ts = timespec.isValid() ? timespec : timeSpec();
  KDateTime st( start, ts );
  KDateTime nd( end, ts );
  KDateTime yesterStart = st.addDays( -1 );

  // Get non-recurring events
  QHashIterator<QString, Incidence::Ptr>i( d->mIncidences[Incidence::TypeEvent] );
  Event::Ptr event;
  while ( i.hasNext() ) {
    i.next();
    event = i.value().staticCast<Event>();
    KDateTime rStart = event->dtStart();
    if ( nd < rStart ) {
      continue;
    }
    if ( inclusive && rStart < st ) {
      continue;
    }

    if ( !event->recurs() ) { // non-recurring events
      KDateTime rEnd = event->dtEnd();
      if ( rEnd < st ) {
        continue;
      }
      if ( inclusive && nd < rEnd ) {
        continue;
      }
    } else { // recurring events
      switch( event->recurrence()->duration() ) {
      case -1: // infinite
        if ( inclusive ) {
          continue;
        }
        break;
      case 0: // end date given
      default: // count given
        KDateTime rEnd( event->recurrence()->endDate(), ts );
        if ( !rEnd.isValid() ) {
          continue;
        }
        if ( rEnd < st ) {
          continue;
        }
        if ( inclusive && nd < rEnd ) {
          continue;
        }
        break;
      } // switch(duration)
    } //if(recurs)

    eventList.append( event );
  }

  return eventList;
}

Event::List MemoryCalendar::rawEventsForDate( const KDateTime &kdt ) const
{
  return rawEventsForDate( kdt.date(), kdt.timeSpec() );
}

Event::List MemoryCalendar::rawEvents( EventSortField sortField,
                                       SortDirection sortDirection ) const
{
  Event::List eventList;
  QHashIterator<QString, Incidence::Ptr> i( d->mIncidences[Incidence::TypeEvent] );
  while ( i.hasNext() ) {
    i.next();
    eventList.append( i.value().staticCast<Event>() );
  }
  return Calendar::sortEvents( eventList, sortField, sortDirection );
}

Event::List MemoryCalendar::deletedEvents( EventSortField sortField,
                                           SortDirection sortDirection ) const
{
  Event::List eventList;
  QHashIterator<QString, Incidence::Ptr>i( d->mDeletedIncidences[Incidence::TypeEvent] );
  while ( i.hasNext() ) {
    i.next();
    eventList.append( i.value().staticCast<Event>() );
  }
  return Calendar::sortEvents( eventList, sortField, sortDirection );
}

Event::List MemoryCalendar::eventInstances( const Incidence::Ptr &event,
                                            EventSortField sortField,
                                            SortDirection sortDirection ) const
{
  Event::List list;

  QList<Incidence::Ptr> values = d->mIncidences[Incidence::TypeEvent].values( event->uid() );
  QList<Incidence::Ptr>::const_iterator it;
  for ( it = values.constBegin(); it != values.constEnd(); ++it ) {
    Event::Ptr ev = ( *it ).staticCast<Event>();
    if ( ev->hasRecurrenceId() ) {
      list.append( ev );
    }
  }
  return Calendar::sortEvents( list, sortField, sortDirection );
}

bool MemoryCalendar::addJournal( const Journal::Ptr &journal )
{
  return addIncidence( journal );
}

bool MemoryCalendar::deleteJournal( const Journal::Ptr &journal )
{
  return deleteIncidence( journal );
}

bool MemoryCalendar::deleteJournalInstances( const Journal::Ptr &journal )
{
  return deleteIncidenceInstances( journal );
}

void MemoryCalendar::deleteAllJournals()
{
  d->deleteAllIncidences( Incidence::TypeJournal );
}

Journal::Ptr MemoryCalendar::journal( const QString &uid,
                                      const KDateTime &recurrenceId ) const
{
  return d->incidence( uid, Incidence::TypeJournal, recurrenceId ).staticCast<Journal>();
}

Journal::Ptr MemoryCalendar::deletedJournal( const QString &uid,
                                             const KDateTime &recurrenceId ) const
{
  return d->deletedIncidence( uid, recurrenceId, Incidence::TypeJournal ).staticCast<Journal>();
}

Journal::List MemoryCalendar::rawJournals( JournalSortField sortField,
                                           SortDirection sortDirection ) const
{
  Journal::List journalList;
  QHashIterator<QString, Incidence::Ptr>i( d->mIncidences[Incidence::TypeJournal] );
  while ( i.hasNext() ) {
    i.next();
    journalList.append( i.value().staticCast<Journal>() );
  }
  return Calendar::sortJournals( journalList, sortField, sortDirection );
}

Journal::List MemoryCalendar::deletedJournals( JournalSortField sortField,
                                               SortDirection sortDirection ) const
{
  Journal::List journalList;
  QHashIterator<QString, Incidence::Ptr>i( d->mDeletedIncidences[Incidence::TypeJournal] );
  while ( i.hasNext() ) {
    i.next();
    journalList.append( i.value().staticCast<Journal>() );
  }
  return Calendar::sortJournals( journalList, sortField, sortDirection );
}

Journal::List MemoryCalendar::journalInstances( const Incidence::Ptr &journal,
                                                JournalSortField sortField,
                                                SortDirection sortDirection ) const
{
  Journal::List list;

  QList<Incidence::Ptr> values = d->mIncidences[Incidence::TypeJournal].values( journal->uid() );
  QList<Incidence::Ptr>::const_iterator it;
  for ( it = values.constBegin(); it != values.constEnd(); ++it ) {
    Journal::Ptr j = ( *it ).staticCast<Journal>();
    if ( j->hasRecurrenceId() ) {
      list.append( j );
    }
  }
  return Calendar::sortJournals( list, sortField, sortDirection );
}

Journal::List MemoryCalendar::rawJournalsForDate( const QDate &date ) const
{
  Journal::List journalList;
  Journal::Ptr j;

  QString dateStr = date.toString();
  QMultiHash<QString, IncidenceBase::Ptr >::const_iterator it =
    d->mIncidencesForDate[Incidence::TypeJournal].constFind( dateStr );

  while ( it != d->mIncidencesForDate[Incidence::TypeJournal].constEnd() && it.key() == dateStr ) {
    j = it.value().staticCast<Journal>();
    journalList.append( j );
    ++it;
  }
  return journalList;
}

void MemoryCalendar::virtual_hook( int id, void *data )
{
  Q_UNUSED( id );
  Q_UNUSED( data );
  Q_ASSERT( false );
}
