#include "taskmodel.h"

#include <QDebug>

#include "branchitem.h"
#include "branchobj.h"
#include "task.h"
#include "vymmodel.h"

TaskModel::TaskModel(QObject *parent) : QAbstractTableModel(parent)
{
    showParentsLevel = 0;

    QSize size = QSize(22, 22);
    QSize size2 = QSize(44, 22);

    arrow_up_icon =
        QIcon(QPixmap(":/flag-arrow-up.svg").scaled(size, Qt::KeepAspectRatio));
    arrow_2up_icon = QIcon(
        QPixmap(":/flag-arrow-2up.svg").scaled(size, Qt::KeepAspectRatio));

    task_new_icon =
        QIcon(QPixmap(":/flag-task-new.svg").scaled(size, Qt::KeepAspectRatio));
    task_new_morning_icon = QIcon(QPixmap(":/flag-task-new-morning.svg")
                                      .scaled(size, Qt::KeepAspectRatio));
    task_new_sleeping_icon = QIcon(QPixmap(":/flag-task-new-sleeping.svg")
                                       .scaled(size, Qt::KeepAspectRatio));

    task_wip_icon =
        QIcon(QPixmap(":/flag-task-wip.svg").scaled(size, Qt::KeepAspectRatio));
    task_wip_morning_icon = QIcon(QPixmap(":/flag-task-wip-morning.svg")
                                      .scaled(size, Qt::KeepAspectRatio));
    task_wip_sleeping_icon = QIcon(QPixmap(":/flag-task-wip-sleeping.svg")
                                       .scaled(size, Qt::KeepAspectRatio));

    task_finished_icon = QIcon(
        QPixmap(":/flag-task-finished.svg").scaled(size, Qt::KeepAspectRatio));

    taskfilter_stopsign_icon =
        QIcon(QPixmap(":/flag-stopsign.svg").scaled(size, Qt::KeepAspectRatio));
    taskfilter_stopsign_arrow_up_icon =
        QIcon(QPixmap(":/flag-stopsign-arrow-up.png")
                  .scaled(size2, Qt::KeepAspectRatio));
    taskfilter_stopsign_arrow_2up_icon =
        QIcon(QPixmap(":/flag-stopsign-arrow-2up.png")
                  .scaled(size2, Qt::KeepAspectRatio));
}

QModelIndex TaskModel::index(Task *t) const
{
    int n = tasks.indexOf(t);
    if (n < 0)
        return QModelIndex();
    else
        return createIndex(n, 0, t);
}

QModelIndex TaskModel::indexRowEnd(Task *t)
{
    int n = tasks.indexOf(t);
    if (n < 0)
        return QModelIndex();
    else
        return createIndex(n, 8, t);
}

Task *TaskModel::getTask(const QModelIndex &ix) const
{
    if (ix.isValid())
        return tasks.at(ix.row());
    else
        return NULL;
}

Task *TaskModel::getTask(const int i) const
{
    if (i >= 0 && i < count())
        return getTask(createIndex(i, 0));
    else
        return NULL;
}

int TaskModel::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return tasks.size();
}

int TaskModel::columnCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return 9;
}

QVariant TaskModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

    if (index.row() >= tasks.size() || index.row() < 0)
        return QVariant();

    BranchItem *bi = tasks.at(index.row())->getBranch();
    Task *t = tasks.at(index.row());

    if (role == Qt::DisplayRole) {
        if (index.column() == 0)
            return t->getPriority();
        else if (index.column() == 1)
            return t->getPriorityDelta();
        else if (index.column() == 2)
            return QString(); // return t->getStatusString() + " - "
                              // +t->getAwakeString();
        else if (index.column() == 3)
            return t->getAgeCreation();
        else if (index.column() == 4)
            return t->getAgeModification();
        else if (index.column() == 5) {
            if (t->getDaysSleep() > 0)
                return t->getDaysSleep();
            else
                return "-";
        }
        else if (index.column() == 6) {
            QString s = bi->getModel()->getMapName();
            if (s.isEmpty())
                return "-";
            else
                return bi->getModel()->getMapName();
        }
        else if (index.column() == 8) {
            BranchItem *bi = tasks.at(index.row())->getBranch();
            return bi->getHeadingPlainWithParents(showParentsLevel);
        }
    }
    else if (role == Qt::DecorationRole && index.column() == 2) {
        QString s = t->getIconString();
        if (s == "task-new")
            return task_new_icon;
        else if (s == "task-new-morning")
            return task_new_morning_icon;
        else if (s == "task-new-sleeping")
            return task_new_sleeping_icon;
        else if (s == "task-wip")
            return task_wip_icon;
        else if (s == "task-wip-sleeping")
            return task_wip_sleeping_icon;
        else if (s == "task-wip-morning")
            return task_wip_morning_icon;
        else if (s == "task-finished")
            return task_finished_icon;
        else {
            qWarning() << "Unknown task type in TaskModel::data: " << s;
            return QVariant();
        }
    }
    else if (role == Qt::DecorationRole && index.column() == 7) {
        BranchItem *bi = t->getBranch();
        if (bi->hasActiveFlag("stopsign")) {
            if (bi->hasActiveFlag("2arrow-up"))
                return taskfilter_stopsign_arrow_2up_icon;
            else if (bi->hasActiveFlag("arrow-up"))
                return taskfilter_stopsign_arrow_up_icon;
            else
                return taskfilter_stopsign_icon;
        }
        else {
            if (bi->hasActiveFlag("2arrow-up"))
                return arrow_2up_icon;
            else if (bi->hasActiveFlag("arrow-up"))
                return arrow_up_icon;
        }
        return QIcon();
    }
    else // role != Qt::DisplayRole
    {
        if (role == Qt::EditRole && index.column() == 1) // DeltaPrio
            return t->getPriorityDelta();
        if (role == Qt::ForegroundRole && bi)
            return bi->getHeadingColor();
        if (role == Qt::BackgroundRole && bi) {
            BranchItem *frameBI = bi->getFramedParentBranch(bi);
            if (frameBI && index.column() != 5) {
                BranchObj *bo = frameBI->getBranchObj();
                if (bo)
                    // Return frame background
                    return bo->getFrameBrushColor();
            }
            else {
                // Return map background
                return bi->getModel()->getMapBackgroundColor();
            }
        }
    }

    return QVariant();
}

QVariant TaskModel::headerData(int section, Qt::Orientation orientation,
                               int role) const
{
    if (role != Qt::DisplayRole)
        return QVariant();

    if (orientation == Qt::Horizontal) {
        switch (section) {
        case 0:
            return tr("Prio", "TaskEditor");
        case 1:
            return tr("Delta", "TaskEditor");
        case 2:
            return tr("Status", "TaskEditor");
        case 3:
            return tr("Age total", "TaskEditor");
        case 4:
            return tr("Age mod.", "TaskEditor");
        case 5:
            return tr("Sleep", "TaskEditor");
        case 6:
            return tr("Map", "TaskEditor");
        case 7:
            return tr("Flags", "TaskEditor");
        case 8:
            return tr("Task", "TaskEditor");
        default:
            return QVariant();
        }
    }
    return QVariant();
}

bool TaskModel::insertRows(int position, int rows, const QModelIndex &index,
                           Task *t)
{
    Q_UNUSED(index);
    beginInsertRows(QModelIndex(), position, position + rows - 1);

    for (int row = 0; row < rows; row++)
        tasks.insert(position, t);

    endInsertRows();
    return true;
}

bool TaskModel::removeRows(int position, int rows, const QModelIndex &index)
{
    Q_UNUSED(index);
    beginRemoveRows(QModelIndex(), position, position + rows - 1);

    for (int row = 0; row < rows; ++row)
        delete (tasks.takeAt(position));

    endRemoveRows();
    return true;
}

bool TaskModel::setData(const QModelIndex &index, const QVariant &value,
                        int role)
{
    if (index.isValid() && role == Qt::EditRole) {
        Task *t = tasks.at(index.row());
        if (!t) {
            qWarning() << "TaskModel::setData  no task found";
            return false;
        }

        if (index.column() == 1) // set Delta Priority
        {
            BranchItem *bi = t->getBranch();
            VymModel *m = bi->getModel();
            m->setTaskPriorityDelta(value.toInt(), bi);
            recalcPriorities();
            emit(dataChanged(index, index));
            return true;
        }
        if (index.column() == 8) // set Heading
        {
            BranchItem *bi = t->getBranch();
            VymModel *m = bi->getModel();
            m->setHeadingPlainText(value.toString(), bi);
            emit(dataChanged(index, index));
            return true;
        }
    }

    return false;
}

void TaskModel::emitDataChanged(Task *t)
{
    QModelIndex ix = index(t);
    if (ix.isValid()) {
        int row = ix.row();
        int col = 0;
        while (col < columnCount(QModelIndex())) {
            ix = createIndex(row, col, t);
            if (ix.isValid())
                emit(dataChanged(ix, ix));
            col++;
        }
    }
}

Qt::ItemFlags TaskModel::flags(const QModelIndex &index) const
{
    if (!index.isValid())
        return Qt::ItemIsEnabled;

    return QAbstractTableModel::flags(index) | Qt::ItemIsDragEnabled |
           Qt::ItemIsDropEnabled | Qt::ItemIsEditable;
}

int TaskModel::count(VymModel *model) const
{
    if (!model)
        return tasks.size();
    int n = 0;
    foreach (Task *t, tasks)
        if (t->getBranch()->getModel() == model)
            n++;
    return n;
}

Task *TaskModel::createTask(BranchItem *bi)
{
    if (bi) {
        foreach (Task *t, tasks) {
            if (t->getBranch() == bi) {
                qWarning() << "TaskModel::createTask Branch exists already!";
                return NULL;
            }
        }
        Task *task = new Task(this);
        task->setBranch(bi);
        task->setAwake(Task::Morning);
        insertRows(tasks.count(), 1, QModelIndex(), task);

        bi->setTask(task);

        return task;
    }
    qWarning() << "TaskEditor::addItem - item exists";
    return NULL;
}

void TaskModel::deleteTask(Task *t)
{
    int pos = tasks.indexOf(t);
    if (pos >= 0)
        removeRows(pos, 1, QModelIndex());
}

bool TaskModel::updateAwake(bool force)
{
    bool awake_changed = false;
    foreach (Task *t, tasks) {
        if (t->updateAwake() || force) {
            t->getBranch()->updateTaskFlag();
            awake_changed = true;
        }
    }
    return awake_changed;
}

void TaskModel::recalcPriorities()
{
    emit(layoutAboutToBeChanged());
    int minPrio = 1000000;
    foreach (Task *t, tasks) {
        int p = 0;
        BranchItem *bi = t->getBranch();

        // Status
        switch (t->getStatus()) {
        case Task::NotStarted:
            break;
        case Task::WIP:
            p += 10;
            break;
        case Task::Finished:
            p += 2000;
            break;
        }

        // Awake and sleeping
        switch (t->getAwake()) {
        case Task::Morning:
            p -= 1000;
            break;
        case Task::WideAwake:
            break;
        case Task::Sleeping:
            //p += 1000 + t->getDaysSleep();
            break;
        }

        // Color (importance)
        QColor c = bi->getHeadingColor();

        // light blueish green
        if (c == QColor("#00aa7f"))
            p -= 20;

        // green (e.g. from vym < 2.6.3 with #005500)
        if (c.red() == 0 && c.blue() == 0 && c.green() < 160)
            p -= 40;

        // orange
        if (c == QColor("#d95100"))
            p -= 60;

        // red
        if (c == QColor("#ff0000"))
            p -= 80;

        // Flags
        if (bi->hasActiveFlag("stopsign"))
            p -= 450;
        if (bi->hasActiveFlag("2arrow-up"))
            p -= 1000;
        if (bi->hasActiveFlag("arrow-up"))
            p -= 500;

        // Age
        p -= t->getAgeModification();
        p -= t->getAgeCreation() * 1.0 / 365 *
             80; // After a year, this is as important as "red"

        // Position in subtree
        p += bi->num();

        // Priority delta (set menually)
        p -= t->getPriorityDelta();

        // Set priority finally
        t->setPriority(p);
        if (p < minPrio)
            minPrio = p;
    }
    // Normalize, so that most important task has prio 1
    foreach (Task *t, tasks) {
        t->setPriority(1 - minPrio + t->getPriority());
    }

    emit(layoutChanged());
}

void TaskModel::setShowParentsLevel(uint i)
{
    showParentsLevel = i;
    recalcPriorities(); // Triggers update of view
}

uint TaskModel::getShowParentsLevel() { return showParentsLevel; }

Qt::DropActions TaskModel::supportedDropActions() const
{
    return Qt::MoveAction;
}

QStringList TaskModel::mimeTypes() const
{
    QStringList types;
    types << "application/vnd.text.list";
    return types;
}

QMimeData *TaskModel::mimeData(const QModelIndexList &indexes) const
{
    QMimeData *mimeData = new QMimeData();
    QByteArray encodedData;

    QDataStream stream(&encodedData, QIODevice::WriteOnly);

    if (indexes.count() > 0 && indexes.first().isValid()) {
        Task *task = getTask(indexes.first());

        // Field 0: Heading
        QString text = task->getBranch()->getHeadingPlain();
        stream << text;

        // Field 1: task row
        stream << QString::number(index(task).row());
    }

    mimeData->setData("application/vnd.text.list", encodedData);
    return mimeData;
}

bool TaskModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
                             int row, int column, const QModelIndex &parent)
{
    Q_UNUSED(row);

    if (action == Qt::IgnoreAction)
        return true;

    if (!data->hasFormat("application/vnd.text.list"))
        return false;

    if (column > 0)
        return false;

    QByteArray encodedData = data->data("application/vnd.text.list");
    QDataStream stream(&encodedData, QIODevice::ReadOnly);
    QStringList newItems;
    int rows = 0;

    while (!stream.atEnd()) {
        QString text;
        stream >> text;
        newItems << text;
        ++rows;
    }

    Task *dst = getTask(parent);
    Task *src = getTask(newItems[1].toInt());

    // qDebug() << "Dropping: " <<  src->getBranch()->getHeadingPlain() << " on
    // " << dst->getBranch()->getHeadingPlain();

    int delta_p = dst->getPriority() - src->getPriority();

    src->setPriorityDelta(src->getPriorityDelta() - delta_p + 1);
    BranchItem *bi = src->getBranch();
    bi->getModel()->emitDataChanged(bi);
    bi->getModel()->setChanged();
    return true;
}
