/*
 * Copyright (c) 2018 Frank Fischer <frank-fischer@shadow-soft.de>
 *
 * This program 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.
 *
 * 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 General Public License
 * along with this program.  If not, see  <http://www.gnu.org/licenses/>
 */

#include "ZoomImage.hxx"

#include "BaseImage.hxx"

#include <QtGui/QImage>
#include <QtGui/QPainter>

struct ZoomImage::Data {
    QPointF viewSize = {0.1, 0.1};
    QPointF center;
    QColor cross_color = Qt::red;
    QColor border_color = Qt::white;
    BaseImage* source = nullptr;
};

ZoomImage::ZoomImage() : d(new Data) {}

ZoomImage::~ZoomImage() = default;

QPointF ZoomImage::viewSize() const
{
    return d->viewSize;
}

void ZoomImage::setViewSize(const QPointF& viewSize)
{
    // all ratio coordinates must be in [0,1]
    QPointF vs = QPointF{qBound<qreal>(0, viewSize.x(), 1), qBound<qreal>(0, viewSize.y(), 1)};
    if (vs != d->viewSize) {
        d->viewSize = vs;
        emit viewSizeChanged();
        update();
    }
}

QPointF ZoomImage::center() const
{
    return d->center;
}

void ZoomImage::setCenter(const QPointF& center)
{
    QPointF c = QPointF{qBound<qreal>(0, center.x(), 1), qBound<qreal>(0, center.y(), 1)};
    if (c != d->center) {
        d->center = c;
        emit centerChanged();
        update();
    }
}

QColor ZoomImage::borderColor() const
{
    return d->border_color;
}

void ZoomImage::setBorderColor(const QColor& color)
{
    if (color != d->border_color) {
        d->border_color = color;
        emit borderColorChanged();
        update();
    }
}

QColor ZoomImage::crossColor() const
{
    return d->cross_color;
}

void ZoomImage::setCrossColor(const QColor& color)
{
    if (color != d->cross_color) {
        d->cross_color = color;
        emit crossColorChanged();
        update();
    }
}

void ZoomImage::setSource(BaseImage* source)
{
    if (source == d->source) return;

    if (d->source) {
        disconnect(d->source, &BaseImage::imageChanged, this, &ZoomImage::updateImage);
    }

    d->source = source;

    if (d->source) {
        connect(d->source, &BaseImage::imageChanged, this, &ZoomImage::updateImage);
        update();
    }

    emit sourceChanged();
}

BaseImage* ZoomImage::source()
{
    return d->source;
}

void ZoomImage::paint(QPainter* p)
{
    if (!d->source) return;

    auto w = width();
    auto h = height();

    // get the source image
    QImage image = d->source->image();
    auto iw = image.width();
    auto ih = image.height();

    // prepare the clipping path (a circle) so that the picture is only shown
    // within the preview area.
    auto linewidth = std::min(width(), height()) / 20;
    QPainterPath clip;
    clip.addEllipse(0, 0, width(), height());

    // draw the part of image with clipping, note that we use ration coordinates
    p->setClipPath(clip);
    p->drawImage(QRectF{0, 0, w, h},
                 image,
                 QRectF(iw * (d->center.x() - d->viewSize.x() / 2),
                        ih * (d->center.y() - d->viewSize.y() / 2),
                        iw * d->viewSize.x(),
                        ih * d->viewSize.y()));

    // disable clipping for drawing the boundary
    p->setClipping(false);

    // draw the boundary
    QPen pen(d->border_color);
    pen.setWidth(linewidth);
    p->setPen(pen);
    p->drawEllipse(linewidth / 2, linewidth / 2, w - linewidth, h - linewidth);

    // draw the cross
    p->setPen(d->cross_color);
    p->drawLine(w / 2, h / 2 - h / 6, w / 2, h / 2 + h / 6);
    p->drawLine(w / 2 - w / 6, h / 2, w / 2 + w / 6, h / 2);
}

void ZoomImage::updateImage()
{
    update();
}
