/*
 *   This file only:
 *     Copyright (C) 2003  Mark Bucciarelli <mark@hubcapconsutling.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.
 *
 */

// #include <iostream>

#include <tqdatetime.h>
#include <tqpaintdevicemetrics.h>
#include <tqpainter.h>
#include <tqmap.h>

#include <tdeglobal.h>
#include <kdebug.h>
#include <tdelocale.h>            // i18n
#include <event.h>

#include "karmutility.h"        // formatTime()
#include "timekard.h"
#include "task.h"
#include "taskview.h"
#include <assert.h>

const int taskWidth = 40;
const int timeWidth = 6;
const int totalTimeWidth = 7;
const int reportWidth = taskWidth + timeWidth;

const TQString cr = TQString::fromLatin1("\n");

TQString TimeKard::totalsAsText(TaskView* taskview, bool justThisTask, WhichTime which)
// Print the total Times as text. If justThisTask, use activeTask, else, all tasks
{
  kdDebug(5970) << "Entering TimeKard::totalsAsText" << endl;
  TQString retval;
  TQString line;
  TQString buf;
  long sum;

  line.fill('-', reportWidth);
  line += cr;

  // header
  retval += i18n("Task Totals") + cr;
  retval += TDEGlobal::locale()->formatDateTime(TQDateTime::currentDateTime());
  retval += cr + cr;
  retval += TQString(TQString::fromLatin1("%1    %2"))
    .arg(i18n("Time"), timeWidth)
    .arg(i18n("Task"));
  retval += cr;
  retval += line;

  // tasks
  if (taskview->current_item())
  {
    if (justThisTask)
    {
      // a task's total time includes the sum of all subtask times
      sum = which == TotalTime ? taskview->current_item()->totalTime() : taskview->current_item()->sessionTime();
      printTask(taskview->current_item(), retval, 0, which);
    }
    else
    {
      sum = 0;
      for (Task* task= taskview->item_at_index(0); task;
          task= task->nextSibling())
      {
        kdDebug(5970) << "Copying task " << task->name() << endl;
        int time = which == TotalTime ? task->totalTime() : task->totalSessionTime();
        sum += time;
        if ( time || task->firstChild() )
                printTask(task, retval, 0, which);
      }
    }

    // total
    buf.fill('-', reportWidth);
    retval += TQString(TQString::fromLatin1("%1")).arg(buf, timeWidth) + cr;
    retval += TQString(TQString::fromLatin1("%1 %2"))
      .arg(formatTime(sum),timeWidth)
      .arg(i18n("Total"));
  }
  else
    retval += i18n("No tasks.");

  return retval;
}

// Print out "<indent for level> <task total> <task>", for task and subtasks. Used by totalsAsText.
void TimeKard::printTask(Task *task, TQString &s, int level, WhichTime which)
{
  TQString buf;

  s += buf.fill(' ', level);
  s += TQString(TQString::fromLatin1("%1    %2"))
    .arg(formatTime(which == TotalTime?task->totalTime():task->totalSessionTime()), timeWidth)
    .arg(task->name());
  s += cr;

  for (Task* subTask = task->firstChild();
      subTask;
      subTask = subTask->nextSibling())
  {
    int time = which == TotalTime ? subTask->totalTime() : subTask->totalSessionTime();
    if (time)
      printTask(subTask, s, level+1, which);
  }
}

void TimeKard::printTaskHistory(const Task *task,
    const TQMap<TQString,long>& taskdaytotals,
    TQMap<TQString,long>& daytotals,
    const TQDate& from,
    const TQDate& to,
    const int level, TQString& s, bool totalsOnly)
{
  long sectionsum = 0;
  for ( TQDate day = from; day <= to; day = day.addDays(1) )
  {
    TQString daykey = day.toString(TQString::fromLatin1("yyyyMMdd"));
    TQString daytaskkey = TQString::fromLatin1("%1_%2")
                         .arg(daykey)
                         .arg(task->uid());

    if (taskdaytotals.contains(daytaskkey))
    {
      if ( !totalsOnly )
      {
        s += TQString::fromLatin1("%1")
             .arg(formatTime(taskdaytotals[daytaskkey]/60), timeWidth);
      }
      sectionsum += taskdaytotals[daytaskkey];  // in seconds

      if (daytotals.contains(daykey))
        daytotals.replace(daykey, daytotals[daykey] + taskdaytotals[daytaskkey]);
      else
        daytotals.insert(daykey, taskdaytotals[daytaskkey]);
    }
    else if ( !totalsOnly )
    {
      TQString buf;
      buf.fill(' ', timeWidth);
      s += buf;
    }
  }

  // Total for task this section (e.g. week)
  s += TQString::fromLatin1("%1").arg(formatTime(sectionsum/60), totalTimeWidth);

  // Task name
  TQString buf;
  s += buf.fill(' ', level + 1);
  s += TQString::fromLatin1("%1").arg(task->name());
  s += cr;

  for (Task* subTask = task->firstChild();
      subTask;
      subTask = subTask->nextSibling())
  {
    // recursive
    printTaskHistory(subTask, taskdaytotals, daytotals, from, to, level+1, s, totalsOnly);
  }
}

TQString TimeKard::sectionHistoryAsText(
  TaskView* taskview,
  const TQDate& sectionFrom, const TQDate& sectionTo,
  const TQDate& from, const TQDate& to,
  const TQString& name,
  bool justThisTask, bool totalsOnly)
{

  const int sectionReportWidth = taskWidth + ( totalsOnly ? 0 : sectionFrom.daysTo(sectionTo) * timeWidth ) + totalTimeWidth;
  assert( sectionReportWidth > 0 );
  TQString line;
  line.fill('-', sectionReportWidth);
  line += cr;

  TQValueList<HistoryEvent> events;
  if ( sectionFrom < from && sectionTo > to)
  {
    events = taskview->getHistory(from, to);
  }
  else if ( sectionFrom < from )
  {
    events = taskview->getHistory(from, sectionTo);
  }
  else if ( sectionTo > to)
  {
    events = taskview->getHistory(sectionFrom, to);
  }
  else
  {
    events = taskview->getHistory(sectionFrom, sectionTo);
  }

  TQMap<TQString, long> taskdaytotals;
  TQMap<TQString, long> daytotals;

  // Build lookup dictionary used to output data in table cells.  keys are
  // in this format: YYYYMMDD_NNNNNN, where Y = year, M = month, d = day and
  // NNNNN = the VTODO uid.  The value is the total seconds logged against
  // that task on that day.  Note the UID is the todo id, not the event id,
  // so times are accumulated for each task.
  for (TQValueList<HistoryEvent>::iterator event = events.begin(); event != events.end(); ++event)
  {
    TQString daykey = (*event).start().date().toString(TQString::fromLatin1("yyyyMMdd"));
    TQString daytaskkey = TQString::fromLatin1("%1_%2")
                         .arg(daykey)
                         .arg((*event).todoUid());

    if (taskdaytotals.contains(daytaskkey))
      taskdaytotals.replace(daytaskkey,
                            taskdaytotals[daytaskkey] + (*event).duration());
    else
      taskdaytotals.insert(daytaskkey, (*event).duration());
  }

  TQString retval;
  // section name (e.g. week name)
  retval += cr + cr;
  TQString buf;
  if ( name.length() < (unsigned int)sectionReportWidth )
    buf.fill(' ', int((sectionReportWidth - name.length()) / 2));
  retval += buf + name + cr;

  if ( !totalsOnly )
  {
    // day headings
    for (TQDate day = sectionFrom; day <= sectionTo; day = day.addDays(1))
    {
      retval += TQString::fromLatin1("%1").arg(day.day(), timeWidth);
    }
    retval += cr;
    retval += line;
  }

  // the tasks
  if (events.empty())
  {
    retval += "  ";
    retval += i18n("No hours logged.");
  }
  else
  {
    if (justThisTask)
    {
      printTaskHistory(taskview->current_item(), taskdaytotals, daytotals,
                       sectionFrom, sectionTo, 0, retval, totalsOnly);
    }
    else
    {
      for (Task* task= taskview->current_item(); task;
           task= task->nextSibling())
      {
        printTaskHistory(task, taskdaytotals, daytotals,
                         sectionFrom, sectionTo, 0, retval, totalsOnly);
      }
    }
    retval += line;

    // per-day totals at the bottom of the section
    long sum = 0;
    for (TQDate day = sectionFrom; day <= sectionTo; day = day.addDays(1))
    {
      TQString daykey = day.toString(TQString::fromLatin1("yyyyMMdd"));

      if (daytotals.contains(daykey))
      {
        if ( !totalsOnly )
        {
          retval += TQString::fromLatin1("%1")
                    .arg(formatTime(daytotals[daykey]/60), timeWidth);
        }
        sum += daytotals[daykey];  // in seconds
      }
      else if ( !totalsOnly )
      {
        buf.fill(' ', timeWidth);
        retval += buf;
      }
    }

    retval += TQString::fromLatin1("%1 %2")
              .arg(formatTime(sum/60), totalTimeWidth)
              .arg(i18n("Total"));
  }
  return retval;
}

TQString TimeKard::historyAsText(TaskView* taskview, const TQDate& from,
    const TQDate& to, bool justThisTask, bool perWeek, bool totalsOnly)
{
  // header
  TQString retval;
  retval += totalsOnly ? i18n("Task Totals") : i18n("Task History");
  retval += cr;
  retval += i18n("From %1 to %2")
    .arg(TDEGlobal::locale()->formatDate(from))
    .arg(TDEGlobal::locale()->formatDate(to));
  retval += cr;
  retval += i18n("Printed on: %1")
    .arg(TDEGlobal::locale()->formatDateTime(TQDateTime::currentDateTime()));

  if ( perWeek )
  {
    // output one time card table for each week in the date range
    TQValueList<Week> weeks = Week::weeksFromDateRange(from, to);
    for (TQValueList<Week>::iterator week = weeks.begin(); week != weeks.end(); ++week)
    {
      retval += sectionHistoryAsText( taskview, (*week).start(), (*week).end(), from, to, (*week).name(), justThisTask, totalsOnly );
    }
  } else
  {
    retval += sectionHistoryAsText( taskview, from, to, from, to, "", justThisTask, totalsOnly );
  }
  return retval;
}

Week::Week() {}

Week::Week(TQDate from)
{
  _start = from;
}

TQDate Week::start() const
{
  return _start;
}

TQDate Week::end() const
{
  return _start.addDays(6);
}

TQString Week::name() const
{
  return i18n("Week of %1").arg(TDEGlobal::locale()->formatDate(start()));
}

TQValueList<Week> Week::weeksFromDateRange(const TQDate& from, const TQDate& to)
{
  TQDate start;
  TQValueList<Week> weeks;

  // The TQDate weekNumber() method always puts monday as the first day of the
  // week.
  //
  // Not that it matters here, but week 1 always includes the first Thursday
  // of the year.  For example, January 1, 2000 was a Saturday, so
  // TQDate(2000,1,1).weekNumber() returns 52.

  // Since report always shows a full week, we generate a full week of dates,
  // even if from and to are the same date.  The week starts on the day
  // that is set in the locale settings.
  start = from.addDays(
      -((7 - TDEGlobal::locale()->weekStartDay() + from.dayOfWeek()) % 7));

  for (TQDate d = start; d <= to; d = d.addDays(7))
    weeks.append(Week(d));

  return weeks;
}