/***************************************************************************
                          kmymoneydatetbl.cpp  -  description
                             -------------------
    begin                : Thu Jul 3 2003
    copyright            : (C) 2000-2003 by Michael Edwardes
    email                : mte@users.sourceforge.net
                           Javier Campos Morales <javi_c@users.sourceforge.net>
                           Felix Rodriguez <frodriguez@users.sourceforge.net>
                           John C <thetacoturtle@users.sourceforge.net>
                           Thomas Baumgart <ipwizard@users.sourceforge.net>
                           Kevin Tambascio <ktambascio@users.sourceforge.net>
 ***************************************************************************/

 /****************************************************************************
 Contains code from the KDateTable class ala tdelibs-3.1.2.  Original license:

    This file is part of the KDE libraries
    Copyright (C) 1997 Tim D. Gilman (tdgilman@best.org)
              (C) 1998-2001 Mirko Boehm (mirko@kde.org)
    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., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.
*/

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

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

// ----------------------------------------------------------------------------
// QT Includes
#include <tqglobal.h>
#include <tqdatetime.h>
#include <tqstring.h>
#include <tqpen.h>
#include <tqpainter.h>
#include <tqdialog.h>

// ----------------------------------------------------------------------------
// KDE Includes
#include <tdeversion.h>
#include <kdatetbl.h> // Use the classes available for maximum re-use
#include <tdeglobal.h>
#include <tdeglobalsettings.h>
#include <tdeapplication.h>
#include <tdelocale.h>
#include <kdebug.h>
#include <knotifyclient.h>
#if KDE_IS_VERSION(3,2,0)
#include <kcalendarsystem.h>
#endif

// ----------------------------------------------------------------------------
// Project Includes
#include "kmymoneydatetbl.h"

kMyMoneyDateTbl::kMyMoneyDateTbl(TQWidget *parent, TQDate date_, const char* name, WFlags f)
  : TQGridView(parent, name, f)
{
  // call this first to make sure that member variables are initialized
  setType(MONTHLY);

  setFontSize(10);

  if(!date_.isValid())
  {
    kdDebug() << "kMyMoneyDateTbl ctor: WARNING: Given date is invalid, using current date." << endl;
    date_=TQDate::currentDate();
  }
  setFocusPolicy( TQ_StrongFocus );


  viewport()->setEraseColor(TDEGlobalSettings::baseColor());

  setDate(date_); // this initializes firstday, numdays, numDaysPrevMonth

  // So we can emit hoverDate
//  TQApplication::setGlobalMouseTracking(true);
  viewport()->setMouseTracking(true);
}

void
kMyMoneyDateTbl::paintCell(TQPainter *painter, int row, int col)
{
  TQRect rect;
  TQString text;
  TQPen pen;
  int w=cellWidth();
  int h=cellHeight();
  TQBrush brushBlue(TDEGlobalSettings::activeTitleColor());
  TQBrush brushLightblue(TDEGlobalSettings::baseColor());
  TQFont font=TDEGlobalSettings::generalFont();

  // -----
  font.setPointSize(fontsize);

#if TDE_VERSION < 310
  int firstWeekDay = TDEGlobal::locale()->weekStartsMonday() ? 1 : 0;
#else
  int firstWeekDay = TDEGlobal::locale()->weekStartDay();
#endif

  if (row==0)
  { // we are drawing the headline
    if (m_type == MONTHLY)
    {
      font.setBold(true);
      painter->setFont(font);
      bool normalday = true;
      TQString daystr;
      if ( col+firstWeekDay < 8 )
        daystr = WEEK_DAY_NAME(col+firstWeekDay, true);
      else
        daystr = WEEK_DAY_NAME(col+firstWeekDay-7, true);

      if ( daystr==i18n("Sunday", "Sun") || daystr==i18n("Saturday", "Sat") )
        normalday=false;

      if (!normalday)
      {
        painter->setPen(TDEGlobalSettings::baseColor());
        painter->setBrush(brushLightblue);
        painter->drawRect(0, 0, w, h);
        painter->setPen(TDEGlobalSettings::activeTitleColor());
      } else {
        painter->setPen(TDEGlobalSettings::activeTitleColor());
        painter->setBrush(brushBlue);
        painter->drawRect(0, 0, w, h);
        painter->setPen(TDEGlobalSettings::activeTextColor());
      }
      painter->drawText(0, 0, w, h-1, AlignCenter,
                        daystr, -1, &rect);
      painter->setPen(TDEGlobalSettings::textColor());
      painter->moveTo(0, h-1);
      painter->lineTo(w-1, h-1);

      if(rect.width()>maxCell.width())
        maxCell.setWidth(rect.width());

      if(rect.height()>maxCell.height())
        maxCell.setHeight(rect.height());
    }
    else if (m_type == WEEKLY)
    {
      painter->setPen(TDEGlobalSettings::activeTitleColor());
      painter->setBrush(brushBlue);
      painter->drawRect(0, 0, w, h);
      painter->setPen(TDEGlobalSettings::activeTextColor());

      int year=date.year();
      TQString headerText;
      // FIXME: Shouldn't that be i18n()'ed as well
      TQString weekStr = TQString::number(date.weekNumber(&year));
      TQString yearStr = TQString::number(year);
      headerText = i18n("Week %1 for year %2.")
        .arg(weekStr)
        .arg(yearStr);

      painter->drawText(0, 0, w, h-1, AlignCenter, headerText, -1, &rect);

      maxCell.setWidth(width());

      if(rect.height()>maxCell.height())
        maxCell.setHeight(rect.height());
    }
    else if (m_type == QUARTERLY)
    {
      int athird = width()/3;

      painter->setPen(TDEGlobalSettings::activeTitleColor());
      painter->setBrush(brushBlue);
      painter->setPen(/*TDEGlobalSettings::activeTextColor()*/black);

      if (col == 0)
      {
        painter->drawRect(0, 0, athird, h);
        painter->drawText(0, 0, athird, h-1, AlignCenter, "Month 1", -1, &rect);

        painter->drawRect(athird, 0, athird, h);
        painter->drawText(athird, 0, athird, h-1, AlignCenter, "Month 2", -1, &rect);

        painter->drawRect(athird*2, 0, athird, h);
        painter->drawText(athird*2, 0, athird, h-1, AlignCenter, "Month 3", -1, &rect);
      }
    }
  }
  else
  {
    int pos;

    TQDate drawDate(date);

    if (m_type == MONTHLY)
    {
      pos=7*(row-1)+col;
      if ( firstWeekDay < 4 )
        pos += firstWeekDay;
      else
        pos += firstWeekDay - 7;

      if (pos<firstday || (firstday+numdays<=pos))
      { // we are either
        //  painting a day of the previous month or
        //  painting a day of the following month

        if (pos<firstday)
        { // previous month
          drawDate = drawDate.addMonths(-1);
          text.setNum(numDaysPrevMonth+pos-firstday+1);
          drawDate.setYMD(drawDate.year(), drawDate.month(), text.toInt());
        } else { // following month
          drawDate = drawDate.addMonths(1);
          text.setNum(pos-firstday-numdays+1);
          drawDate.setYMD(drawDate.year(), drawDate.month(), text.toInt());
        }
      } else { // paint a day of the current month
        text.setNum(pos-firstday+1);
        drawDate.setYMD(drawDate.year(), drawDate.month(), text.toInt());
      }
    }
    else if (m_type == WEEKLY)
    {
      // TODO: Handle other start weekdays than Monday
      text = TQDate::shortDayName(row);
      text += " ";

      int dayOfWeek = date.dayOfWeek();
      int diff;

      if (row < dayOfWeek)
      {
        diff = -(dayOfWeek - row);
      }
      else
      {
        diff = row - dayOfWeek;
      }

      drawDate = date.addDays(diff);
    }
    else if (m_type == QUARTERLY)
    {
    }

    drawCellContents(painter, row, col, drawDate);
  }
}

void
kMyMoneyDateTbl::keyPressEvent( TQKeyEvent *e )
{
    if ( e->key() == TQt::Key_Prior ) {
        setDate(date.addMonths(-1));
        return;
    }
    if ( e->key() == TQt::Key_Next ) {
        setDate(date.addMonths(1));
        return;
    }

    if ( e->key() == TQt::Key_Up ) {
        if ( date.day() > 7 ) {
            setDate(date.addDays(-7));
            return;
        }
    }
    if ( e->key() == TQt::Key_Down ) {
        if ( date.day() <= date.daysInMonth()-7 ) {
            setDate(date.addDays(7));
            return;
        }
    }
    if ( e->key() == TQt::Key_Left ) {
        if ( date.day() > 1 ) {
            setDate(date.addDays(-1));
            return;
        }
    }
    if ( e->key() == TQt::Key_Right ) {
        if ( date.day() < date.daysInMonth() ) {
            setDate(date.addDays(1));
            return;
        }
    }

    if ( e->key() == TQt::Key_Minus ) {
        setDate(date.addDays(-1));
        return;
    }
    if ( e->key() == TQt::Key_Plus ) {
        setDate(date.addDays(1));
        return;
    }
    if ( e->key() == TQt::Key_N ) {
        setDate(TQDate::currentDate());
        return;
    }

    KNotifyClient::beep();
}

void
kMyMoneyDateTbl::viewportResizeEvent(TQResizeEvent * e)
{
  if (e)
    TQGridView::viewportResizeEvent(e);

  setCellWidth(viewport()->width()/m_colCount);
  setCellHeight(viewport()->height()/m_rowCount);
}

void
kMyMoneyDateTbl::setFontSize(int size)
{
  int count;
  TQFontMetrics metrics(fontMetrics());
  TQRect rect;

  // ----- store rectangles:
  fontsize=size;

  // ----- find largest day name:
  maxCell.setWidth(0);
  maxCell.setHeight(0);

  for(count=0; count<m_colCount; ++count)
  {
    rect=metrics.boundingRect(WEEK_DAY_NAME(count+1, true));
    maxCell.setWidth(TQMAX(maxCell.width(), rect.width()));
    maxCell.setHeight(TQMAX(maxCell.height(), rect.height()));
  }

  if (m_type == WEEKLY)
  {
    // Re-size to width
    maxCell.setWidth(width());
  }

  // ----- compare with a real wide number and add some space:
  rect=metrics.boundingRect(TQString::fromLatin1("88"));
  maxCell.setWidth(TQMAX(maxCell.width()+2, rect.width()));
  maxCell.setHeight(TQMAX(maxCell.height()+4, rect.height()));
}

void
kMyMoneyDateTbl::wheelEvent ( TQWheelEvent * e )
{
  setDate(date.addMonths( -(int)(e->delta()/120)) );
  e->accept();
}

void
kMyMoneyDateTbl::contentsMouseReleaseEvent(TQMouseEvent *e)
{
  if (e->type()!=TQEvent::MouseButtonRelease)
  { // the KDatePicker only reacts on mouse press events:
    return;
  }

  if(!isEnabled())
  {
    KNotifyClient::beep();
    return;
  }

#if TDE_VERSION < 310
  int dayoff = TDEGlobal::locale()->weekStartsMonday() ? 1 : 0;
#else
  int dayoff = TDEGlobal::locale()->weekStartDay();
#endif

  // -----
  int row, col, pos, temp;
  TQPoint mouseCoord;

  // -----
  mouseCoord = e->pos();
  row=rowAt(mouseCoord.y());
  col=columnAt(mouseCoord.x());
  if(row<1 || col<0)
  { // the user clicked on the frame of the table
    return;
  }

  if (m_type == MONTHLY)
  {
    // Rows and columns are zero indexed.  The (row - 1) below is to avoid counting
    // the row with the days of the week in the calculation.  We however want pos
    // to be "1 indexed", hence the "+ 1" at the end of the sum.
    pos = (7 * (row - 1)) + col + 1;

    // This gets pretty nasty below.  firstday is a locale independant index for
    // the first day of the week.  dayoff is the day of the week that the week
    // starts with for the selected locale.  Monday = 1 .. Sunday = 7
    // Strangely, in some cases dayoff is in fact set to 8, hence all of the
    // "dayoff % 7" sections below.

    if (pos + dayoff % 7 <= firstday)
    { // this day is in the previous month
      setDate(date.addDays(-1 * (date.day() + firstday - pos - dayoff % 7)));
      return;
    }

    if (firstday + numdays < pos + dayoff % 7)
    { // this date is in the next month
      setDate(date.addDays(pos - firstday - date.day() + dayoff % 7));
      return;
    }
    temp = firstday + date.day() - dayoff % 7 - 1;

    setDate(TQDate(date.year(), date.month(), pos - firstday + dayoff % 7));

    updateCell(temp/7+1, temp%7); // Update the previously selected cell
  }
  else if (m_type == WEEKLY)
  {
    int dayOfWeek = date.dayOfWeek();
    int diff;

    if (row < dayOfWeek)
    {
      diff = -(dayOfWeek - row);
    }
    else
    {
      diff = row - dayOfWeek;
    }

    setDate(date.addDays(diff));
    updateCell(dayOfWeek, 0);
  }
  else if (m_type == QUARTERLY)
  {
  }

  updateCell(row, col); // Update the selected cell

  emit(tableClicked());
}

bool
kMyMoneyDateTbl::setDate(const TQDate& date_)
{
  bool changed=false;
  TQDate temp;
  // -----
  if(!date_.isValid())
  {
    kdDebug() << "kMyMoneyDateTbl::setDate: refusing to set invalid date." << endl;
    return false;
  }

  if(date!=date_)
  {
    date=date_;
    changed=true;
  }

  temp.setYMD(date.year(), date.month(), 1);
  firstday=temp.dayOfWeek();

  if (firstday==1)
    firstday=8;

  numdays=date.daysInMonth();

  if (date.month()==1)
  { // set to december of previous year
    temp.setYMD(date.year()-1, 12, 1);
  } else { // set to previous month
    temp.setYMD(date.year(), date.month()-1, 1);
  }

  numDaysPrevMonth=temp.daysInMonth();

  if (changed)
  {
    repaintContents(false);
  }

  emit(dateChanged(date));
  return true;
}

const TQDate&
kMyMoneyDateTbl::getDate() const
{
  return date;
}

// what are those repaintContents() good for? (pfeiffer)
void kMyMoneyDateTbl::focusInEvent( TQFocusEvent *e )
{
//    repaintContents(false);
    TQGridView::focusInEvent( e );
}

void kMyMoneyDateTbl::focusOutEvent( TQFocusEvent *e )
{
//    repaintContents(false);
    TQGridView::focusOutEvent( e );
}

TQSize
kMyMoneyDateTbl::sizeHint() const
{
  if (maxCell.height()>0 && maxCell.width()>0)
  {
    return TQSize(maxCell.width()*numCols()+2*frameWidth(),
           (maxCell.height()+2)*numRows()+2*frameWidth());
  } else {
    kdDebug() << "kMyMoneyDateTbl::sizeHint: obscure failure - " << endl;
    return TQSize(-1, -1);
  }
}


void kMyMoneyDateTbl::setType(calendarType type)
{
  if (type == WEEKLY)
  {
    m_rowCount = 8;
    m_colCount = 1;
    m_type = WEEKLY;
  }
  else if (type == QUARTERLY)
  {
    m_rowCount = 7;
    m_colCount = 21;
    m_type = QUARTERLY;
  }
  else // default to monthly
  {
    m_rowCount = m_colCount = 7;
    m_type = MONTHLY;
  }

  setNumRows(m_rowCount);
  setNumCols(m_colCount);
  setHScrollBarMode(AlwaysOff);
  setVScrollBarMode(AlwaysOff);

  viewportResizeEvent(NULL);
}

void kMyMoneyDateTbl::contentsMouseMoveEvent(TQMouseEvent* e)
{
  int row, col, pos;
  TQPoint mouseCoord;

  mouseCoord = e->pos();
  row = rowAt(mouseCoord.y());
  col = columnAt(mouseCoord.x());
  if (row<1 || col<0)
  {
    return;
  }

#if TDE_VERSION < 310
  int firstWeekDay = TDEGlobal::locale()->weekStartsMonday() ? 1 : 0;
#else
  int firstWeekDay = TDEGlobal::locale()->weekStartDay();
#endif

  TQDate drawDate(date);
  TQString text;

  if (m_type == MONTHLY)
  {
    pos=7*(row-1)+col;
    if ( firstWeekDay < 4 )
      pos += firstWeekDay;
    else
      pos += firstWeekDay - 7;

    if (pos<firstday || (firstday+numdays<=pos))
    { // we are either
      //  painting a day of the previous month or
      //  painting a day of the following month

      if (pos<firstday)
      { // previous month
        drawDate = drawDate.addMonths(-1);
        text.setNum(numDaysPrevMonth+pos-firstday+1);
        drawDate.setYMD(drawDate.year(), drawDate.month(), text.toInt());
      } else { // following month
        drawDate = drawDate.addMonths(1);
        text.setNum(pos-firstday-numdays+1);
        drawDate.setYMD(drawDate.year(), drawDate.month(), text.toInt());
      }
    } else { // paint a day of the current month
      text.setNum(pos-firstday+1);
      drawDate.setYMD(drawDate.year(), drawDate.month(), text.toInt());
    }
  }
  else if (m_type == WEEKLY)
  {
    // TODO: Handle other start weekdays than Monday
    text = TQDate::shortDayName(row);
    text += " ";

    int dayOfWeek = date.dayOfWeek();
    int diff;

    if (row < dayOfWeek)
    {
      diff = -(dayOfWeek - row);
    }
    else
    {
      diff = row - dayOfWeek;
    }

    drawDate = date.addDays(diff);
  }
  else if (m_type == QUARTERLY)
  {
  }

  if (m_drawDateOrig != drawDate)
  {
    m_drawDateOrig = drawDate;
    emit hoverDate(drawDate);
  }

  TQGridView::contentsMouseMoveEvent(e);
}

#include "kmymoneydatetbl.moc"
