/* This file is part of the KDE project
   SPDX-FileCopyrightText: 2011 Silvio Heinrich <plassy@web.de>

   SPDX-License-Identifier: LGPL-2.0-or-later
*/

#include "CropWidget.h"
#include "KoImageData.h"
#include "PictureShape.h"

#include <KoClipPath.h>

#include <QPainter>
#include <QResizeEvent>

qreal calcScale(const QSizeF &imgSize, const QSizeF viewSize, bool fitView)
{
    if (qFuzzyCompare(imgSize.width(), qreal(0)) || qFuzzyCompare(imgSize.height(), qreal(0)) || qFuzzyCompare(viewSize.width(), qreal(0))
        || qFuzzyCompare(viewSize.height(), qreal(0))) {
        return 1;
    }

    qreal viewAspect = viewSize.width() / viewSize.height();
    qreal imgAspect = imgSize.width() / imgSize.height();

    if (fitView) {
        if (viewAspect > imgAspect) {
            return viewSize.height() / imgSize.height();
        } else {
            return viewSize.width() / imgSize.width();
        }
    } else {
        if (viewAspect > imgAspect) {
            return viewSize.width() / imgSize.width();
        } else {
            return viewSize.height() / imgSize.height();
        }
    }
}

QRectF centerRectHorizontally(const QRectF &rect, const QSizeF viewSize)
{
    QSizeF diff = viewSize - rect.size();
    return QRectF(diff.width() / 2.0, rect.y(), rect.width(), rect.height());
}

bool compareRects(const QRectF &a, const QRectF &b, qreal epsilon)
{
    qreal x = qAbs(a.x() - b.x());
    qreal y = qAbs(a.y() - b.y());
    qreal w = qAbs(a.width() - b.width());
    qreal h = qAbs(a.height() - b.height());

    return x <= epsilon && y <= epsilon && w <= epsilon && h <= epsilon;
}

// ---------------------------------------------------------------- //

CropWidget::CropWidget(QWidget *parent)
    : QWidget(parent)
    , m_pictureShape(nullptr)
    , m_isMousePressed(false)
    , m_undoLast(false)
{
    setMinimumSize(100, 100);
    setMouseTracking(true);
}

void CropWidget::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event);

    if (!m_pictureShape || m_imageRect.isNull())
        return;

    QPainter painter(this);
    QImage image = m_pictureShape->imageData()->image();

    painter.translate(m_imageRect.topLeft());
    painter.scale(m_imageRect.width(), m_imageRect.height());

    painter.drawImage(QRectF(0, 0, 1, 1), image);

    painter.setBrush(QColor(0, 0, 0, 127));
    painter.setPen(Qt::NoPen);
    painter.drawPolygon(QPolygonF(QRectF(0, 0, 1, 1)).subtracted(m_selectionRect.getRect()));

    painter.setBrush(Qt::NoBrush);
    painter.setPen(QPen(this->palette().color(QPalette::Highlight), 0));
    painter.drawRect(m_selectionRect.getRect());

    painter.setBrush(QBrush(this->palette().color(QPalette::Highlight).darker()));
    for (int i = 0; i < m_selectionRect.getNumHandles(); ++i)
        painter.drawRect(m_selectionRect.getHandleRect(m_selectionRect.getHandleFlags(i)));

    KoClipPath *clipPath = m_pictureShape->clipPath();
    if (clipPath) {
        painter.scale(0.01, 0.01); // the path is defined in 100x100 equaling shapesize
        painter.setBrush(Qt::NoBrush);
        painter.setPen(QPen(Qt::red, 0));
        painter.drawPath(clipPath->path());
    }
}

void CropWidget::mousePressEvent(QMouseEvent *event)
{
    m_selectionRect.beginDragging(toUniformCoord(event->localPos()));
    m_isMousePressed = true;
}

void CropWidget::mouseMoveEvent(QMouseEvent *event)
{
    QPointF pos = toUniformCoord(event->localPos());
    SelectionRect::HandleFlags flags = m_selectionRect.getHandleFlags(pos);

    switch (flags) {
    case SelectionRect::TOP_HANDLE:
    case SelectionRect::BOTTOM_HANDLE:
        QWidget::setCursor(Qt::SizeVerCursor);
        break;

    case SelectionRect::LEFT_HANDLE:
    case SelectionRect::RIGHT_HANDLE:
        QWidget::setCursor(Qt::SizeHorCursor);
        break;

    case SelectionRect::LEFT_HANDLE | SelectionRect::TOP_HANDLE:
    case SelectionRect::RIGHT_HANDLE | SelectionRect::BOTTOM_HANDLE:
        QWidget::setCursor(Qt::SizeFDiagCursor);
        break;

    case SelectionRect::LEFT_HANDLE | SelectionRect::BOTTOM_HANDLE:
    case SelectionRect::RIGHT_HANDLE | SelectionRect::TOP_HANDLE:
        QWidget::setCursor(Qt::SizeBDiagCursor);
        break;

    case SelectionRect::INSIDE_RECT:
        QWidget::setCursor(Qt::SizeAllCursor);
        break;

    default:
        QWidget::setCursor(Qt::ArrowCursor);
        break;
    }

    if (m_isMousePressed) {
        m_selectionRect.doDragging(pos);
        update();
        emitCropRegionChanged();
    }
}

void CropWidget::mouseReleaseEvent(QMouseEvent *event)
{
    Q_UNUSED(event);
    m_selectionRect.finishDragging();
    m_isMousePressed = false;
    emitCropRegionChanged();
    m_undoLast = false; // we are done dragging
}

void CropWidget::resizeEvent(QResizeEvent *event)
{
    Q_UNUSED(event);
    calcImageRect();
}

void CropWidget::setPictureShape(PictureShape *shape)
{
    m_pictureShape = shape;

    calcImageRect();
    m_oldSelectionRect = shape->cropRect();
    m_selectionRect.setRect(shape->cropRect());
    m_selectionRect.setConstrainingRect(QRectF(0, 0, 1, 1));
    m_selectionRect.setHandleSize(0.04);
    // emit sigCropRegionChanged(shape->cropRect());
    update();
}

void CropWidget::setCropRect(const QRectF &rect)
{
    m_selectionRect.setRect(rect);
    emitCropRegionChanged();
}

void CropWidget::setKeepPictureProportion(bool keepProportion)
{
    qreal aspect = keepProportion ? (m_pictureShape->size().width() / m_pictureShape->size().height()) : 0.0;
    m_selectionRect.setConstrainingAspectRatio(aspect);
    emitCropRegionChanged();
}

void CropWidget::maximizeCroppedArea()
{
    m_selectionRect.setRect(QRectF(0, 0, 1, 1));
    emitCropRegionChanged();
}

QPointF CropWidget::toUniformCoord(const QPointF &coord) const
{
    QPointF result = coord - m_imageRect.topLeft();
    return QPointF(result.x() / m_imageRect.width(), result.y() / m_imageRect.height());
}

QPointF CropWidget::fromUniformCoord(const QPointF &coord) const
{
    return m_imageRect.topLeft() + QPointF(coord.x() * m_imageRect.width(), coord.y() * m_imageRect.height());
}

void CropWidget::emitCropRegionChanged()
{
    if (!compareRects(m_oldSelectionRect, m_selectionRect.getRect(), 0.01)) {
        m_oldSelectionRect = m_selectionRect.getRect();
        Q_EMIT sigCropRegionChanged(m_selectionRect.getRect(), m_undoLast);
        update();

        m_undoLast = m_isMousePressed;
    }
}

void CropWidget::calcImageRect()
{
    if (m_pictureShape) {
        QSizeF imageSize = m_pictureShape->imageData()->image().size();
        imageSize = imageSize * calcScale(imageSize, size(), true);
        m_imageRect = centerRectHorizontally(QRect(0, 0, imageSize.width(), imageSize.height()), size());
        m_selectionRect.setAspectRatio(m_imageRect.width() / m_imageRect.height());
    } else {
        m_imageRect = QRectF();
    }
}
