// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

#include "imageviewer.h"

#include <QFileDialog>
#include <QLabel>
#include <QMainWindow>
#include <QToolBar>

#include <QColorSpace>
#include <QGuiApplication>
#include <QIcon>
#include <QImageReader>
#include <QKeySequence>
#include <QPainter>
#include <QPixmap>
#include <QScreen>

#include <QDir>

#ifdef QT_DOCUMENTVIEWER_PRINTSUPPORT
#  include <QPrinter>
#endif

using namespace Qt::StringLiterals;

static QStringList imageFormats()
{
    QStringList result;
    const QByteArrayList &allFormats = QImageReader::supportedImageFormats();
    for (const auto &format : allFormats) {
        if (format != "tif" && format != "cur") // duplicate/non-existent
            result.append("image/"_L1 + QLatin1StringView(format));
    }
    return result;
}

static QString msgOpen(const QString &name, const QImage &image)
{
    const QString description = image.colorSpace().isValid()
    ? image.colorSpace().description() : ImageViewer::tr("unknown");
    return ImageViewer::tr("Opened \"%1\", %2x%3, Depth: %4 (%5)")
                           .arg(QDir::toNativeSeparators(name))
                           .arg(image.width()).arg(image.height())
                           .arg(image.depth()).arg(description);
}

//! [init]
ImageViewer::ImageViewer() : m_formats(imageFormats())
{
    connect(this, &AbstractViewer::uiInitialized, this, &ImageViewer::setupImageUi);
    QImageReader::setAllocationLimit(1024); // MB
}
//! [init]

ImageViewer::~ImageViewer() = default;

void ImageViewer::init(QFile *file, QWidget *parent, QMainWindow *mainWindow)
{
    m_imageLabel = new QLabel(parent);
    m_imageLabel->setFrameShape(QFrame::Box);
    m_imageLabel->setAlignment(Qt::AlignCenter);
    m_imageLabel->setScaledContents(true);

    AbstractViewer::init(file, m_imageLabel, mainWindow);

    m_toolBar = addToolBar(tr("Images"));

    m_zoomInAct = m_toolBar->addAction(tr("Zoom &In"), this, &ImageViewer::zoomIn);
    m_zoomInAct->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::ZoomIn));
    m_zoomInAct->setShortcut(QKeySequence::ZoomIn);

    m_zoomOutAct = m_toolBar->addAction(tr("Zoom &Out"), this, &ImageViewer::zoomOut);
    m_zoomOutAct->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::ZoomOut));
    m_zoomOutAct->setShortcut(QKeySequence::ZoomOut);

    m_resetZoomAct = m_toolBar->addAction(tr("Reset Zoom"), this, &ImageViewer::resetZoom);
    m_resetZoomAct->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::ZoomFitBest));
    m_resetZoomAct->setShortcut(QKeySequence(Qt::ControlModifier | Qt::Key_0));
}

QStringList ImageViewer::supportedMimeTypes() const
{
    return m_formats;
}

void ImageViewer::clear()
{
    m_imageLabel->setPixmap({});
    m_maxScaleFactor = m_minScaleFactor = m_initialScaleFactor = m_scaleFactor = 1;
}

void ImageViewer::setupImageUi()
{
    openFile();
}

//! [open]
void ImageViewer::openFile()
{
#if QT_CONFIG(cursor)
    QGuiApplication::setOverrideCursor(Qt::WaitCursor);
#endif
    const QString name = m_file->fileName();
    QImageReader reader(name);
    const QImage origImage = reader.read();
    if (origImage.isNull()) {
        statusMessage(tr("Cannot read file %1:\n%2.")
                      .arg(QDir::toNativeSeparators(name),
                           reader.errorString()), tr("open"));
        disablePrinting();
#if QT_CONFIG(cursor)
        QGuiApplication::restoreOverrideCursor();
#endif
        return;
    }

    clear();

    QImage image = origImage.colorSpace().isValid()
        ? origImage.convertedToColorSpace(QColorSpace::SRgb)
        : origImage;

    const auto devicePixelRatio = m_imageLabel->devicePixelRatioF();
    m_imageSize = QSizeF(image.size()) / devicePixelRatio;

    QPixmap pixmap = QPixmap::fromImage(image);
    pixmap.setDevicePixelRatio(devicePixelRatio);
    m_imageLabel->setPixmap(pixmap);

    const QSizeF targetSize = m_imageLabel->parentWidget()->size();
    if (m_imageSize.width() > targetSize.width()
        || m_imageSize.height() > targetSize.height()) {
        m_initialScaleFactor = qMin(targetSize.width() / m_imageSize.width(),
                                    targetSize.height() / m_imageSize.height());
    }
    m_maxScaleFactor = 3 * m_initialScaleFactor;
    m_minScaleFactor = m_initialScaleFactor / 3;
    doSetScaleFactor(m_initialScaleFactor);

    statusMessage(msgOpen(name, origImage));
#if QT_CONFIG(cursor)
    QGuiApplication::restoreOverrideCursor();
#endif

    maybeEnablePrinting();
}
//! [open]

void ImageViewer::setScaleFactor(qreal scaleFactor)
{
    if (!qFuzzyCompare(m_scaleFactor, scaleFactor))
        doSetScaleFactor(scaleFactor);
}

void ImageViewer::doSetScaleFactor(qreal scaleFactor)
{
    m_scaleFactor = scaleFactor;
    const QSize labelSize = (m_imageSize * m_scaleFactor).toSize();
    m_imageLabel->setFixedSize(labelSize);
    enableZoomActions();
}

void ImageViewer::zoomIn()
{
    setScaleFactor(m_scaleFactor * 1.25);
}

void ImageViewer::zoomOut()
{
    setScaleFactor(m_scaleFactor * 0.8);
}

void ImageViewer::resetZoom()
{
    setScaleFactor(m_initialScaleFactor);
}

bool ImageViewer::hasContent() const
{
    return !m_imageLabel->pixmap().isNull();
}

void ImageViewer::enableZoomActions()
{
    m_resetZoomAct->setEnabled(!qFuzzyCompare(m_scaleFactor, m_initialScaleFactor));
    m_zoomInAct->setEnabled(m_scaleFactor < m_maxScaleFactor);
    m_zoomOutAct->setEnabled(m_scaleFactor > m_minScaleFactor);
}

#ifdef QT_DOCUMENTVIEWER_PRINTSUPPORT
void ImageViewer::printDocument(QPrinter *printer) const
{
    if (!hasContent())
        return;

    QPainter painter(printer);
    QPixmap pixmap = m_imageLabel->pixmap();
    QRect rect = painter.viewport();
    QSize size = pixmap.size();
    size.scale(rect.size(), Qt::KeepAspectRatio);
    painter.setViewport(rect.x(), rect.y(), size.width(), size.height());
    painter.setWindow(pixmap.rect());
    painter.drawPixmap(0, 0, pixmap);
}
#endif // QT_DOCUMENTVIEWER_PRINTSUPPORT
