/*
 *   Copyright 2009 Andrew Stromme <astromme@chatonka.com>
 *
 *   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, 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 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 "taskitemdelegate.h"

#include "taskeditor.h"

#include <QApplication>

#include <Plasma/Theme>
#include <Plasma/Svg>

#include <QColor>
#include <QLinearGradient>
#include <QCheckBox>

#include <KDebug>

const qreal TaskItemDelegate::MARGIN = 1.0;
const qreal TaskItemDelegate::MARGINH = 6.0;
const qreal TaskItemDelegate::COLORTAB = MARGIN*6;

// These colors are hardcoded because they represent priorities and need to be the same for visual recognition purposes.
const QColor TaskItemDelegate::priority1 = QColor(255, 123, 0); // reddish-orange
const QColor TaskItemDelegate::priority2 = QColor(0, 132, 255); // Darker blue
const QColor TaskItemDelegate::priority3 = QColor(53, 201, 255);  // Ligher Blue
const QColor TaskItemDelegate::priorityNone = QColor(241, 241, 241); // Almost white

const QString TaskItemDelegate::tagsPrefix = i18n("Tags: ");

TaskItemDelegate::TaskItemDelegate(QObject *parent)
  : QStyledItemDelegate(parent),
    dragging(false)
{
  setSmallTasks(false);
}

void TaskItemDelegate::setSmallTasks(bool small)
{
  smallTasks = small;
  leftMargin = small ? 0 : MARGIN;
}


bool TaskItemDelegate::eventFilter(QObject* object, QEvent* event) {
  if (event->type() == QEvent::DragMove)
    dragging = true;
  if (event->type() == QEvent::Drop || QEvent::DragLeave)
    dragging = false;
  return QStyledItemDelegate::eventFilter(object, event);
}

QColor TaskItemDelegate::itemPriorityColor(const QModelIndex &index) const
{
  switch (index.data(Qt::RTMPriorityRole).toInt()) { // Set color based on priority
    case 1:
      return priority1;
    case 2:
      return priority2;
    case 3:
      return priority3;
    case 4:
      return priorityNone;
  }
  return QColor();
}

void TaskItemDelegate::paintDueHeader(QPainter* painter, const QRectF& rect, const QModelIndex &index) const
{
  painter->save();
  
  QFontMetrics fm = QApplication::fontMetrics();
  QString dueString = index.data(Qt::DisplayRole).toString();

  QLinearGradient gradient(rect.bottomLeft(), rect.bottomRight());
  gradient.setColorAt(0, Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor));
  gradient.setColorAt((qreal)(0.9*fm.width(dueString)+2*MARGIN)/rect.width(), Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor));
  gradient.setColorAt((qreal)(1.3*fm.width(dueString)+2*MARGIN)/rect.width(), Qt::transparent);
  painter->setBrush(Qt::NoBrush);
  painter->setPen(QPen(QBrush(gradient), 1));
  painter->drawLine(rect.bottomLeft()+QPoint(0, -MARGIN), rect.bottomRight()+QPoint(0, -MARGIN));
  
  painter->setPen(Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor));
  painter->drawText(rect.bottomLeft()+QPoint(MARGIN, -2*MARGIN) ,dueString);
  
  painter->restore();
}

void TaskItemDelegate::paintPriorityHeader(QPainter* painter, const QRectF& rect, const QModelIndex &index) const
{
  painter->save();
  
  //Draw colored gradient as priority background
  QLinearGradient gradient(rect.topLeft(), rect.bottomRight());
  gradient.setColorAt(0, itemPriorityColor(index));
  gradient.setColorAt(1, Qt::transparent);
  
  int h = QApplication::fontMetrics().height();
  QPainterPath path;
  path.moveTo(rect.bottomLeft());
  path.quadTo(rect.bottomLeft()+QPointF(0, -h), rect.bottomLeft()+QPointF(2, -h));
  path.lineTo(rect.bottomRight()-QPointF(2, h));
  path.quadTo(rect.bottomRight()+QPointF(0, -h), rect.bottomRight());
  painter->setBrush(Qt::NoBrush);
  QPen thickPen(itemPriorityColor(index));
  thickPen.setWidth(2);
  painter->setPen(thickPen);
  painter->drawPath(path);
  
  // Draw priority text
  QString priority = index.data(Qt::DisplayRole).toString();
  painter->setPen(Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor));
  painter->setBrush(Qt::NoBrush);
  painter->drawText(rect.bottomLeft()+QPoint(MARGIN, -2*MARGIN) ,priority);
 
  painter->restore();
}

void TaskItemDelegate::paintTask(QPainter* painter, const QStyleOptionViewItem &option, const QModelIndex& index) const
{
  QRect rect = option.rect;
  QFontMetrics fm = QApplication::fontMetrics();
  
  // Painting Order:
  //    Background wash
  //    Mouseover highlight
  //    Priority Tab on left
  //    Bounding Box
  //    Task name
  //    If not small, other task information
  
  painter->save();
  if (smallTasks) { // Paint the background for a small task
    painter->save();
    painter->setOpacity(0.5);
    painter->setBrush(Plasma::Theme::defaultTheme()->color(Plasma::Theme::BackgroundColor));
    painter->setPen(Qt::NoPen);
    painter->drawRoundedRect(rect.adjusted(leftMargin, MARGIN, -MARGIN, -MARGIN), 3, 3);
    painter->restore();
  }
  else { // Paint the background for a two row task
    painter->save();
    painter->setOpacity(0.5);
    painter->setBrush(Plasma::Theme::defaultTheme()->color(Plasma::Theme::BackgroundColor));
    painter->drawRect(option.rect.adjusted(leftMargin, MARGIN, -MARGIN, -MARGIN));
    
    painter->restore();
    painter->drawRect(QRectF(option.rect.topLeft()+QPointF(leftMargin, MARGIN), option.rect.bottomLeft()+QPointF(COLORTAB, -MARGIN))); 
  }
  
  if (option.state & QStyle::State_MouseOver) {
    painter->save();
    painter->setBrush(Plasma::Theme::defaultTheme()->color(Plasma::Theme::HighlightColor));
    painter->setPen(Qt::NoPen);
    if (smallTasks)
      painter->drawRoundedRect(rect.adjusted(leftMargin, MARGIN, -MARGIN, -MARGIN), 3, 3);
    else
      painter->drawRect(rect.adjusted(leftMargin, MARGIN, -MARGIN, -MARGIN));
    painter->restore();
  }
  
  //Paint Priority tab on the left
  painter->setPen(Qt::NoPen);
  painter->setBrush(itemPriorityColor(index));
  painter->drawRoundedRect(QRectF(option.rect.topLeft()+QPointF(leftMargin, MARGIN), option.rect.bottomLeft()+QPointF(COLORTAB, 0)), 3, 3);

  
  // Draw Bounding rect/box
  painter->save();
  painter->setOpacity(.5);
  painter->setPen(Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor));
  painter->setBrush(Qt::NoBrush);
  if (smallTasks)
    painter->drawRoundedRect(option.rect.adjusted(leftMargin, MARGIN, -MARGIN, -MARGIN), 3, 3);
  else
    painter->drawRect(option.rect.adjusted(leftMargin, MARGIN, -MARGIN, -MARGIN));
  painter->restore();
  
  
  // Draw Task information
  painter->setPen(Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor));
  
  QString task = index.data(Qt::RTMNameRole).toString();
  QString due = index.data(Qt::RTMDueRole).toDate().toString("MMM d");
  QString tags = index.data(Qt::RTMTagsRole).toStringList().join(", ");
  if (!tags.isEmpty())
    tags.prepend(tagsPrefix);
  
  QFont font = painter->font();
  font.setPointSize(font.pointSize());
  QFontMetrics largerFM(font);
  
  QDate date = index.data(Qt::RTMDueRole).toDate();
  
  if (smallTasks && date.isValid() && date < QDate::currentDate()) {
    // Elide (text with dots like th...) if wider than view and draw
    font.setBold(true);
    task = largerFM.elidedText(task, Qt::ElideRight, option.rect.width()-COLORTAB-3*MARGIN - QFontMetrics(font).width(due));
    due = due.append(" "); // So that we have a space after the date before the task name
    painter->setFont(font);
    painter->drawText(option.rect.topLeft()+QPoint(COLORTAB+MARGIN,largerFM.height()),due);
    
    font.setBold(false);
    painter->setFont(font);
    painter->drawText(option.rect.topLeft()+QPoint(COLORTAB+3*MARGIN + largerFM.width(due),largerFM.height()),task);
  }
  else {
    // Elide (text with dots like th...) if wider than view and draw
    task = largerFM.elidedText(task, Qt::ElideRight, option.rect.width()-COLORTAB-3*MARGIN);
    painter->setFont(font);
    painter->drawText(option.rect.topLeft()+QPoint(COLORTAB+MARGIN,largerFM.height()),task);
  }
  
  if (smallTasks) {
    painter->restore();
    return;
  }
  
  // Set our smaller attributes and blue hue for additional text
  font.setPointSize(font.pointSize()-1);
  font.setItalic(true);
  painter->setFont(font);
  fm = QFontMetrics(font);
  QColor moreText(Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor));
  
  qreal red = moreText.redF();
  qreal green = moreText.greenF();
  qreal blue = moreText.blueF();
  qreal colorChangeMult = 0.85;
  if (blue < colorChangeMult)
    blue /= colorChangeMult;
  else {
    red *= colorChangeMult;
    green *= colorChangeMult;
  }
  moreText.setRgbF(red, green, blue);
  painter->setPen(moreText);
  
  // Elide the due from the front because "Due on" isn't necessary
  due = fm.elidedText(due, Qt::ElideLeft, option.rect.width() - COLORTAB+3*MARGIN - fm.width(tags));
  painter->drawText(option.rect.bottomLeft()+QPoint(COLORTAB+MARGIN, -MARGINH), tags);
  painter->drawText(option.rect.bottomRight()+QPoint(-COLORTAB+MARGIN-fm.width(due), -MARGINH), due);

  painter->restore();
}

void TaskItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
                const QModelIndex &index) const
{
  switch (index.data(Qt::RTMItemType).toInt()) {
    case RTMPriorityHeader:
      paintPriorityHeader(painter, option.rect, index);
      break;
    case RTMDateHeader:
      paintDueHeader(painter, option.rect, index);
      break;
    case RTMTaskItem:
      paintTask(painter, option, index);
      break;
  }
}

QSize TaskItemDelegate::sizeHint(const QStyleOptionViewItem &option,
                    const QModelIndex &index) const
{
  Q_UNUSED(option)
  if (smallTasks || index.data(Qt::RTMItemType).toInt() != RTMTaskItem) {
    QString priority = index.data(Qt::DisplayRole).toString();
    QSize size;
    if (index.data(Qt::RTMItemType).toInt() == RTMDateHeader)
      size.setHeight(QApplication::fontMetrics().height()+MARGINH*1.5);
    else 
      size.setHeight(QApplication::fontMetrics().height()+MARGINH);
    size.setWidth(QApplication::fontMetrics().width(priority));
    return size;
  }
  else {
    QString task = index.data(Qt::RTMNameRole).toString();
    QString due = index.data(Qt::RTMDueRole).toDate().toString("MMM d");
    QString tags = index.data(Qt::RTMTagsRole).toStringList().join(", ");
    if (!tags.isEmpty())
      tags.prepend(tagsPrefix);

    //QSize size = QStyledItemDelegate::sizeHint(option, index);
    QSize size;
    size.setHeight(MARGINH + MARGINH + QApplication::fontMetrics().height()*2);
    size.setWidth(MARGIN + MARGIN + QApplication::fontMetrics().width(task+due));
    return size;
  }
}
