/***
   This file is part of fly-ocr.

   Copyright (C) 2016-2020 Sandro Mani <manisandro@gmail.com>
   Copyright (C) 2021 LLC "RusBITech-Astra" (https://astralinux.ru)

    Based on gImageReader source code by Sandro Mani under the GNU
    General Public License (GPL) version 3 or later

   Foobar 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.

   Foobar 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 fly-ocr. If not, see <https://www.gnu.org/licenses/>.
***/

#include "mainwindow.h"
#include "configsettings.h"
#include "displayer.h"
#include "displayrenderer.h"
#include "sourcemanager.h"
#include "utils.h"
#include "mainwindow_ui.h"
#include <cmath>
#include <QFileDialog>
#include <QFuture>
#include <QGraphicsPixmapItem>
#include <QGraphicsSceneDragDropEvent>
#include <QMessageBox>
#include <QMouseEvent>
#include <QScrollBar>
#include <QWheelEvent>
#include <QtConcurrent/QtConcurrentRun>
#include <QtConcurrent/QtConcurrentMap>


class GraphicsScene : public QGraphicsScene
{
public:
    using QGraphicsScene::QGraphicsScene;

protected:
    void dragEnterEvent(QGraphicsSceneDragDropEvent* event) override
    {
        if (Utils::handleSourceDragEvent(event->mimeData())) {
            event->acceptProposedAction();
        }
    }
    void dragMoveEvent(QGraphicsSceneDragDropEvent* /*event*/) override {}
    void dropEvent(QGraphicsSceneDragDropEvent* event) override
    {
        Utils::handleSourceDropEvent(event->mimeData());
    }
};

Displayer::Displayer(const MainWindowUi* _ui, QWidget* parent)
    : QGraphicsView(parent), m_ui(_ui)
{
    m_scene = new GraphicsScene();
    setScene(m_scene);
    setBackgroundBrush(Qt::gray);
    setRenderHint(QPainter::Antialiasing);
    setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
    setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);

    m_ui->actionRotateCurrentPage->setData(static_cast<int>(RotateMode::CurrentPage));
    m_ui->actionRotateAllPages->setData(static_cast<int>(RotateMode::AllPages));

    m_renderTimer.setSingleShot(true);
    m_scaleTimer.setSingleShot(true);

    m_ui->actionRotateLeft->setData(270.0);
    m_ui->actionRotateRight->setData(90.0);

    m_ui->actionRotateCurrentPage->setChecked(true);

    connect(m_ui->menuRotation, &QMenu::triggered, this, &Displayer::setRotateMode);
    connect(m_ui->actionRotateLeft, &QAction::triggered, this, &Displayer::rotate90);
    connect(m_ui->actionRotateRight, &QAction::triggered, this, &Displayer::rotate90);
    connect(m_ui->spinBoxRotation, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &Displayer::setAngle);
    connect(m_ui->spinBoxPage, qOverload<int>(&QSpinBox::valueChanged), this, &Displayer::queueRenderImage);
    connect(m_ui->spinBoxBrightness, qOverload<int>(&QSpinBox::valueChanged), this, &Displayer::queueRenderImage);
    connect(m_ui->spinBoxContrast, qOverload<int>(&QSpinBox::valueChanged), this, &Displayer::queueRenderImage);
    connect(m_ui->spinBoxResolution, qOverload<int>(&QSpinBox::valueChanged), this, &Displayer::queueRenderImage);
    connect(m_ui->checkBoxInvertColors, &QCheckBox::toggled, this, &Displayer::queueRenderImage);
    connect(m_ui->actionZoomIn, &QAction::triggered, this, &Displayer::zoomIn);
    connect(m_ui->actionZoomOut, &QAction::triggered, this, &Displayer::zoomOut);
    connect(m_ui->actionBestFit, &QAction::triggered, this, &Displayer::zoomFit);
    connect(m_ui->actionOriginalSize, &QAction::triggered, this, &Displayer::zoomOriginal);
    connect(m_ui->actionFitWidth, &QAction::triggered, this, &Displayer::zoomFitWidth);
    connect(&m_renderTimer, &QTimer::timeout, this, &Displayer::renderImage);
    connect(&m_scaleTimer, &QTimer::timeout, this, &Displayer::scaleImage);
    connect(&m_scaleWatcher, &QFutureWatcher<QImage>::finished, this, [this] { setScaledImage(m_scaleWatcher.future().result()); });
//    connect(m_ui->listWidgetThumbnails, &QListWidget::currentRowChanged, [this](int idx) { m_ui->spinBoxPage->setValue(idx + 1); });
//    connect(m_ui->checkBoxThumbnails, &QCheckBox::toggled, this, &Displayer::thumbnailsToggled);
//    connect(m_ui->spinBoxPage, qOverload<int>(&QSpinBox::valueChanged), this, [this](int page) {
//        QSignalBlocker blocker(m_ui->listWidgetThumbnails);
//        m_ui->listWidgetThumbnails->setCurrentRow(page - 1);
//    });
    connect(horizontalScrollBar(), &QScrollBar::valueChanged, this, &Displayer::checkViewportChanged);
    connect(horizontalScrollBar(), &QScrollBar::rangeChanged, this, &Displayer::checkViewportChanged);
    connect(verticalScrollBar(), &QScrollBar::valueChanged, this, &Displayer::checkViewportChanged);
    connect(verticalScrollBar(), &QScrollBar::rangeChanged, this, &Displayer::checkViewportChanged);

//    ADD_SETTING(SwitchSetting("thumbnails", m_ui->checkBoxThumbnails, true));


    m_scaleGroup = new QActionGroup(this);
    m_scaleGroup->addAction(m_ui->actionOriginalSize);
    m_scaleGroup->addAction(m_ui->actionBestFit);
    m_scaleGroup->addAction(m_ui->actionFitWidth);
    m_scaleGroup->setExclusive(true);
}

Displayer::~Displayer()
{
    delete m_scene;
}

void Displayer::setTool(DisplayerTool* tool)
{
    m_tool = tool;
}

bool Displayer::setPage(std::shared_ptr<SourceDocumentPage> page)
{
    if (m_page == page)
        return true;

    m_page = page;

    m_scaleTimer.stop();
    m_scaleWatcher.waitForFinished();

    if (m_tool) {
        m_tool->reset();
    }

    m_renderTimer.stop();
    if (m_imageItem) {
        m_scene->removeItem(m_imageItem);
        delete m_imageItem;
        m_imageItem = nullptr;
    }

    m_pixmap = QPixmap();
    Utils::setSpinBlocked(m_ui->spinBoxRotation, 0);
    Utils::setSpinBlocked(m_ui->spinBoxBrightness, 0);
    Utils::setSpinBlocked(m_ui->spinBoxContrast, 0);
    Utils::setSpinBlocked(m_ui->spinBoxResolution, 100);
    m_ui->checkBoxInvertColors->blockSignals(true);
    m_ui->checkBoxInvertColors->setChecked(false);
    m_ui->checkBoxInvertColors->blockSignals(false);
    m_ui->actionZoomIn->setEnabled(true);
    m_ui->actionZoomOut->setEnabled(true);
    unsetCursor();

    if (!m_page || m_page->renderer->isEmpty()) {
        emit imageChanged();
        return false;
    }

    Utils::setSpinBlocked(m_ui->spinBoxResolution, m_page->resolution);
    Utils::setSpinBlocked(m_ui->spinBoxBrightness, m_page->brightness);
    Utils::setSpinBlocked(m_ui->spinBoxContrast, m_page->contrast);
    Utils::setSpinBlocked(m_ui->spinBoxRotation, m_page->angle);
    m_ui->checkBoxInvertColors->blockSignals(true);
    m_ui->checkBoxInvertColors->setChecked(m_page->invert);
    m_ui->checkBoxInvertColors->blockSignals(false);

    m_imageItem = new QGraphicsPixmapItem();
    m_imageItem->setTransformationMode(Qt::SmoothTransformation);
    m_scene->addItem(m_imageItem);
    if (!renderImage()) {
        QMessageBox::critical(this, tr("Failed to load image"), tr("The file might not be an image or be corrupt:\n%1").arg(m_page->displayName));
        m_page.reset();
        return false;
    }
    return true;
}

bool Displayer::setup(const int* page, const int* resolution, const double* angle)
{
    bool changed = false;
    if (page) {
        changed |= (*page + 1) != m_ui->spinBoxPage->value();
        //Utils::setSpinBlocked(m_ui->spinBoxPage, *page + 1);
        m_ui->spinBoxPage->setValue(*page + 1);
    }
    if (resolution) {
        changed |= *resolution != m_ui->spinBoxResolution->value();
        Utils::setSpinBlocked(m_ui->spinBoxResolution, *resolution);
    }
    if (changed && !renderImage()) {
        return false;
    }
    if (angle) {
        setAngle(*angle);
    }
    return true;
}

void Displayer::queueRenderImage()
{
    m_renderTimer.start(500);
}

bool Displayer::renderImage()
{
    if (!m_page || m_page->renderer->isEmpty()) {
        return false;
    }
    //int page = m_ui->spinBoxPage->value();

    m_scaleTimer.stop();
    m_scaleWatcher.waitForFinished();

    //int oldResolution = m_page->resolution;
    //int oldPage = m_page->resolution;
    //Source* oldSource = m_currentSource;


//    Utils::setSpinBlocked(m_ui->spinBoxResolution, m_page->resolution);
//    Utils::setSpinBlocked(m_ui->spinBoxBrightness, m_page->brightness);
//    Utils::setSpinBlocked(m_ui->spinBoxContrast, m_page->contrast);
//    m_ui->checkBoxInvertColors->blockSignals(true);
//    m_ui->checkBoxInvertColors->setChecked(m_page->invert);
//    m_ui->checkBoxInvertColors->blockSignals(false);


    // Update source struct
//    m_currentSource->page = m_pageMap[m_ui->spinBoxPage->value()].second;
    m_page->brightness = m_ui->spinBoxBrightness->value();
    m_page->contrast = m_ui->spinBoxContrast->value();
    m_page->resolution = m_ui->spinBoxResolution->value();
    m_page->invert = m_ui->checkBoxInvertColors->isChecked();

    // Notify tools about changes
    /*if (m_tool) {
        if (m_currentSource != oldSource || m_currentSource->page != oldPage) {
            m_tool->pageChanged();
        }
        if (oldResolution != m_currentSource->resolution) {
            double factor = double(m_currentSource->resolution) / double(oldResolution);
            m_tool->resolutionChanged(factor);
        }
    }*/

    Utils::setSpinBlocked(m_ui->spinBoxRotation, m_page->angle);

    // Render new image
//    DisplayRenderer* renderer = m_sourceRenderers[m_currentSource];
//    if (!renderer) {
//        return false;
//    }
    QImage image = m_page->renderer->render(m_page->page, m_page->resolution);
    if (image.isNull()) {
        return false;
    }
    m_page->renderer->adjustImage(image, m_page->brightness, m_page->contrast, m_page->invert);
    m_pixmap = QPixmap::fromImage(image);
    m_imageItem->setPixmap(m_pixmap);
    m_imageItem->setScale(1.);
    m_imageItem->setTransformOriginPoint(m_imageItem->boundingRect().center());
    m_imageItem->setPos(m_imageItem->pos() - m_imageItem->sceneBoundingRect().center());
    m_scene->setSceneRect(m_imageItem->sceneBoundingRect());
    centerOn(sceneRect().center());
    setAngle(m_ui->spinBoxRotation->value());
    if (m_scale < 1.0) {
        m_scaleTimer.start(100);
    }
    emit imageChanged();
    return true;
}

int Displayer::currentPage() const
{
    return m_ui->spinBoxPage->value() - 1;
}

int Displayer::pageCount() const
{
    return m_ui->spinBoxPage->maximum();
}

double Displayer::currentAngle() const
{
    return m_ui->spinBoxRotation->value();
}

double Displayer::currentScale() const
{
    return m_scale;
}

int Displayer::currentResolution() const
{
    return m_ui->spinBoxResolution->value();
}

QString Displayer::currentImage() const
{
    return m_page ? m_page->path : QString();
}

bool Displayer::hasMultipleOCRAreas()
{
    return m_tool->hasMultipleOCRAreas();
}

QList<QImage> Displayer::getOCRAreas()
{
    return m_tool->getOCRAreas();
}

bool Displayer::allowAutodetectOCRAreas() const
{
    return m_tool->allowAutodetectOCRAreas();
}

void Displayer::setCursor(const QCursor& cursor)
{
    viewport()->setCursor(cursor);
}

void Displayer::unsetCursor()
{
    viewport()->unsetCursor();
}

void Displayer::autodetectOCRAreas()
{
    m_tool->autodetectOCRAreas();
}

void Displayer::checkViewportChanged()
{
    if (m_viewportTransform != viewportTransform()) {
        m_viewportTransform = viewportTransform();
        emit viewportChanged();
    }
}

void Displayer::setZoom(Zoom action, ViewportAnchor anchor)
{
    if (!m_imageItem) {
        return;
    }
    m_scaleTimer.stop();
    m_scaleWatcher.waitForFinished();
    setUpdatesEnabled(false);

    QRectF bb = m_imageItem->sceneBoundingRect();
    double fit = std::min((viewport()->width() - 10) / bb.width(), (viewport()->height() - 10) / bb.height());
    double fitWidth = (viewport()->width() - 20) / bb.width();

    if (action == Zoom::Original) {
        m_scale = 1.0;
    } else if (action == Zoom::In) {
        m_scale = std::min(10., m_scale * 1.25);
    } else if (action == Zoom::Out) {
        m_scale = std::max(0.05, m_scale * 0.8);
    }

    /*m_ui->actionBestFit->setChecked(false);
    m_ui->actionFitWidth->setChecked(false);

    if (action == Zoom::In || action == Zoom::Out) {
        if (m_scale / fit >= 0.9 && m_scale / fit <= 1.09)
            m_ui->actionBestFit->setChecked(true);
        else if (m_scale / fitWidth >= 0.9 && m_scale / fitWidth <= 1.09)
            m_ui->actionFitWidth->setChecked(true);
    }*/

    if (action == Zoom::FitPage) {
        m_scale = fit;
        setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
        setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
        //m_ui->actionBestFit->setChecked(true);
    } else if (action == Zoom::FitWidth) {
        m_scale = fitWidth;
        setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
        setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
        //m_ui->actionFitWidth->setChecked(true);
    } else {
        setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
        setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
    }

    m_ui->actionOriginalSize->setChecked(m_scale == 1.0);
    m_ui->actionZoomIn->setEnabled(m_scale < 10);
    m_ui->actionZoomOut->setEnabled(m_scale > 0.01);

    setTransformationAnchor(anchor);
    QTransform t;
    t.scale(m_scale, m_scale);
    setTransform(t);
    if (m_scale < 1.0) {
        m_scaleTimer.start(100);
    } else {
        m_imageItem->setPixmap(m_pixmap);
        m_imageItem->setScale(1.);
        m_imageItem->setTransformOriginPoint(m_imageItem->boundingRect().center());
        m_imageItem->setPos(m_imageItem->pos() - m_imageItem->sceneBoundingRect().center());
    }
    setUpdatesEnabled(true);
    update();
    checkViewportChanged();
}

void Displayer::setAngle(double angle)
{
    if (m_imageItem) {
        angle = angle < 0.0 ? angle + 360.0 : angle >= 360.0 ? angle - 360.0 : angle,
        Utils::setSpinBlocked(m_ui->spinBoxRotation, angle);
        double delta = angle - m_page->angle;
        if (m_rotateMode == RotateMode::CurrentPage) {
            m_page->angle = angle;
        } else if (delta != 0) {
            MAIN->sourceManager()->rotateAll(delta);
        }
        m_imageItem->setRotation(angle);
        if (m_tool && delta != 0) {
            m_tool->rotationChanged(delta);
        }
        m_scene->setSceneRect(m_imageItem->sceneBoundingRect());
        if (m_ui->actionBestFit->isChecked()) {
            setZoom(Zoom::FitPage);
        } else if (m_ui->actionFitWidth->isChecked()) {
            setZoom(Zoom::FitWidth);
        } else if (m_ui->actionOriginalSize) {
            setZoom(Zoom::Original);
        }
    }
}

void Displayer::rotate90()
{
    setAngle(m_ui->spinBoxRotation->value() + qobject_cast<QAction*>(QObject::sender())->data().toDouble());
}

void Displayer::resizeEvent(QResizeEvent* event)
{
    QGraphicsView::resizeEvent(event);
    if (m_ui->actionBestFit->isChecked()) {
        setZoom(Zoom::FitPage);
    } else if (m_ui->actionFitWidth->isChecked()) {
        setZoom(Zoom::FitWidth);
    }
}

void Displayer::keyPressEvent(QKeyEvent* event)
{
    if (event->key() == Qt::Key_PageUp) {
        m_ui->spinBoxPage->setValue(m_ui->spinBoxPage->value() - 1);
        event->accept();
    } else if (event->key() == Qt::Key_PageDown) {
        m_ui->spinBoxPage->setValue(m_ui->spinBoxPage->value() + 1);
        event->accept();
    } else {
        QGraphicsView::keyPressEvent(event);
    }
}

void Displayer::mousePressEvent(QMouseEvent* event)
{
    if (event->button() == Qt::MiddleButton) {
        m_panPos = event->pos();
    } else {
        event->ignore();
        QGraphicsView::mousePressEvent(event);
        if (!event->isAccepted() && m_tool && m_page) {
            m_tool->mousePressEvent(event);
        }
    }
}

void Displayer::mouseMoveEvent(QMouseEvent* event)
{
    if ((event->buttons() & Qt::MiddleButton) == Qt::MiddleButton) {
        QPoint delta = event->pos() - m_panPos;
        horizontalScrollBar()->setValue( horizontalScrollBar()->value() - delta.x() );
        verticalScrollBar()->setValue( verticalScrollBar()->value() - delta.y() );
        m_panPos = event->pos();
    } else {
        event->ignore();
        QGraphicsView::mouseMoveEvent(event);
        if (!event->isAccepted() && m_tool && m_page) {
            m_tool->mouseMoveEvent(event);
        }
    }
}

void Displayer::mouseReleaseEvent(QMouseEvent* event)
{
    event->ignore();
    QGraphicsView::mouseReleaseEvent(event);
    if (!event->isAccepted() && m_tool && m_page) {
        m_tool->mouseReleaseEvent(event);
    }
}

void Displayer::wheelEvent(QWheelEvent* event)
{
    if (event->modifiers() & Qt::ControlModifier) {
        setZoom(event->angleDelta().y() > 0 ? Zoom::In : Zoom::Out, QGraphicsView::AnchorUnderMouse);
        event->accept();
    } else if (event->modifiers() & Qt::ShiftModifier) {
        QScrollBar* hscroll = horizontalScrollBar();
        if (event->angleDelta().y() < 0) {
            hscroll->setValue(hscroll->value() + hscroll->singleStep());
        } else {
            hscroll->setValue(hscroll->value() - hscroll->singleStep());
        }
        event->accept();
    } else {
        QGraphicsView::wheelEvent(event);
    }
}

QPointF Displayer::mapToSceneClamped(const QPoint& p) const
{
    QPointF q = mapToScene(p);
    QRectF bb = m_imageItem->sceneBoundingRect();
    q.rx() = std::min(std::max(bb.x(), q.x()), bb.x() + bb.width());
    q.ry() = std::min(std::max(bb.y(), q.y()), bb.y() + bb.height());
    return q;
}

void Displayer::setRotateMode(QAction* action)
{
    m_rotateMode = static_cast<RotateMode>(action->data().value<int>());
    m_ui->toolButtonRotation->setIcon(action->icon());
}

QImage Displayer::getImage(const QRectF& rect)
{
    QImage image(rect.width(), rect.height(), QImage::Format_RGB32);
    image.fill(Qt::black);
    QPainter painter(&image);
    painter.setRenderHint(QPainter::SmoothPixmapTransform);
    QTransform t;
    t.translate(-rect.x(), -rect.y());
    t.rotate(m_ui->spinBoxRotation->value());
    t.translate(-0.5 * m_pixmap.width(), -0.5 * m_pixmap.height());
    painter.setTransform(t);
    painter.drawPixmap(0, 0, m_pixmap);
    return image;
}

QRectF Displayer::getSceneBoundingRect() const
{
    // We cannot use m_imageItem->sceneBoundingRect() since its pixmap
    // can currently be downscaled and therefore have slightly different
    // proportions.
    int width = m_pixmap.width();
    int height = m_pixmap.height();
    QRectF rect(width * -0.5, height * -0.5, width, height);
    QTransform transform;
    transform.rotate(m_ui->spinBoxRotation->value());
    return transform.mapRect(rect);
}

void Displayer::setBlockAutoscale(bool block)
{
    m_scaleTimer.blockSignals(block);
    if (!block) {
        m_scaleTimer.start(100);
    }
}

void Displayer::scaleImage()
{
    double resolution = m_scale * m_page->resolution;
    int brightness = m_page->brightness;
    int contrast = m_page->contrast;
    bool invert = m_page->invert;
    QFuture<QImage> future = QtConcurrent::run([ = ] {
        if (!m_page || m_page->renderer->isEmpty())
        {
            return QImage();
        }
        QImage image = m_page->renderer->render(m_page->page, resolution);
        if (!image.isNull())
        {
            m_page->renderer->adjustImage(image, brightness, contrast, invert);
        }
        return image;
    });
    m_scaleWatcher.setFuture(future);
}

void Displayer::setScaledImage(const QImage &image)
{
    if (!image.isNull() && m_imageItem) {
        m_imageItem->setPixmap(QPixmap::fromImage(image));
        m_imageItem->setScale(1.0 / m_scale);
        m_imageItem->setTransformOriginPoint(m_imageItem->boundingRect().center());
        m_imageItem->setPos(m_imageItem->pos() - m_imageItem->sceneBoundingRect().center());
    }
}

void Displayer::zoomIn()
{
    if (m_scaleGroup->checkedAction())
        m_scaleGroup->checkedAction()->setChecked(false);
    setZoom(Zoom::In);
}

void Displayer::zoomOut()
{
    if (m_scaleGroup->checkedAction())
        m_scaleGroup->checkedAction()->setChecked(false);
    setZoom(Zoom::Out);
}

void Displayer::zoomFit()
{
    setZoom(Zoom::FitPage);
}

void Displayer::zoomOriginal()
{
    setZoom(Zoom::Original);
}

void Displayer::zoomFitWidth()
{
    setZoom(Zoom::FitWidth);
}

///////////////////////////////////////////////////////////////////////////////

DisplayerSelection::DisplayerSelection(DisplayerTool* tool, const QPointF& anchor)
    : QGraphicsRectItem(QRectF(anchor, anchor)), m_tool(tool), m_anchor(anchor), m_point(anchor)
{
    setAcceptHoverEvents(true);
    setZValue(10);
}

void DisplayerSelection::setAnchorAndPoint(const QPointF& anchor, const QPointF& point)
{
    m_anchor = anchor;
    m_point = point;
    setRect(QRectF(m_anchor, m_point).normalized());
}

void DisplayerSelection::setMinimumRect(const QRectF& rect)
{
    m_minRect = rect;
}

void DisplayerSelection::setPoint(const QPointF& point)
{
    m_point = point;
    setRect(QRectF(m_anchor, m_point).normalized());
}

void DisplayerSelection::rotate(const QTransform& transform)
{
    m_anchor = transform.map(m_anchor);
    m_point = transform.map(m_point);
    setRect(QRectF(m_anchor, m_point).normalized());
}

void DisplayerSelection::scale(double factor)
{
    m_anchor *= factor;
    m_point *= factor;
}

void DisplayerSelection::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
{
    QColor c = QPalette().highlight().color();
    setBrush(QColor(c.red(), c.green(), c.blue(), 63));
    QPen pen;
    pen.setColor(c);
    pen.setWidth(1 / m_tool->displayer()->currentScale());
    setPen(pen);

    painter->setRenderHint(QPainter::Antialiasing, false);
    QGraphicsRectItem::paint(painter, option, widget);
    painter->setRenderHint(QPainter::Antialiasing, true);
}

void DisplayerSelection::hoverMoveEvent(QGraphicsSceneHoverEvent* event)
{
    QPointF p = event->pos();
    QRectF r = rect();
    double tol = 10.0 / m_tool->displayer()->currentScale();

    bool left = std::abs(r.x() - p.x()) < tol;
    bool right = std::abs(r.x() + r.width() - p.x()) < tol;
    bool top = std::abs(r.y() - p.y()) < tol;
    bool bottom = std::abs(r.y() + r.height() - p.y()) < tol;

    if ((top && left) || (bottom && right)) {
        setCursor(Qt::SizeFDiagCursor);
    } else if ((top && right) || (bottom && left)) {
        setCursor(Qt::SizeBDiagCursor);
    } else if (top || bottom) {
        setCursor(Qt::SizeVerCursor);
    } else if (left || right) {
        setCursor(Qt::SizeHorCursor);
    } else {
        unsetCursor();
    }
}

void DisplayerSelection::mousePressEvent(QGraphicsSceneMouseEvent* event)
{
    QPointF p = event->pos();
    double tol = 10.0 / m_tool->displayer()->currentScale();
    m_resizeHandlers.clear();
    m_resizeOffset = QPointF(0.0, 0.0);
    if (std::abs(m_point.x() - p.x()) < tol) { // pointx
        m_resizeHandlers.append(resizePointX);
        m_resizeOffset.setX(event->pos().x() - m_point.x());
    } else if (std::abs(m_anchor.x() - p.x()) < tol) { // anchorx
        m_resizeHandlers.append(resizeAnchorX);
        m_resizeOffset.setX(event->pos().x() - m_anchor.x());
    }
    if (std::abs(m_point.y() - p.y()) < tol) { // pointy
        m_resizeHandlers.append(resizePointY);
        m_resizeOffset.setY(event->pos().y() - m_point.y());
    } else if (std::abs(m_anchor.y() - p.y()) < tol) { // anchory
        m_resizeHandlers.append(resizeAnchorY);
        m_resizeOffset.setY(event->pos().y() - m_anchor.y());
    }
    if (!m_resizeHandlers.empty()) {
        event->accept();
    } else {
        event->ignore();
    }
}

void DisplayerSelection::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
{
    QPointF p = event->pos() - m_resizeOffset;
    QRectF bb = m_tool->displayer()->getSceneBoundingRect();
    p.rx() = std::min(std::max(bb.x(), p.x()), bb.x() + bb.width());
    p.ry() = std::min(std::max(bb.y(), p.y()), bb.y() + bb.height());
    if (!m_resizeHandlers.isEmpty()) {
        for (const ResizeHandler& handler : m_resizeHandlers) {
            handler(p, m_anchor, m_point);
        }
        QRectF newRect = QRectF(m_anchor, m_point).normalized().united(m_minRect);
        setRect(newRect);
        event->accept();
    } else {
        event->ignore();
    }
}

void DisplayerSelection::mouseReleaseEvent(QGraphicsSceneMouseEvent* /*event*/)
{
    if (!m_resizeHandlers.isEmpty()) {
        emit geometryChanged(rect());
        m_resizeHandlers.clear();
    }
}

void DisplayerSelection::resizeAnchorX(const QPointF& pos, QPointF& anchor, QPointF&)
{
    anchor.rx() = pos.x();
}

void DisplayerSelection::resizeAnchorY(const QPointF& pos, QPointF& anchor, QPointF&)
{
    anchor.ry() = pos.y();
}

void DisplayerSelection::resizePointX(const QPointF& pos, QPointF&, QPointF& point)
{
    point.rx() = pos.x();
}

void DisplayerSelection::resizePointY(const QPointF& pos, QPointF&, QPointF& point)
{
    point.ry() = pos.y();
}

bool DisplayerTool::hasMultipleOCRAreas() const
{
    return false;
}

bool DisplayerTool::allowAutodetectOCRAreas() const
{
    return false;
}

Displayer* DisplayerTool::displayer() const
{
    return m_displayer;
}
