/* poppler-ps-converter.cc: qt interface to poppler
 * Copyright (C) 2007, 2009, 2010, 2015, 2020, 2022, 2026, Albert Astals Cid <aacid@kde.org>
 * Copyright (C) 2008, Pino Toscano <pino@kde.org>
 * Copyright (C) 2010 Hib Eris <hib@hiberis.nl>
 * Copyright (C) 2011 Glad Deschrijver <glad.deschrijver@gmail.com>
 * Copyright (C) 2012 Fabio D'Urso <fabiodurso@hotmail.it>
 * Copyright (C) 2013 Thomas Freitag <Thomas.Freitag@alfa.de>
 * Copyright (C) 2014 Adrian Johnson <ajohnson@redneon.com>
 * Copyright (C) 2020 William Bader <williambader@hotmail.com>
 * Copyright (C) 2023 Kevin Ottens <kevin.ottens@enioka.com>. Work sponsored by De Bortoli Wines
 * Copyright (C) 2025, 2026 g10 Code GmbH, Author: Sune Stolborg Vuorela <sune@vuorela.dk>
 *
 * 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 2, 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, write to the Free Software
 * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include "poppler-qt6.h"

#include "poppler-private.h"
#include "poppler-converter-private.h"

#include "PSOutputDev.h"

static void outputToQIODevice(void *stream, std::string_view string)
{
    static_cast<QIODevice *>(stream)->write(string.data(), string.length());
}

namespace Poppler {

class PSConverterPrivate : public BaseConverterPrivate
{
public:
    PSConverterPrivate();
    ~PSConverterPrivate() override;

    QList<int> pageList;
    QString title;
    double hDPI = 72;
    double vDPI = 72;
    int rotate = 0;
    int paperWidth = -1;
    int paperHeight = -1;
    int marginRight = 0;
    int marginBottom = 0;
    int marginLeft = 0;
    int marginTop = 0;
    PSConverter::PSOptions opts;
    void (*pageConvertedCallback)(int page, void *payload) = nullptr;
    void *pageConvertedPayload = nullptr;
};

PSConverterPrivate::PSConverterPrivate() : opts(PSConverter::Printing) { }

PSConverterPrivate::~PSConverterPrivate() = default;

PSConverter::PSConverter(DocumentData *document) : BaseConverter(*new PSConverterPrivate())
{
    Q_D(PSConverter);
    d->document = document;
}

PSConverter::~PSConverter() = default;

void PSConverter::setPageList(const QList<int> &pageList)
{
    Q_D(PSConverter);
    d->pageList = pageList;
}

void PSConverter::setTitle(const QString &title)
{
    Q_D(PSConverter);
    d->title = title;
}

void PSConverter::setHDPI(double hDPI)
{
    Q_D(PSConverter);
    d->hDPI = hDPI;
}

void PSConverter::setVDPI(double vDPI)
{
    Q_D(PSConverter);
    d->vDPI = vDPI;
}

void PSConverter::setRotate(int rotate)
{
    Q_D(PSConverter);
    d->rotate = rotate;
}

void PSConverter::setPaperWidth(int paperWidth)
{
    Q_D(PSConverter);
    d->paperWidth = paperWidth;
}

void PSConverter::setPaperHeight(int paperHeight)
{
    Q_D(PSConverter);
    d->paperHeight = paperHeight;
}

void PSConverter::setRightMargin(int marginRight)
{
    Q_D(PSConverter);
    d->marginRight = marginRight;
}

void PSConverter::setBottomMargin(int marginBottom)
{
    Q_D(PSConverter);
    d->marginBottom = marginBottom;
}

void PSConverter::setLeftMargin(int marginLeft)
{
    Q_D(PSConverter);
    d->marginLeft = marginLeft;
}

void PSConverter::setTopMargin(int marginTop)
{
    Q_D(PSConverter);
    d->marginTop = marginTop;
}

void PSConverter::setStrictMargins(bool strictMargins)
{
    Q_D(PSConverter);
    if (strictMargins) {
        d->opts |= StrictMargins;
    } else {
        d->opts &= ~StrictMargins;
    }
}

void PSConverter::setForceOverprintPreview(bool forceOverprintPreview)
{
    Q_D(PSConverter);
    if (forceOverprintPreview) {
        d->opts |= ForceOverprintPreview;
    } else {
        d->opts &= ~ForceOverprintPreview;
    }
}

void PSConverter::setForceRasterize(bool forceRasterize)
{
    Q_D(PSConverter);
    if (forceRasterize) {
        d->opts |= ForceRasterization;
    } else {
        d->opts &= ~ForceRasterization;
    }
}

void PSConverter::setPSOptions(PSConverter::PSOptions options)
{
    Q_D(PSConverter);
    d->opts = options;
}

PSConverter::PSOptions PSConverter::psOptions() const
{
    Q_D(const PSConverter);
    return d->opts;
}

void PSConverter::setPageConvertedCallback(void (*callback)(int page, void *payload), void *payload)
{
    Q_D(PSConverter);
    d->pageConvertedCallback = callback;
    d->pageConvertedPayload = payload;
}

static bool annotDisplayDecideCbk(Annot *annot, void *user_data)
{
    if (annot->getType() == Annot::typeWidget) {
        return true; // Never hide forms
    }
    return *static_cast<bool *>(user_data);
}

bool PSConverter::convert()
{
    Q_D(PSConverter);
    d->lastError = NoError;

    Q_ASSERT(!d->pageList.isEmpty());
    Q_ASSERT(d->paperWidth != -1);
    Q_ASSERT(d->paperHeight != -1);

    if (d->document->locked) {
        d->lastError = FileLockedError;
        return false;
    }

    QIODevice *dev = d->openDevice();
    if (!dev) {
        d->lastError = OpenOutputError;
        return false;
    }

    QByteArray pstitle8Bit = d->title.toLocal8Bit();
    char *pstitlechar;
    if (!d->title.isEmpty()) {
        pstitlechar = pstitle8Bit.data();
    } else {
        pstitlechar = nullptr;
    }

    std::vector<int> pages;
    Q_FOREACH (int page, d->pageList) {
        pages.push_back(page);
    }

    auto *psOut = new PSOutputDev(outputToQIODevice, dev, pstitlechar, d->document->doc.get(), pages, (d->opts & PrintToEPS) ? psModeEPS : psModePS, d->paperWidth, d->paperHeight, false, false, d->marginLeft, d->marginBottom,
                                  d->paperWidth - d->marginRight, d->paperHeight - d->marginTop, (d->opts & ForceRasterization) ? psAlwaysRasterize : psRasterizeWhenNeeded);
    if (d->opts & ForceOverprintPreview) {
        psOut->setForceRasterize(psAlwaysRasterize);
        psOut->setOverprintPreview(true);
    }

    if (d->opts & StrictMargins) {
        double xScale = (static_cast<double>(d->paperWidth) - static_cast<double>(d->marginLeft) - static_cast<double>(d->marginRight)) / static_cast<double>(d->paperWidth);
        double yScale = (static_cast<double>(d->paperHeight) - static_cast<double>(d->marginBottom) - static_cast<double>(d->marginTop)) / static_cast<double>(d->paperHeight);
        psOut->setScale(xScale, yScale);
    }

    if (psOut->isOk()) {
        bool isPrinting = (d->opts & Printing) != 0;
        bool showAnnotations = (d->opts & HideAnnotations) == 0;
        Q_FOREACH (int page, d->pageList) {
            d->document->doc->displayPage(psOut, page, d->hDPI, d->vDPI, d->rotate, false, true, isPrinting, nullptr, nullptr, annotDisplayDecideCbk, &showAnnotations, true);
            if (d->pageConvertedCallback) {
                (*d->pageConvertedCallback)(page, d->pageConvertedPayload);
            }
        }
        delete psOut;
        d->closeDevice();
        return true;
    }
    delete psOut;
    d->closeDevice();
    return false;
}

}
