/*
    This file is part of KOrganizer.

    Copyright (c) 2001 Eitzenberger Thomas <thomas.eitzenberger@siemens.at>
    Copyright (c) 2003 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.
*/
#ifndef KODAYMATRIX_H
#define KODAYMATRIX_H

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

#include <tqframe.h>
#include <tqcolor.h>
#include <tqtooltip.h>
#include <tqmap.h>

class TQDragEnterEvent;
class TQDragMoveEvent;
class TQDragLeaveEvent;
class TQDropEvent;

class KODayMatrix;

namespace KCal {
class Incidence;
class Calendar;
}
using namespace KCal;


/**
 *  small helper class to dynamically show tooltips inside the day matrix.
 *  This class asks the day matrix object for a appropriate label which
 *  is in our special case the name of the holiday or null if this day is no holiday.
 */
class DynamicTip : public TQToolTip
{
  public:
    /**
     * Constructor that expects a KODayMatrix object as parent.
     *
     * @param parent the parent KODayMatrix control.
     */
    DynamicTip( TQWidget *parent );

  protected:
    /**
     * TQt's callback to ask the object to provide an approrpiate text for the
     * tooltip to be shown.
     *
     * @param pos coordinates of the mouse.
     */
    void maybeTip( const TQPoint &pos );

  private:
    /** the parent control this tooltip is designed for. */
    KODayMatrix *mMatrix;
};

/**
 *  Replacement for kdpdatebuton.cpp that used 42 widgets for the day matrix to be displayed.
 *  Cornelius thought this was a waste of memory and a lot of overhead.
 *  In addition the selection was not very intuitive so I decided to rewrite it using a TQFrame
 *  that draws the labels and allows for dragging selection while maintaining nearly full
 *  compatibility in behavior with its predecessor.
 *
 *  The following functionality has been changed:
 *
 *  o when shifting events in the agenda view from one day to another the day matrix is updated now
 *  o dragging an event to the matrix will MOVE not COPY the event to the new date.
 *  o no support for Ctrl+click to create groups of dates
 *    (This has not really been supported in the predecessor. It was not very intuitive nor was it
 *     user friendly.)
 *    This feature has been replaced with dragging a selection on the matrix. The matrix will
 *    automatically choose the appropriate selection (e.g. you are not any longer able to select
 *    two distinct groups of date selections as in the old class)
 *  o now that you can select more then a week it can happen that not all selected days are
 *    displayed in the matrix. However this is preferred to the alternative which would mean to
 *    adjust the selection and leave some days undisplayed while scrolling through the months
 *
 *  @short day matrix widget of the KDateNavigator
 *
 *  @author Eitzenberger Thomas
 */
class KODayMatrix: public TQFrame, public KCal::Calendar::Observer
{
    TQ_OBJECT
  
  public:
    /** constructor to create a day matrix widget.
     *
     *  @param parent widget that is the parent of the day matrix.
     *  Normally this should be a KDateNavigator
     *  @param name name of the widget
     */
    KODayMatrix( TQWidget *parent, const char *name );

    /** destructor that deallocates all dynamically allocated private members.
     */
    ~KODayMatrix();

    /** returns the first and last date of the 6*7 matrix that displays @p month
     * @param month The month we want to get matrix boundaries
     */
    static TQPair<TQDate,TQDate> matrixLimits( const TQDate &month );

    /**
      Associate a calendar with this day matrix. If there is a calendar, the day
      matrix will accept drops and days with events will be highlighted.
    */
    void setCalendar( Calendar * );

    /** updates the day matrix to start with the given date. Does all the necessary
     *  checks for holidays or events on a day and stores them for display later on.
     *  Does NOT update the view visually. Call repaint() for this.
     *
     *  @param actdate recalculates the day matrix to show NUMDAYS starting from this
     *                 date.
     */
    void updateView( const TQDate &actdate );

    /**
      Update event states of dates. Depending of the preferences days with
      events are highlighted in some way.
    */
    void updateEvents();

    /** returns the TQDate object associated with day indexed by the
     *  supplied offset.
     */
    const TQDate& getDate( int offset );

    /** returns the official name of this holy day or 0 if there is no label
     *  for this day.
     */
    TQString getHolidayLabel( int offset );

    /** adds all actual selected days from mSelStart to mSelEnd to the supplied
     *  DateList.
     */
    void addSelectedDaysTo( DateList & );

    /** sets the actual to be displayed selection in the day matrix starting from
     *  start and ending with end. Theview must be manually updated by calling
     *  repaint. (?)
     */
    void setSelectedDaysFrom( const TQDate &start, const TQDate &end );

    /**
      Clear all selections.
    */
    void clearSelection();

    /** Is today visible in the view? Keep this in sync with
    * the values today (below) can take.
    */
    bool isTodayVisible() const { return mToday >= 0; }

    /** If today is visible, then we can find out if today is
    * near the beginning or the end of the month.
    * This is dependent on today remaining the index
    * in the array of visible dates and going from
    * top left (0) to bottom right (41).
    */
    bool isBeginningOfMonth() const { return mToday <= 8; }
    bool isEndOfMonth() const { return mToday >= 27; }

    /* reimplmented from KCal::Calendar::Observer */
    void calendarIncidenceAdded( Incidence *incidence );
    void calendarIncidenceChanged( Incidence *incidence );
    void calendarIncidenceDeleted( Incidence *incidence );

    void setUpdateNeeded();

  public slots:
    /** Recalculates all the flags of the days in the matrix like holidays or events
     *  on a day (Actually calls above method with the actual startdate).
     */
    void updateView();

    /**
    * Calculate which square in the matrix should be
    * hilighted to indicate it's today.
    */
    void recalculateToday();

    /**
     * Handle resource changes.
     */
    void resourcesChanged();

  signals:
    /** emitted if the user selects a block of days with the mouse by dragging a rectangle
     *  inside the matrix
     *
     *  @param daylist list of days that have been selected by the user
     */
    void selected( const KCal::DateList &daylist );

    /** emitted if the user has dropped an incidence (event or todo) inside the matrix
     *
     *  @param incidence the dropped calendar incidence
     *  @param dt TQDate that has been selected
     */
    void incidenceDropped( Incidence *incidence, const TQDate &dt );
    /** emitted if the user has dropped an event inside the matrix and chose to move it instead of copy
     *
     *  @param oldincidence the new calendar incidence
     *  @param dt TQDate that has been selected
     */
    void incidenceDroppedMove( Incidence *oldincidence, const TQDate &dt );

  protected:
    void paintEvent( TQPaintEvent *ev );

    void mousePressEvent( TQMouseEvent *e );

    void mouseReleaseEvent( TQMouseEvent *e );

    void mouseMoveEvent( TQMouseEvent *e );

    void dragEnterEvent( TQDragEnterEvent * );

    void dragMoveEvent( TQDragMoveEvent * );

    void dragLeaveEvent( TQDragLeaveEvent * );

    void dropEvent( TQDropEvent * );

    void resizeEvent( TQResizeEvent * );

  private:
    /** returns the index of the day located at the matrix's widget (x,y) position.
     *
     *  @param x horizontal coordinate
     *  @param y vertical coordinate
     */
    int getDayIndexFrom( int x, int y );

    /** calculates a "shaded" color from the supplied color object.
     *  (Copied from Cornelius's kdpdatebutton.cpp)
     *
     *  @param color source based on which a shaded color should be calculated.
     */
    TQColor getShadedColor( const TQColor &color );

    /** number of days to be displayed. For now there is no support for any other number then 42.
        so change it at your own risk :o) */
    static const int NUMDAYS;

    /** calendar instance to be queried for holidays, events, ... */
    Calendar  *mCalendar;

    /** starting date of the matrix */
    TQDate     mStartDate;

    /** array of day labels to optimeize drawing performance. */
    TQString   *mDayLabels;

    /** array of days displayed to reduce memory consumption by
        subsequently calling TQDate::addDays(). */
    TQDate     *mDays;

    /** array of storing the number of events on a given day.
      *  used for drawing a bold font if there is at least one event on that day.
      */
    int      *mEvents;

    /** stores holiday names of the days shown in the matrix. */
    TQMap<int,TQString>  mHolidays;

    /** index of today or -1 if today is not visible in the matrix. */
    int       mToday;

    /** index of day where dragged selection was initiated.
        used to detect "negative" timely selections */
    int       mSelInit;

    /** if mSelStart has this value it indicates that there is no
        actual selection in the matrix. */
    static const int NOSELECTION;

    /** index of first selected day. */
    int       mSelStart;

    /** index of last selected day. */
    int       mSelEnd;

    /** dynamic tooltip to handle mouse dependent tips for each day in the matrix. */
    DynamicTip* mToolTip;

    /** default width of the frame drawn around today if it is visible in the matrix. */
    int       mTodayMarginWidth;

    /** stores actual size of each day in the widget so that I don't need to ask this data
     *  on every repaint.
     */
    TQRect     mDaySize;

    /**
     * Indicate pending calendar changes.
     */
    bool mPendingChanges;
};

#endif