/*
 * kPPPlogview: a accounting log system for kPPP
 *
 *            Copyright (C) 1998 Mario Weilguni <mweilguni@kde.org>
 *
 * This program 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 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library 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 <qpainter.h>
#include <qcombobox.h>
#include <qfile.h>
#include <qheader.h>

#include <kcalendarsystem.h>
#include <klocale.h>
#include <kglobal.h>
#include <klistview.h>
#include <kdebug.h>
#include <kmessagebox.h>

#include "monthly.h"
#include "export.h"
#include <qstringlist.h>

static void formatBytes(double bytes, QString &result) {
  if(bytes < 1024)
    result.setNum(bytes);
  else if(bytes < 1024*1024)
    result = i18n("%1 KB").arg(KGlobal::locale()->formatNumber((float)bytes / 1024.0, 1));
  else
    result = i18n("%1 MB").arg(KGlobal::locale()->formatNumber((float)bytes / 1024.0 / 1024.0, 1));
}
 
static void formatBytesMonth(double bytes, QString &result) {

  int day, days;
  day = QDate::currentDate().day();
  days = QDate::currentDate().daysInMonth();

  bytes = (bytes / day) * days;

  if(bytes < 1024)
    result.setNum(bytes);
  else if(bytes < 1024*1024)
    result = i18n("%1 KB").arg(KGlobal::locale()->formatNumber((float)bytes / 1024.0, 1));
  else
    result = i18n("%1 MB").arg(KGlobal::locale()->formatNumber((float)bytes / 1024.0 / 1024.0, 1));
}

static void formatDuration(int seconds, QString &result) {
  QString sec;
  sec.sprintf("%02d", seconds%60);
  if(seconds < 60)
    result = i18n("%1s").arg(sec);
  else if(seconds < 3600)
    result = i18n("%1m %2s").arg(seconds/60).arg(sec);
  else
    result = i18n("%1h %2m %3s")
        .arg(seconds/3600)
        .arg((seconds % 3600)/60)
        .arg(sec);
}
 
static void formatDurationMonth(int seconds, QString &result) {

	int day, days;
	day = QDate::currentDate().day();
	days = QDate::currentDate().daysInMonth();

	seconds = (seconds / day) * days;

	QString sec;
	sec.sprintf("%02d", seconds%60);
	if(seconds < 60)
		result = i18n("%1s").arg(sec);
	else if(seconds < 3600)
		result = i18n("%1m %2s").arg(seconds/60).arg(sec);
	else
		result = i18n("%1h %2m %3s")
			.arg(seconds/3600)
			.arg((seconds % 3600)/60)
			.arg(sec);
}

static void costsMonth(double costs, double &result) {

	int day, days;
	day = QDate::currentDate().day();
	days = QDate::currentDate().daysInMonth();

	result = (costs / day) * days;

}

class LogListItem : public QListViewItem {
public:
    LogListItem(LogInfo *l,
		QListView * parent,
		QString s1, QString s2,
		QString s3, QString s4,
		QString s5, QString s6,
		QString s7, QString s8)
	: QListViewItem(parent, s1, s2, s3, s4, s5, s6, s7, s8),
	  li(l)
	{
	}
  virtual void paintCell( QPainter *p, const QColorGroup & cg,
			  int column, int width, int alignment );

    virtual QString key(int, bool) const;

    LogInfo *li;
};

void LogListItem::paintCell( QPainter *p, const QColorGroup & cg,
			     int column, int width, int alignment )
{
  QListViewItem::paintCell(p, cg, column, width, alignment);

  // double line above sum
  //if(!li) {
   // p->drawLine(0, 0, width, 0);
    //p->drawLine(0, 2, width, 2);
  //}
}

QString LogListItem::key(int c, bool ascending) const
{
  if (!li)	// we want the sum to be always at the bottom
    return ascending ? "z" : " ";

  QString k;
  switch (c) {
  case 0:
    k = li->connectionName();
    break;
  case 1:
  case 2:
  case 3:
    k.sprintf("%012u", (uint)li->from_t());
    break;
  case 4:
    k.sprintf("%012d", li->duration());
    break;
  case 5:
    k.sprintf("%012.2f", li->sessionCosts());
    break;
  case 6:
    k.sprintf("%012f", li->bytesIn());
    break;
  case 7:
    k.sprintf("%012f", li->bytesOut());
    break;
  }
  return k;
}

MonthlyWidget::MonthlyWidget(QWidget *parent) :
  QWidget(parent)
{
  tl = 0;

  lv = new KListView(this);
  lv->addColumn(i18n("Connection"));
  lv->addColumn(i18n("Day"));
  lv->addColumn(i18n("From"));
  lv->addColumn(i18n("Until"));
  lv->addColumn(i18n("Duration"));
  lv->addColumn(i18n("Costs"));
  lv->addColumn(i18n("Bytes In"));
  lv->addColumn(i18n("Bytes Out"));
  lv->setColumnAlignment(1, AlignRight);
  lv->setColumnAlignment(2, AlignRight);
  lv->setColumnAlignment(3, AlignRight);
  lv->setColumnAlignment(4, AlignRight);
  lv->setColumnAlignment(5, AlignRight);
  lv->setColumnAlignment(6, AlignRight);
  lv->setColumnAlignment(7, AlignRight);
  lv->setAllColumnsShowFocus(true);
  lv->setShowSortIndicator(true);
  lv->setItemMargin(1);
  lv->setSorting(1);
  lv->setMinimumWidth(180);
  lv->setMinimumHeight(280);
  lv->setSelectionMode(QListView::Extended);
  selectionItem = 0L;
  connect(lv, SIGNAL(selectionChanged()), SLOT(slotSelectionChanged()));

  lv2 = new KListView(this);
  lv2->addColumn(i18n("Connection"));
  lv2->addColumn(i18n("Duration"));
  lv2->addColumn(i18n("Costs"));
  lv2->addColumn(i18n("Bytes In"));
  lv2->addColumn(i18n("Bytes Out"));
  lv2->setColumnAlignment(1, AlignRight);
  lv2->setColumnAlignment(2, AlignRight);
  lv2->setColumnAlignment(3, AlignRight);
  lv2->setColumnAlignment(4, AlignRight);
  lv2->setAllColumnsShowFocus(true);
  lv2->setSorting(-1);
  lv2->setItemMargin(2);
  lv2->setMaximumHeight(100);
  lv2->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
  lv2->setSelectionMode(QListView::NoSelection);

  title = new QLabel("X", this);
  QFont f = title->font();
  f.setPointSize(f.pointSize() + 2);
  f.setBold(TRUE);
  title->setFont(f);
  title->setFixedHeight(title->sizeHint().height()*2);

  cboConnections = new QComboBox(false, this);            // add a combo box to select connections
  cboConnections->setMaximumWidth(200);                   // a resonable size
  cboConnections->insertItem(i18n("All Connections"));    // default to all connections
  connect(cboConnections, SIGNAL(activated(int)),
	  this, SLOT(slotConnections(int)));

  bbox = new KButtonBox(this, Qt::Vertical);
  prev = bbox->addButton(i18n("&Prev Month"));
  next = bbox->addButton(i18n("&Next Month"));
  bbox->addStretch(1);
  today = bbox->addButton(i18n("C&urrent Month"));
  bbox->addStretch(1);
  exportBttn = bbox->addButton(i18n("&Export..."));

  connect(prev, SIGNAL(released()),
	  this, SLOT(prevMonth()));
  connect(next, SIGNAL(released()),
	  this, SLOT(nextMonth()));
  connect(today, SIGNAL(released()),
	  this, SLOT(currentMonth()));
  connect(exportBttn, SIGNAL(clicked()),
	  this, SLOT(exportWizard()));

  bbox->addStretch(8);
  bbox->layout();

  currentMonth();
  layoutWidget();
}

void MonthlyWidget::layoutWidget() {
  if(tl)
    delete tl;

  tl = new QGridLayout(this, 1, 1, 11, 16, "MainLayout");
  tl->addWidget(title, 0, 0);
  tl->addWidget(cboConnections, 0, 1);
  QLabel *l = new QLabel(this);
  l->setText(i18n("Statistics:"));
  QFont f2 = l->font();
  f2.setPointSize(f2.pointSize() + 1);
  f2.setBold(TRUE);
  l->setFont(f2);
  l->setFixedHeight(l->sizeHint().height());
  l->setAlignment( AlignLeft );
  tl->addWidget(l, 5, 0);
  tl->addWidget(bbox, 1, 2);
  tl->addMultiCellWidget(lv, 1, 4, 0, 1);
  tl->addMultiCellWidget(lv2, 6, 6, 0, 1);
      
  tl->activate();
}

int bestlen(QWidget *w, const char *s) {
  return w->fontMetrics().boundingRect(s).width() + 8;
}

void MonthlyWidget::plotMonth() {
  // name of the current connection
  QString con;

  // for collecting monthly statistics
  int count = 0;
  double costs = 0;
  double bin = 0, bout = 0;
  int duration = 0;
  lv->clear();
  selectionItem = 0L;
  lv2->clear();

  const KCalendarSystem * calendar = KGlobal::locale()->calendar();
  QDate startDate = periodeFirst();

  for(int i = 0; i < (int)logList.count(); i++) {
    LogInfo *li = logList.at(i);

    QDate logDate = li->from().date();
    if ( periodeFirst() <= logDate && periodeLast() >= logDate ) {
      // get connection name for this line
      con = li->connectionName();

      // this connection name not in the list and combo box
      if(lstConnections.findIndex(con) == -1) {
	lstConnections.append(con);
        cboConnections->insertItem(con);
      }
      // if all connections or the selected one
      if(cboConnections->currentText() != con &&
	cboConnections->currentItem() != 0)
        continue;
      count++;
      costs += li->sessionCosts();
      if(bin >= 0) {
        if(li->bytesIn() < 0)
          bin = -1;
        else
          bin += li->bytesIn();
      }

      if(bout >= 0) {
        if(li->bytesOut() < 0)
          bout = -1;
        else
          bout += li->bytesOut();
      }

      duration += li->from().secsTo(li->until());

      QString _bin, _bout, b;
      if(li->bytesIn() >= 0)
        formatBytes(li->bytesIn(), _bin);
      else
        _bin = i18n("n/a");

      if(li->bytesOut() >= 0)
        formatBytes(li->bytesOut(), _bout);
      else
        _bout = i18n("n/a");

      if(li->bytes() > 0)
        formatBytes(li->bytes(), b);
      else
        b = i18n("n/a");

      QString day;
      day.sprintf("%2d", li->from().date().day());

      QString s_duration;
      formatDuration(li->from().secsTo(li->until()),
                     s_duration);

      QString s_lifrom, s_liuntil, s_costs;
      s_lifrom = KGlobal::locale()->formatTime(li->from().time(), false);
      s_liuntil = KGlobal::locale()->formatTime(li->until().time(), false);
      s_costs = KGlobal::locale()->formatMoney(li->sessionCosts());

      (void) new LogListItem(li, lv, con, day, s_lifrom, s_liuntil, s_duration, s_costs, _bin, _bout);
    }
  }

  if(count) {
    QString _bin, _bout, _b;

    if(bin < 0)
      _bin = i18n("n/a");
    else
      formatBytes(bin, _bin);

    if(bout < 0)
      _bout = i18n("n/a");
    else
      formatBytes(bout, _bout);

    if(bin < 0 || bout < 0)
      _b = i18n("n/a");
    else
      formatBytes(bout + bin, _b);

    QString s_duration;
    formatDuration(duration,
                   s_duration);

    QString s_costs(KGlobal::locale()->formatMoney(costs, QString::null, 2));

    selectionItem =  new LogListItem(0, lv2,
			   i18n("Selection (%n connection)", "Selection (%n connections)", 0),
			   QString::null, QString::null, QString::null,
			   QString::null, QString::null, QString::null, QString::null);
    (void) new LogListItem(0, lv2,
			   i18n("%n connection", "%n connections", count),
			   s_duration, s_costs, _bin, _bout, QString::null, QString::null, QString::null);

	const KCalendarSystem * calendar = KGlobal::locale()->calendar();

	if(calendar->month(periodeFirst()) == calendar->month(QDate::currentDate())) {

	QString m_bin, m_bout;

    if(bin < 0)
      _bin = i18n("n/a");
    else
	formatBytesMonth(bin, m_bin);

    if(bout < 0)
      _bout = i18n("n/a");
    else
	formatBytesMonth(bout, m_bout);

    QString m_duration;
    formatDurationMonth(duration, m_duration);

	costsMonth(costs, costs);
    QString m_costs(KGlobal::locale()->formatMoney(costs, QString::null, 2));

    (void) new QListViewItem(lv2, selectionItem,
			   i18n("Monthly estimates"), m_duration, m_costs, m_bin, m_bout,
			   QString::null, QString::null, QString::null);
	}
  }

  QString t;
  if(lv->childCount() > 0) {
    exportBttn->setEnabled(true); // export possibility
    t = i18n("Connection log for %1 %2")
	      .arg(calendar->monthName(startDate))
	      .arg(calendar->year(startDate));
  } else {
    exportBttn->setEnabled(false); // nothing to export
    t = i18n("No connection log for %1 %2 available")
	      .arg(calendar->monthName(startDate))
	      .arg(calendar->year(startDate));
  }

  title->setText(t);
}

void MonthlyWidget::slotConnections(int) {
  plotMonth();
}

void MonthlyWidget::nextMonth() {
  m_periodeFirst = KGlobal::locale()->calendar()->addMonths(m_periodeFirst, 1);

  plotMonth();
}

void MonthlyWidget::prevMonth() {
  m_periodeFirst = KGlobal::locale()->calendar()->addMonths(m_periodeFirst, -1);

  plotMonth();
}

void MonthlyWidget::currentMonth() {
  const KCalendarSystem * calendar = KGlobal::locale()->calendar();
  QDate dt = QDate::currentDate();
  calendar->setYMD(m_periodeFirst, calendar->year(dt), calendar->month(dt), 1);

  plotMonth();
}

void MonthlyWidget::exportWizard() {
  const KCalendarSystem * calendar = KGlobal::locale()->calendar();
  QString date = QString::fromLatin1("%1-%2") // e.g.: June-2001
    .arg(calendar->monthName(periodeFirst()))
    .arg(calendar->year(periodeFirst()));

  ExportWizard *wizard = new ExportWizard(0, date);
  wizard->exec();
  if (wizard->filename.isEmpty()) { // wizard aborted...
    return;
  }
  if (QFile::exists(wizard->filename)) {  // overwrite?
    if (KMessageBox::Continue!=KMessageBox::warningContinueCancel(0, i18n("A document with this name already exists."), i18n("Overwrite file?"), i18n("&Overwrite") /*, true*/)) { // no
      return;
    }
  }

  // open file
  Export *exportIFace = wizard->createExportFilter();
  if (exportIFace == NULL) { // error
    return;
  }

  if (!exportIFace->openFile()) {  // error opening
    KMessageBox::sorry(0, i18n("An error occurred while trying to open this file"), i18n("Sorry"), true);
    delete exportIFace;
    return; // abort...
  }

  // start writing data
  exportIFace->addHeadline(i18n("Connection"), i18n("Day"), i18n("From"), i18n("Until"),
			   i18n("Duration"), i18n("Costs"), i18n("Bytes In"), i18n("Bytes Out") );

  // name of the current connection
  QString con;

  // for collecting monthly statistics
  int count = 0;
  double costs = 0;
  double bin = 0, bout = 0;
  int duration = 0;

  for(int i = 0; i < (int)logList.count(); i++) {
    LogInfo *li = logList.at(i);

    QDate logDate = li->from().date();
    if (periodeFirst() <= logDate && periodeLast() >= logDate ) {
      // get connection name for this line
      con = li->connectionName();

      // this connection name not in the list and combo box
      if(lstConnections.findIndex(con) == -1) {
          lstConnections.append(con);
        cboConnections->insertItem(con);
      }
      // if all connections or the selected one
      if(cboConnections->currentText() != con &&
         cboConnections->currentItem() != 0)
        continue;

      count++;
      costs += li->sessionCosts();
      if(bin >= 0) {
        if(li->bytesIn() < 0)
          bin = -1;
        else
          bin += li->bytesIn();
      }

      if(bout >= 0) {
        if(li->bytesOut() < 0)
          bout = -1;
        else
          bout += li->bytesOut();
      }

      duration += li->from().secsTo(li->until());

      QString _bin, _bout, b;
      if(li->bytesIn() >= 0)
        formatBytes(li->bytesIn(), _bin);
      else
        _bin = i18n("n/a");

      if(li->bytesOut() >= 0)
        formatBytes(li->bytesOut(), _bout);
      else
        _bout = i18n("n/a");

      if(li->bytes() > 0)
        formatBytes(li->bytes(), b);
      else
        b = i18n("n/a");

      QString day;
      day.sprintf("%2d", li->from().date().day());
      QString con = li->connectionName();

      QString s_duration;
      formatDuration(li->from().secsTo(li->until()),
                     s_duration);

      QString s_lifrom, s_liuntil, s_costs;
      s_lifrom = KGlobal::locale()->formatTime(li->from().time(), false);
      s_liuntil = KGlobal::locale()->formatTime(li->until().time(), false);
      s_costs = KGlobal::locale()->formatMoney(li->sessionCosts());

      // call export method
      exportIFace->addDataline(con, day, s_lifrom, s_liuntil, s_duration,
			       s_costs, _bin, _bout);

    }
  }

	if(calendar->month(periodeFirst()) == calendar->month(QDate::currentDate())) {

	QString m_bin, m_bout;
	if(bin < 0)
		m_bin = i18n("n/a");
	else
		formatBytesMonth(bin, m_bin);

	if(bout < 0)
		m_bout = i18n("n/a");
	else
		formatBytesMonth(bout, m_bout);

	QString m_duration;
	formatDurationMonth(duration, m_duration);

	costsMonth(costs, costs);
	QString m_costs(KGlobal::locale()->formatMoney(costs, QString::null, 2));

	QString datetime = KGlobal::locale()->formatDateTime( QDateTime::currentDateTime(), true);

	exportIFace->addEmptyLine();
	exportIFace->addDataline(i18n("Monthly estimates (%1)").arg(datetime), 
		QString::null, QString::null, QString::null, m_duration, m_costs, m_bin, m_bout);
	}

  if(count) {
    QString _bin, _bout, _b;

    if(bin < 0)
      _bin = i18n("n/a");
    else
      formatBytes(bin, _bin);

    if(bout < 0)
      _bout = i18n("n/a");
    else
      formatBytes(bout, _bout);

    if(bin < 0 || bout < 0)
      _b = i18n("n/a");
    else
      formatBytes(bout + bin, _b);

    QString s_duration;
    formatDuration(duration,
                   s_duration);

    QString s_costs(KGlobal::locale()->formatMoney(costs, QString::null, 2));

    // call export methods
    exportIFace->addEmptyLine();
    exportIFace->addDataline(i18n("%n connection", "%n connections", count), QString::null, QString::null, QString::null, s_duration,
			     s_costs, _bin, _bout);
    exportIFace->setFinishCode();

    // write buffer to file and close file
    if (!exportIFace->closeFile())  {
      KMessageBox::sorry(0, i18n("An error occurred while trying to write to this file."), i18n("Sorry"), true);
      delete exportIFace;
      return;
    }

  }
  delete exportIFace;
}

QDate MonthlyWidget::periodeFirst() const
{
  return m_periodeFirst;
}

QDate MonthlyWidget::periodeLast() const
{
  const KCalendarSystem * calendar = KGlobal::locale()->calendar();

  // One month minus one day
  return calendar->addDays(calendar->addMonths(m_periodeFirst, 1), -1);
}

void MonthlyWidget::slotSelectionChanged()
{
  if (selectionItem)
  {
    int count = 0;
    double costs = 0;
    double bin = 0, bout = 0;
    int duration = 0;
    LogListItem *item;
    LogInfo *li;
    QListViewItemIterator it(lv);
    while ( it.current() )
    {
      item = dynamic_cast<LogListItem*>(it.current());
      if ( item && item->isSelected() && item->li)
      {
        li = item->li;
        costs += li->sessionCosts();
        if(bin >= 0) {
          if(li->bytesIn() < 0)
            bin = -1;
          else
            bin += li->bytesIn();
        }

        if(bout >= 0) {
          if(li->bytesOut() < 0)
            bout = -1;
          else
            bout += li->bytesOut();
        }

        duration += li->from().secsTo(li->until());
        count++;
      }
      ++it;
    }
    if(count)
    {
      QString _bin, _bout, _b;

      if(bin < 0)
        _bin = i18n("n/a");
      else
        formatBytes(bin, _bin);

      if(bout < 0)
        _bout = i18n("n/a");
      else
        formatBytes(bout, _bout);

      if(bin < 0 || bout < 0)
        _b = i18n("n/a");
      else
        formatBytes(bout + bin, _b);

      QString s_duration;
      formatDuration(duration,
                    s_duration);

      QString s_costs(KGlobal::locale()->formatMoney(costs, QString::null, 2));
      selectionItem->setText(0, i18n("Selection (%n connection)", "Selection (%n connections)", count));
      selectionItem->setText(1, s_duration);
      selectionItem->setText(2, s_costs);
      selectionItem->setText(3, _bin);
      selectionItem->setText(4, _bout);
    }
  }
}

#include "monthly.moc"