/***
   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 <QImageReader>
#include <poppler-qt5.h>

#include <cmath>
#include "djvudocument.h"
#include "displayrenderer.h"
#include "utils.h"

void DisplayRenderer::adjustImage(QImage& image, int brightness, int contrast, bool invert) const
{
    if (brightness == 0 && contrast == 0 && !invert) {
        return;
    }

    double kBr = 1.0 - std::abs(brightness / 200.0);
    double dBr = brightness > 0 ? 255.0 : 0.0;

    double kCn = contrast * 2.55;
    double FCn = (259.0 * (kCn + 255.0)) / (255.0 * (259.0 - kCn));

    int nLinePixels = image.bytesPerLine() / 4;
    int nLines = image.height();
    #pragma omp parallel for
    for (int line = 0; line < nLines; ++line) {
        QRgb* rgb = reinterpret_cast<QRgb*>(image.scanLine(line));
        for (int i = 0; i < nLinePixels; ++i) {
            int red = qRed(rgb[i]);
            int green = qGreen(rgb[i]);
            int blue = qBlue(rgb[i]);
            // Brighntess
            red = dBr * (1.0 - kBr) + red * kBr;
            green = dBr * (1.0 - kBr) + green * kBr;
            blue = dBr * (1.0 - kBr) + blue * kBr;
            // Contrast
            red = std::max(0.0, std::min(FCn * (red - 128.0) + 128.0, 255.0));
            green = std::max(0.0, std::min(FCn * (green - 128.0) + 128.0, 255.0));
            blue = std::max(0.0, std::min(FCn * (blue - 128.0) + 128.0, 255.0));
            // Invert
            if (invert) {
                red = 255 - red;
                green = 255 - green;
                blue = 255 - blue;
            }

            rgb[i] = qRgb(red, green, blue);
        }
    }
}

bool DisplayRenderer::isEmpty() const
{
    return pageCount() == 0;
}

ImageRenderer::ImageRenderer(const QString& filename) : DisplayRenderer(filename)
{
    QImageReader reader(m_filename);
    m_pageCount = reader.imageCount();
    for (int i = 0; i < m_pageCount; ++i) {
        reader.jumpToImage(i);
        reader.setBackgroundColor(Qt::white);
        QSize scaledSize = reader.size();
        scaledSize.scale(210, 210, Qt::KeepAspectRatio);
        reader.setScaledSize(scaledSize);
        m_cache << reader.read().convertToFormat(QImage::Format_RGB32);
    }
}

QImage ImageRenderer::render(int page, double resolution) const
{
    QImageReader reader(m_filename);
    reader.jumpToImage(page);
    reader.setBackgroundColor(Qt::white);
    reader.setScaledSize(reader.size() * resolution / 100.0);
    if (reader.scaledSize().height() > 4000 || reader.scaledSize().width() > 4000) { //limit image size
        QSize size = reader.scaledSize();
        double scale = size.width() > size.height() ? (4000. / size.width()) : (4000. / size.height());
        reader.setScaledSize(size * scale);
    }
    return reader.read().convertToFormat(QImage::Format_RGB32);
}

QImage ImageRenderer::renderThumbnail(int page) const
{
    return m_cache.value(page);
}

int ImageRenderer::pageCount() const
{
    return m_pageCount;
}

PDFRenderer::PDFRenderer(const QString& filename, const QByteArray& password) : DisplayRenderer(filename)
{
    m_document = Poppler::Document::load(filename);
    if (m_document) {
        if (m_document->isLocked()) {
            m_document->unlock(password, password);
        }

        m_document->setRenderHint(Poppler::Document::Antialiasing);
        m_document->setRenderHint(Poppler::Document::TextAntialiasing);
    }
}

PDFRenderer::~PDFRenderer()
{
    delete m_document;
}

QImage PDFRenderer::render(int page, double resolution) const
{
    if (!m_document) {
        return QImage();
    }
    m_mutex.lock();
    Poppler::Page* poppage = m_document->page(page);
    m_mutex.unlock();
    QImage image = poppage->renderToImage(resolution, resolution);
    delete poppage;
    return image.convertToFormat(QImage::Format_RGB32);
}

QImage PDFRenderer::renderThumbnail(int page) const
{
    if (!m_document) {
        return QImage();
    }
    m_mutex.lock();
    Poppler::Page* poppage = m_document->page(page);
    m_mutex.unlock();
    // Resolution such that largest dimension is 64px
    // [points] / 72 * resolution = 64 => resolution = 64 * 72 / points
    QSizeF size = poppage->pageSizeF();
    double resolution = 210. * 72. / qMax(size.width(), size.height());
    QImage image = poppage->renderToImage(resolution, resolution);
    delete poppage;
    return image.convertToFormat(QImage::Format_RGB32);
}

int PDFRenderer::pageCount() const
{
    return m_document ? m_document->numPages() : 1;
}

DJVURenderer::DJVURenderer(const QString& filename) : DisplayRenderer(filename)
{
    m_djvu = new DjVuDocument();
    m_djvu->openFile(filename);
}

DJVURenderer::~DJVURenderer()
{
    delete m_djvu;
}

QImage DJVURenderer::render(int page, double resolution) const
{
    return m_djvu->image(page, resolution);
}

QImage DJVURenderer::renderThumbnail(int pageno) const
{
    const DjVuDocument::Page& page = m_djvu->page(pageno);
    double resolution = 192. / qMax(page.width, page.height) * page.dpi;
    return m_djvu->image(pageno, resolution);
}

int DJVURenderer::pageCount() const
{
    return m_djvu->pageCount();
}
