/*
    This file is part of KOrganizer.

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

    This program 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 2 of the License, or
    (at your option) any later version.

    This program 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 this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

    As a special exception, permission is given to link this program
    with any edition of TQt, and distribute the resulting executable,
    without including the source code for TQt in the source distribution.
*/

#include <tqlistview.h>
#include <tqlayout.h>
#include <tqpopupmenu.h>
#include <tqcursor.h>
#include <tqstyle.h>

#include <klocale.h>
#include <kdebug.h>
#include <kiconloader.h>
#include <kglobal.h>

#include <libkcal/calendar.h>
#include <libkcal/incidenceformatter.h>

#include "koglobals.h"
#include "koprefs.h"
#include "koincidencetooltip.h"
#include "koeventpopupmenu.h"

#include "kolistview.h"
#include "kolistview.moc"

enum {
  Summary_Column = 0,
  Reminder_Column,
  Recurs_Column,
  StartDateTime_Column,
  EndDateTime_Column,
  Categories_Column
};


KOListViewToolTip::KOListViewToolTip( TQWidget* parent,
                                      Calendar *calendar,
                                      KListView *lv )
  :TQToolTip( parent ), mCalendar( calendar )
{
  eventlist = lv;
}

void KOListViewToolTip::maybeTip( const TQPoint &pos )
{
  TQRect r;
  TQListViewItem *it = eventlist->itemAt( pos );
  KOListViewItem *i = static_cast<KOListViewItem*>( it );

  if ( i && KOPrefs::instance()->mEnableToolTips ) {
    /* Calculate the rectangle. */
    r = eventlist->tqitemRect( it );
    /* Show the tip */
    TQString tipText( IncidenceFormatter::toolTipStr( mCalendar, i->data() ) );
    if ( !tipText.isEmpty() ) {
      tip( r, tipText );
    }
  }
}

/**
  This class provides the initialization of a KOListViewItem for calendar
  components using the Incidence::Visitor.
*/
class KOListView::ListItemVisitor : public IncidenceBase::Visitor
{
  public:
    ListItemVisitor( KOListViewItem *item ) : mItem( item ) {}
    ~ListItemVisitor() {}

    bool visit( Event * );
    bool visit( Todo * );
    bool visit( Journal * );

  private:
    KOListViewItem *mItem;
};

bool KOListView::ListItemVisitor::visit( Event *e )
{
  mItem->setText( Summary_Column, e->summary() );
  if ( e->isAlarmEnabled() ) {
    static const TQPixmap alarmPxmp = KOGlobals::self()->smallIcon( "bell" );
    mItem->setPixmap( Reminder_Column, alarmPxmp );
    mItem->setSortKey( Reminder_Column, "1" );
  } else {
    mItem->setSortKey( Reminder_Column, "0" );
  }

  if ( e->doesRecur() ) {
    static const TQPixmap recurPxmp = KOGlobals::self()->smallIcon( "recur" );
    mItem->setPixmap( Recurs_Column, recurPxmp );
    mItem->setSortKey( Recurs_Column, "1" );
  } else {
    mItem->setSortKey( Recurs_Column, "0" );
  }

  TQPixmap eventPxmp;
  if ( e->customProperty( "KABC", "BIRTHDAY" ) == "YES" ) {
    if ( e->customProperty( "KABC", "ANNIVERSARY" ) == "YES" ) {
      eventPxmp = KOGlobals::self()->smallIcon( "calendaranniversary" );
    } else {
      eventPxmp = KOGlobals::self()->smallIcon( "calendarbirthday" );
    }
  } else {
    eventPxmp = KOGlobals::self()->smallIcon( "appointment" );
  }

  mItem->setPixmap( Summary_Column, eventPxmp );

  TQString startDateTime;
  TQString endDateTime;

  mItem->setText( StartDateTime_Column, IncidenceFormatter::dateTimeToString( e->dtStart(), e->doesFloat() ) );
  mItem->setSortKey( StartDateTime_Column, e->dtStart().toString( Qt::ISODate ) );
  mItem->setText( EndDateTime_Column, IncidenceFormatter::dateTimeToString( e->dtEnd(), e->doesFloat() ) );
  mItem->setSortKey( EndDateTime_Column, e->dtEnd().toString( Qt::ISODate ) );
  mItem->setText( Categories_Column, e->categoriesStr() );

  return true;
}

bool KOListView::ListItemVisitor::visit( Todo *t )
{
  static const TQPixmap todoPxmp = KOGlobals::self()->smallIcon( "todo" );
  static const TQPixmap todoDonePxmp = KOGlobals::self()->smallIcon( "checkedbox" );
  mItem->setPixmap(Summary_Column, t->isCompleted() ? todoDonePxmp : todoPxmp );
  mItem->setText(Summary_Column, t->summary());
  if ( t->isAlarmEnabled() ) {
    static const TQPixmap alarmPxmp = KOGlobals::self()->smallIcon( "bell" );
    mItem->setPixmap( Reminder_Column, alarmPxmp );
    mItem->setSortKey( Reminder_Column, "1" );
  } else {
    mItem->setSortKey( Reminder_Column, "0" );
  }

  if ( t->doesRecur() ) {
    static const TQPixmap recurPxmp = KOGlobals::self()->smallIcon( "recur" );
    mItem->setPixmap( Recurs_Column, recurPxmp );
    mItem->setSortKey( Recurs_Column, "1" );
  } else {
    mItem->setSortKey( Recurs_Column, "0" );
  }

  if ( t->hasStartDate() ) {
    mItem->setText( StartDateTime_Column, IncidenceFormatter::dateTimeToString( t->dtStart(), t->doesFloat() ) );
    mItem->setSortKey( StartDateTime_Column, t->dtStart().toString( Qt::ISODate ) );
  } else {
    mItem->setText( StartDateTime_Column, "---" );
  }

  if ( t->hasDueDate() ) {
    mItem->setText( EndDateTime_Column, IncidenceFormatter::dateTimeToString( t->dtDue(), t->doesFloat() ) );
    mItem->setSortKey( EndDateTime_Column, t->dtDue().toString( Qt::ISODate ) );
  } else {
    mItem->setText( EndDateTime_Column, "---" );
  }
  mItem->setText( Categories_Column, t->categoriesStr() );

  return true;
}

bool KOListView::ListItemVisitor::visit( Journal *j )
{
  static const TQPixmap jornalPxmp = KOGlobals::self()->smallIcon( "journal" );
  mItem->setPixmap( Summary_Column, jornalPxmp );
  // Just use the first line
  mItem->setText( Summary_Column, j->description().section( "\n", 0, 0 ) );
  mItem->setText( StartDateTime_Column, IncidenceFormatter::dateTimeToString( j->dtStart(), j->doesFloat() ) );
  mItem->setSortKey( StartDateTime_Column, j->dtStart().toString( Qt::ISODate ) );

  return true;
}

KOListView::KOListView( Calendar *calendar,
                        TQWidget *parent,
                        const char *name,
                        bool nonInteractive )
  : KOEventView( calendar, parent, name )
{
  mActiveItem = 0;
  mIsNonInteractive = nonInteractive;

  mListView = new KListView( this );
  mListView->addColumn( i18n("Summary") );
  mListView->addColumn( i18n("Reminder") ); // alarm set?
  mListView->setColumnAlignment( Reminder_Column, AlignHCenter );

  mListView->addColumn( i18n("Recurs") ); // recurs?
  mListView->setColumnAlignment( Recurs_Column, AlignHCenter );

  mListView->addColumn( i18n("Start Date/Time") );
  mListView->setColumnAlignment( StartDateTime_Column, AlignHCenter );

  mListView->addColumn( i18n("End Date/Time") );
  mListView->setColumnAlignment( EndDateTime_Column, AlignHCenter );

  mListView->addColumn( i18n("Categories") );

  TQBoxLayout *layoutTop = new TQVBoxLayout( this );
  layoutTop->addWidget( mListView );

  mPopupMenu = eventPopup();
/*
  mPopupMenu->insertSeparator();
  mPopupMenu->insertItem(i18n("Show Dates"), this,
                      TQT_SLOT(showDates()));
  mPopupMenu->insertItem(i18n("Hide Dates"), this,
                      TQT_SLOT(hideDates()));
*/

  connect( mListView, TQT_SIGNAL( doubleClicked( TQListViewItem * ) ),
                    TQT_SLOT( defaultItemAction( TQListViewItem * ) ) );
  connect( mListView, TQT_SIGNAL( returnPressed( TQListViewItem * ) ),
                    TQT_SLOT( defaultItemAction( TQListViewItem * ) ) );
  connect( mListView, TQT_SIGNAL( rightButtonClicked ( TQListViewItem *,
                                                            const TQPoint &,
                                                            int ) ),
                    TQT_SLOT( popupMenu( TQListViewItem *, const TQPoint &, int ) ) );
  connect( mListView, TQT_SIGNAL( selectionChanged() ),
                    TQT_SLOT( processSelectionChange() ) );

//  setMinimumSize(100,100);
  mListView->restoreLayout( KOGlobals::self()->config(), "KOListView Layout" );

  new KOListViewToolTip( mListView->viewport(), calendar, mListView );

  mSelectedDates.append( TQDate::tqcurrentDate() );
}

KOListView::~KOListView()
{
  delete mPopupMenu;
}

int KOListView::maxDatesHint()
{
  return 0;
}

int KOListView::tqcurrentDateCount()
{
  return mSelectedDates.count();
}

Incidence::List KOListView::selectedIncidences()
{
  Incidence::List eventList;

  TQListViewItem *item = mListView->selectedItem();
  if ( item ) {
    eventList.append( static_cast<KOListViewItem *>( item )->data() );
  }

  return eventList;
}

DateList KOListView::selectedIncidenceDates()
{
  return mSelectedDates;
}

void KOListView::showDates( bool show )
{
  // Shouldn't we set it to a value greater 0? When showDates is called with
  // show == true at first, then the columnwidths are set to zero.
  static int oldColWidth1 = 0;
  static int oldColWidth3 = 0;

  if ( !show ) {
    oldColWidth1 = mListView->columnWidth( 1 );
    oldColWidth3 = mListView->columnWidth( 3 );
    mListView->setColumnWidth( 1, 0 );
    mListView->setColumnWidth( 3, 0 );
  } else {
    mListView->setColumnWidth( 1, oldColWidth1 );
    mListView->setColumnWidth( 3, oldColWidth3 );
  }
  mListView->tqrepaint();
}

void KOListView::showDates()
{
  showDates( true );
}

void KOListView::hideDates()
{
  showDates( false );
}

void KOListView::updateView()
{
  kdDebug(5850) << "KOListView::updateView() does nothing" << endl;
}

void KOListView::showDates( const TQDate &start, const TQDate &end )
{
  clear();

  TQDate date = start;
  while( date <= end ) {
    addIncidences( calendar()->incidences( date ), date );
    mSelectedDates.append( date );
    date = date.addDays( 1 );
  }

  emit incidenceSelected( 0, TQDate() );
}

void KOListView::showAll()
{
  Incidence::List incidenceList = calendar()->incidences();

  Incidence::List::ConstIterator it;
  for( it = incidenceList.begin(); it != incidenceList.end(); ++it ) {
    // we don't need the date, using showAll in non interactive mode for now
    addIncidence( *it, TQDate() );
  }
}

void KOListView::addIncidences( const Incidence::List &incidenceList, const TQDate &date )
{
  Incidence::List::ConstIterator it;
  for( it = incidenceList.begin(); it != incidenceList.end(); ++it ) {
    addIncidence( *it, date );
  }
}

void KOListView::addIncidence( Incidence *incidence, const TQDate &date )
{
  if ( mUidDict.find( incidence->uid() ) ) {
    return;
  }

  mDateList[incidence->uid()] = date;
  mUidDict.insert( incidence->uid(), incidence );

  KOListViewItem *item = new KOListViewItem( incidence, mListView );
  ListItemVisitor v( item );
  if (incidence->accept( v ) ) {
    return;
  } else {
    delete item;
  }
}

void KOListView::showIncidences( const Incidence::List &incidenceList, const TQDate &date )
{
  clear();

  addIncidences( incidenceList, date );

  // After new creation of list view no events are selected.
  emit incidenceSelected( 0, date );
}

void KOListView::changeIncidenceDisplay( Incidence *incidence, int action )
{
  KOListViewItem *item;
  TQDate f = mSelectedDates.first();
  TQDate l = mSelectedDates.last();

  TQDate date;
  if ( incidence->type() == "Todo" ) {
    date = static_cast<Todo *>( incidence )->dtDue().date();
  } else {
    date = incidence->dtStart().date();
  }

  switch( action ) {
    case KOGlobals::INCIDENCEADDED: {
      if ( date >= f && date <= l )
        addIncidence( incidence, date );
      break;
    }
    case KOGlobals::INCIDENCEEDITED: {
      item = getItemForIncidence( incidence );
      if ( item ) {
        delete item;
        mUidDict.remove( incidence->uid() );
        mDateList.remove( incidence->uid() );
      }
      if ( date >= f && date <= l ) {
        addIncidence( incidence, date );
      }
    }
    break;
    case KOGlobals::INCIDENCEDELETED: {
      item = getItemForIncidence( incidence );
      if ( item ) {
        delete item;
      }
      break;
    }
    default:
      kdDebug(5850) << "KOListView::changeIncidenceDisplay(): Illegal action " << action << endl;
  }
}

KOListViewItem *KOListView::getItemForIncidence( Incidence *incidence )
{
  KOListViewItem *item = static_cast<KOListViewItem *>( mListView->firstChild() );
  while ( item ) {
//    kdDebug(5850) << "Item " << item->text(0) << " found" << endl;
    if ( item->data() == incidence ) {
      return item;
    }
    item = static_cast<KOListViewItem *>( item->nextSibling() );
  }
  return 0;
}

void KOListView::defaultItemAction( TQListViewItem *i )
{
  if ( !mIsNonInteractive ) {
    KOListViewItem *item = static_cast<KOListViewItem *>( i );
    if ( item ) {
      defaultAction( item->data() );
    }
  }
}

void KOListView::popupMenu( TQListViewItem *item,const TQPoint &, int )
{
  if ( !mIsNonInteractive ) {
    mActiveItem = static_cast<KOListViewItem *>( item );
    if ( mActiveItem ) {
      Incidence *incidence = mActiveItem->data();
      // FIXME: For recurring incidences we don't know the date of this
      // occurrence, there's no reference to it at all!
      mPopupMenu->showIncidencePopup( calendar(), incidence, TQDate() );
    } else {
      showNewEventPopup();
    }
  }
}

void KOListView::readSettings( KConfig *config )
{
  mListView->restoreLayout( config,"KOListView Layout" );
}

void KOListView::writeSettings( KConfig *config )
{
  mListView->saveLayout( config, "KOListView Layout" );
}

void KOListView::processSelectionChange()
{
  if ( !mIsNonInteractive ) {
    kdDebug(5850) << "KOListView::processSelectionChange()" << endl;

    KOListViewItem *item =
      static_cast<KOListViewItem *>( mListView->selectedItem() );

    if ( !item ) {
      emit incidenceSelected( 0, TQDate() );
    } else {
      Incidence *incidence = static_cast<Incidence *>( item->data() );
      emit incidenceSelected( incidence, mDateList[incidence->uid()] );
    }
  }
}

void KOListView::clearSelection()
{
  mListView->selectAll( false );
}

void KOListView::clear()
{
  mSelectedDates.clear();
  mListView->clear();
  mUidDict.clear();
  mDateList.clear();
}

TQSize KOListView::tqsizeHint() const
{
  const TQSize s = KOEventView::tqsizeHint();
  return TQSize( s.width() + tqstyle().tqpixelMetric( TQStyle::PM_ScrollBarExtent ) + 1,
                s.height() );
}