/*****************************************************************************
 *
 * Testmanager - Graphical Automation and Visualisation Tool
 *
 * Copyright (C) 2019  Florian Pose <fp@igh.de>
 *
 * This file is part of Testmanager.
 *
 * Testmanager 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 3 of the License, or (at your option)
 * any later version.
 *
 * Testmanager 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 Testmanager. If not, see <http://www.gnu.org/licenses/>.
 *
 ****************************************************************************/

#include "Legend.h"

#include "WidgetContainer.h"
#include "TabPage.h"
#include "MainWindow.h"
#include "StyleDialog.h"
#include "DataSlot.h"

#include <QMenu>
#include <QAction>
#include <QContextMenuEvent>
#include <QPainter>
#include <QDebug>

#define MIN_SIZE (21)
#define RESIZE_AREA_WIDTH (5)

/****************************************************************************/

Legend::Legend(
        WidgetContainer *container,
        QWidget *parent
        ):
    QFrame(parent),
    container(container),
    alignment(Qt::AlignLeft),
    resizeDir(None),
    moving(false)
{
    setFocusPolicy(Qt::StrongFocus); // accept key events

    setMouseTracking(true);

    area[0].setWidth(RESIZE_AREA_WIDTH); // left
    area[1].setHeight(RESIZE_AREA_WIDTH); // top
    area[2].setWidth(RESIZE_AREA_WIDTH); // right
    area[3].setHeight(RESIZE_AREA_WIDTH); // bottom
}

/****************************************************************************/

void Legend::editModeChanged()
{
    unsetCursor();
    update();
}

/****************************************************************************/

void Legend::setAlignment(Qt::AlignmentFlag flag)
{
    alignment = flag;
    update();
}

/*****************************************************************************
 * protected
 ****************************************************************************/

void Legend::resizeEvent(QResizeEvent *)
{
    // left, right
    area[0].setHeight(contentsRect().height());
    area[2].setHeight(contentsRect().height());
    area[2].moveRight(contentsRect().right());

    // top, bottom
    area[1].setWidth(contentsRect().width());
    area[3].setWidth(contentsRect().width());
    area[3].moveBottom(contentsRect().bottom());
}

/****************************************************************************/

void Legend::contextMenuEvent(QContextMenuEvent *event)
{
    if (!container->getTabPage()->getMainWindow()->getEditMode()) {
        event->ignore();
        return;
    }

    QMenu menu, *subMenu;
    QAction *action;

    action = new QAction(this);
    action->setText(tr("Hide"));
    action->setShortcut(tr("Del"));
    connect(action, SIGNAL(triggered()), this, SLOT(hideLegend()));
    menu.addAction(action);

    subMenu = new QMenu(this);
    subMenu->setTitle(tr("Text alignment"));
    menu.addMenu(subMenu);

    action = new QAction(this);
    action->setText(tr("Left"));
    action->setEnabled(alignment != Qt::AlignLeft);
    action->setIcon(QIcon(":/images/format-justify-left.svg"));
    connect(action, SIGNAL(triggered()), this, SLOT(alignLeft()));
    subMenu->addAction(action);

    action = new QAction(this);
    action->setText(tr("Center"));
    action->setEnabled(alignment != Qt::AlignHCenter);
    action->setIcon(QIcon(":/images/format-justify-center.svg"));
    connect(action, SIGNAL(triggered()), this, SLOT(alignCenter()));
    subMenu->addAction(action);

    action = new QAction(this);
    action->setText(tr("Right"));
    action->setEnabled(alignment != Qt::AlignRight);
    action->setIcon(QIcon(":/images/format-justify-right.svg"));
    connect(action, SIGNAL(triggered()), this, SLOT(alignRight()));
    subMenu->addAction(action);

    action = new QAction(this);
    action->setText(tr("Edit stylesheet..."));
    action->setIcon(QIcon(":/images/stylesheet.svg"));
    connect(action, SIGNAL(triggered()), this, SLOT(editStyleSheet()));
    menu.addAction(action);

    menu.exec(event->globalPos());
}

/****************************************************************************/

void Legend::mouseMoveEvent(QMouseEvent *event)
{
    if (!container->getTabPage()->getMainWindow()->getEditMode()) {
        event->ignore();
        return;
    }

    bool over[NumHandles];
    for (int i = 0; i < NumHandles; i++) {
        over[i] = area[i].contains(event->pos());
    }

    if (!resizeDir and !moving) {
        if (over[0] && !over[1] && !over[3]) {
            setCursor(Qt::SizeHorCursor);
        }
        else if (over[1] && !over[2] && !over[0]) {
            setCursor(Qt::SizeVerCursor);
        }
        else if (over[2] && !over[3] && !over[1]) {
            setCursor(Qt::SizeHorCursor);
        }
        else if (over[3] && !over[0] && !over[2]) {
            setCursor(Qt::SizeVerCursor);
        }
        else if (over[0] && over[1]) {
            setCursor(Qt::SizeFDiagCursor);
        }
        else if (over[1] && over[2]) {
            setCursor(Qt::SizeBDiagCursor);
        }
        else if (over[2] && over[3]) {
            setCursor(Qt::SizeFDiagCursor);
        }
        else if (over[3] && over[0]) {
            setCursor(Qt::SizeBDiagCursor);
        }
        else {
            setCursor(Qt::SizeAllCursor);
        }
    }
    else { // resizing or moving

        int gridStep(container->getTabPage()->getMainWindow()->getGridStep());
        QPoint absDiff(event->globalPos() - startGlobalPos);

        QPoint absTopLeft(startRect.topLeft() + absDiff);
        QPoint targetTopLeft;
        if (event->modifiers() & Qt::ShiftModifier) {
            targetTopLeft = absTopLeft;
        }
        else {
            // snap top/left to grid
            targetTopLeft = QPoint(
                    qRound((double) absTopLeft.x() / gridStep) * gridStep,
                    qRound((double) absTopLeft.y() / gridStep) * gridStep
                    );
        }
        QPoint topLeftDiff(targetTopLeft - geometry().topLeft());

        QPoint absBottomRight(startRect.bottomRight() + absDiff);
        QPoint targetBottomRight;
        if (event->modifiers() & Qt::ShiftModifier) {
            targetBottomRight = absBottomRight;
        }
        else {
            // snap bottom/right to grid
            targetBottomRight = QPoint(
                    qRound((double) absBottomRight.x() / gridStep) * gridStep,
                    qRound((double) absBottomRight.y() / gridStep) * gridStep
                    );
        }
        QPoint bottomRightDiff(targetBottomRight - geometry().bottomRight());

#if 0
        if (moving || resizeDir) {
            qDebug() << absDiff << startRect;
            qDebug() << "tl" << absTopLeft << targetTopLeft << topLeftDiff;
            qDebug() << "br" << absBottomRight << targetBottomRight
                << bottomRightDiff;
        }
#endif

        if (resizeDir & Left) {
            adjustLeft(topLeftDiff.x());
            container->legendMoved();
        }

        if (resizeDir & Top) {
            adjustTop(topLeftDiff.y());
            container->legendMoved();
        }

        if (resizeDir & Right) {
            adjustWidth(bottomRightDiff.x());
        }

        if (resizeDir & Bottom) {
            adjustHeight(bottomRightDiff.y());
        }

        if (moving) {
            move(pos() + topLeftDiff);
            container->legendMoved();
        }
    }
}

/****************************************************************************/

void Legend::mousePressEvent(QMouseEvent *event)
{
    if (!container->getTabPage()->getMainWindow()->getEditMode()) {
        event->ignore();
        return;
    }

    if (event->button() == Qt::LeftButton) {

        resizeDir = None;
        for (int i = 0; i < NumHandles; i++) {
            if (area[i].contains(event->pos())) {
                resizeDir |= (1 << i);
            }
        }

        if (!resizeDir) {
            moving = true;
        }

        startGlobalPos = event->globalPos();
        startRect = geometry();
        update();
    }
}

/****************************************************************************/

void Legend::mouseReleaseEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton) {
        resizeDir = None;
        moving = false;
        update();
    }
}

/****************************************************************************/

void Legend::keyPressEvent(QKeyEvent *event)
{
    switch (event->key()) {
        case Qt::Key_Up:
        case Qt::Key_Down:
        case Qt::Key_Left:
        case Qt::Key_Right:
            if (container->getTabPage()->getMainWindow()->getEditMode()) {
                moveLegend(event);
                return;
            }
            break;

        case Qt::Key_Delete:
            if (container->getTabPage()->getMainWindow()->getEditMode()) {
                hideLegend();
                return;
            }
            break;
    }

    return QFrame::keyPressEvent(event);
}

/****************************************************************************/

void Legend::paintEvent(QPaintEvent *event)
{
    QFrame::paintEvent(event);
    QPainter painter(this);

    QColor fillColor;

    if (container->getTabPage()->getMainWindow()->getEditMode()) {
        fillColor = Qt::darkRed;
        fillColor.setAlpha(64);
        painter.fillRect(contentsRect(), fillColor);
    }

    bool colorsSupported(container->getPlugin()->colorsSupported());

    QFontMetrics fm(painter.fontMetrics());
    QRect lineRect(contentsRect());
    lineRect.setHeight(fm.height());
    QRect colorRect(lineRect);
    colorRect.setWidth(colorRect.height());
    colorRect = colorRect.adjusted(2, 2, -2, -2);

    if (colorsSupported) {
        lineRect.setLeft(lineRect.left() + lineRect.height());
    }

    QList<const DataSlot *> dataSlots(
            container->getSlotModel()->getDataSlots());

    foreach (const DataSlot *slot, dataSlots) {

        if (colorsSupported) {
            painter.fillRect(colorRect, slot->color);
            colorRect.moveTop(colorRect.top() + lineRect.height());
        }

        QString text;
        if (slot->alias.isEmpty()) {
            text = slot->url.toString();
        }
        else {
            text = slot->alias;
        }
        text = fm.elidedText(text, Qt::ElideMiddle, lineRect.width());
        painter.drawText(lineRect, alignment, text);
        lineRect.moveTop(lineRect.top() + lineRect.height());
        if (lineRect.top() >= contentsRect().height()) {
            break;
        }
    }
}

/*****************************************************************************
 * private
 ****************************************************************************/

void Legend::adjustLeft(int dx)
{
    if (width() - dx < MIN_SIZE) {
        dx = width() - MIN_SIZE;
    }

    setGeometry(x() + dx, y(), width() - dx, height());
}

/****************************************************************************/

void Legend::adjustTop(int dy)
{
    if (height() - dy < MIN_SIZE) {
        dy = height() - MIN_SIZE;
    }

    setGeometry(x(), y() + dy, width(), height() - dy);
}

/****************************************************************************/

void Legend::adjustWidth(int dw)
{
    if (width() + dw < MIN_SIZE) {
        dw = MIN_SIZE - width();
    }

    resize(width() + dw, height());
}

/****************************************************************************/

void Legend::adjustHeight(int dh)
{
    if (height() + dh < MIN_SIZE) {
        dh = MIN_SIZE - height();
    }

    resize(width(), height() + dh);
}

/*****************************************************************************
 * private slots
 ****************************************************************************/

void Legend::hideLegend()
{
    container->setLegend(false);
}

/****************************************************************************/

void Legend::moveLegend(QKeyEvent *event)
{
    int dx = 0, dy = 0, step;

    if (event->modifiers() & Qt::ShiftModifier) {
        step = 50;
    }
    else if (event->modifiers() & Qt::AltModifier) {
        step = 1;
    }
    else {
        step = 10;
    }

    switch (event->key()) {
        case Qt::Key_Left:
            dx = -step;
            break;
        case Qt::Key_Right:
            dx = step;
            break;
        case Qt::Key_Up:
            dy = -step;
            break;
        case Qt::Key_Down:
            dy = step;
            break;
    }

    move(pos().x() + dx, pos().y() + dy);
    container->legendMoved();
}

/****************************************************************************/

void Legend::editStyleSheet()
{
    StyleDialog dialog(this);
    dialog.exec();
}

/****************************************************************************/

void Legend::alignLeft()
{
    setAlignment(Qt::AlignLeft);
}

/****************************************************************************/

void Legend::alignCenter()
{
    setAlignment(Qt::AlignHCenter);
}

/****************************************************************************/

void Legend::alignRight()
{
    setAlignment(Qt::AlignRight);
}

/****************************************************************************/
