#include <QtCore/QCoreApplication>
#include <QtCore/QDateTime>
#include <QtCore/QDebug>

#include <iostream>

#include <poppler-qt5.h>
#include <poppler-form.h>

static std::ostream &operator<<(std::ostream &out, Poppler::FormField::FormType type)
{
    switch (type) {
    case Poppler::FormField::FormButton:
        out << "Button";
        break;
    case Poppler::FormField::FormText:
        out << "Text";
        break;
    case Poppler::FormField::FormChoice:
        out << "Choice";
        break;
    case Poppler::FormField::FormSignature:
        out << "Signature";
        break;
    }
    return out;
}

static std::ostream &operator<<(std::ostream &out, Poppler::FormFieldButton::ButtonType type)
{
    switch (type) {
    case Poppler::FormFieldButton::Push:
        out << "Push";
        break;
    case Poppler::FormFieldButton::CheckBox:
        out << "CheckBox";
        break;
    case Poppler::FormFieldButton::Radio:
        out << "Radio";
        break;
    }
    return out;
}

static std::ostream &operator<<(std::ostream &out, Poppler::FormFieldText::TextType type)
{
    switch (type) {
    case Poppler::FormFieldText::Normal:
        out << "Normal";
        break;
    case Poppler::FormFieldText::Multiline:
        out << "Multiline";
        break;
    case Poppler::FormFieldText::FileSelect:
        out << "FileSelect";
        break;
    }
    return out;
}

static std::ostream &operator<<(std::ostream &out, Poppler::FormFieldChoice::ChoiceType type)
{
    switch (type) {
    case Poppler::FormFieldChoice::ComboBox:
        out << "ComboBox";
        break;
    case Poppler::FormFieldChoice::ListBox:
        out << "ListBox";
        break;
    }
    return out;
}

static std::ostream &operator<<(std::ostream &out, Poppler::SignatureValidationInfo::SignatureStatus status)
{
    switch (status) {
    case Poppler::SignatureValidationInfo::SignatureValid:
        out << "Valid";
        break;
    case Poppler::SignatureValidationInfo::SignatureInvalid:
        out << "Invalid";
        break;
    case Poppler::SignatureValidationInfo::SignatureDigestMismatch:
        out << "DigestMismatch";
        break;
    case Poppler::SignatureValidationInfo::SignatureDecodingError:
        out << "DecodingError";
        break;
    case Poppler::SignatureValidationInfo::SignatureGenericError:
        out << "GenericError";
        break;
    case Poppler::SignatureValidationInfo::SignatureNotFound:
        out << "NotFound";
        break;
    case Poppler::SignatureValidationInfo::SignatureNotVerified:
        out << "NotVerifiedYet";
        break;
    }
    return out;
}

static std::ostream &operator<<(std::ostream &out, Poppler::SignatureValidationInfo::CertificateStatus status)
{
    switch (status) {
    case Poppler::SignatureValidationInfo::CertificateTrusted:
        out << "Trusted";
        break;
    case Poppler::SignatureValidationInfo::CertificateUntrustedIssuer:
        out << "UntrustedIssuer";
        break;
    case Poppler::SignatureValidationInfo::CertificateUnknownIssuer:
        out << "UnknownIssuer";
        break;
    case Poppler::SignatureValidationInfo::CertificateRevoked:
        out << "Revoked";
        break;
    case Poppler::SignatureValidationInfo::CertificateExpired:
        out << "Expired";
        break;
    case Poppler::SignatureValidationInfo::CertificateGenericError:
        out << "GenericError";
        break;
    case Poppler::SignatureValidationInfo::CertificateNotVerified:
        out << "NotVerifiedYet";
        break;
    }
    return out;
}

static std::ostream &operator<<(std::ostream &out, Qt::Alignment alignment)
{
    switch (alignment) {
    case Qt::AlignLeft:
        out << "Left";
        break;
    case Qt::AlignRight:
        out << "Right";
        break;
    case Qt::AlignHCenter:
        out << "HCenter";
        break;
    case Qt::AlignJustify:
        out << "Justify";
        break;
    case Qt::AlignTop:
        out << "Top";
        break;
    case Qt::AlignBottom:
        out << "Bottom";
        break;
    case Qt::AlignVCenter:
        out << "VCenter";
        break;
    case Qt::AlignCenter:
        out << "Center";
        break;
    case Qt::AlignAbsolute:
        out << "Absolute";
        break;
    }
    return out;
}

static std::ostream &operator<<(std::ostream &out, const QString &string)
{
    out << string.toUtf8().constData();
    return out;
}

static std::ostream &operator<<(std::ostream &out, const QRectF &rect)
{
    out << QStringLiteral("top: %1 left: %2 width: %3 height: %4").arg(rect.x()).arg(rect.y()).arg(rect.width()).arg(rect.height());
    return out;
}

template<typename T>
std::ostream &operator<<(std::ostream &out, const QList<T> &elems)
{
    bool isFirst = true;
    for (int i = 0; i < elems.count(); ++i) {
        if (!isFirst)
            out << " ";
        out << elems[i];
        isFirst = false;
    }
    return out;
}

int main(int argc, char **argv)
{
    QCoreApplication a(argc, argv);

    if (!(argc == 2)) {
        qWarning() << "usage: poppler-forms filename";
        exit(1);
    }

    Poppler::Document *doc = Poppler::Document::load(argv[1]);
    if (!doc) {
        qWarning() << "doc not loaded";
        exit(1);
    }

    std::cout << "Forms for file " << argv[1] << std::endl;
    for (int i = 0; i < doc->numPages(); ++i) {
        Poppler::Page *page = doc->page(i);
        if (page) {
            QList<Poppler::FormField *> forms = page->formFields();
            std::cout << "\tPage " << i + 1 << std::endl;
            foreach (const Poppler::FormField *form, forms) {
                std::cout << "\t\tForm" << std::endl;
                std::cout << "\t\t\tType: " << form->type() << std::endl;
                std::cout << "\t\t\tRect: " << form->rect() << std::endl;
                std::cout << "\t\t\tID: " << form->id() << std::endl;
                std::cout << "\t\t\tName: " << form->name() << std::endl;
                std::cout << "\t\t\tFullyQualifiedName: " << form->fullyQualifiedName() << std::endl;
                std::cout << "\t\t\tUIName: " << form->uiName() << std::endl;
                std::cout << "\t\t\tReadOnly: " << form->isReadOnly() << std::endl;
                std::cout << "\t\t\tVisible: " << form->isVisible() << std::endl;
                switch (form->type()) {
                case Poppler::FormField::FormButton: {
                    const Poppler::FormFieldButton *buttonForm = static_cast<const Poppler::FormFieldButton *>(form);
                    std::cout << "\t\t\tButtonType: " << buttonForm->buttonType() << std::endl;
                    std::cout << "\t\t\tCaption: " << buttonForm->caption() << std::endl;
                    std::cout << "\t\t\tState: " << buttonForm->state() << std::endl;
                    std::cout << "\t\t\tSiblings: " << buttonForm->siblings() << std::endl;
                } break;

                case Poppler::FormField::FormText: {
                    const Poppler::FormFieldText *textForm = static_cast<const Poppler::FormFieldText *>(form);
                    std::cout << "\t\t\tTextType: " << textForm->textType() << std::endl;
                    std::cout << "\t\t\tText: " << textForm->text() << std::endl;
                    std::cout << "\t\t\tIsPassword: " << textForm->isPassword() << std::endl;
                    std::cout << "\t\t\tIsRichText: " << textForm->isRichText() << std::endl;
                    std::cout << "\t\t\tMaximumLength: " << textForm->maximumLength() << std::endl;
                    std::cout << "\t\t\tTextAlignment: " << textForm->textAlignment() << std::endl;
                    std::cout << "\t\t\tCanBeSpellChecked: " << textForm->canBeSpellChecked() << std::endl;
                } break;

                case Poppler::FormField::FormChoice: {
                    const Poppler::FormFieldChoice *choiceForm = static_cast<const Poppler::FormFieldChoice *>(form);
                    std::cout << "\t\t\tChoiceType: " << choiceForm->choiceType() << std::endl;
                    std::cout << "\t\t\tChoices: " << choiceForm->choices() << std::endl;
                    std::cout << "\t\t\tIsEditable: " << choiceForm->isEditable() << std::endl;
                    std::cout << "\t\t\tIsMultiSelect: " << choiceForm->multiSelect() << std::endl;
                    std::cout << "\t\t\tCurrentChoices: " << choiceForm->currentChoices() << std::endl;
                    std::cout << "\t\t\tEditChoice: " << choiceForm->editChoice() << std::endl;
                    std::cout << "\t\t\tTextAlignment: " << choiceForm->textAlignment() << std::endl;
                    std::cout << "\t\t\tCanBeSpellChecked: " << choiceForm->canBeSpellChecked() << std::endl;
                } break;

                case Poppler::FormField::FormSignature: {
                    const Poppler::FormFieldSignature *signatureForm = static_cast<const Poppler::FormFieldSignature *>(form);
                    const Poppler::SignatureValidationInfo svi = signatureForm->validate(Poppler::FormFieldSignature::ValidateVerifyCertificate);
                    std::cout << "\t\t\tSignatureStatus: " << svi.signatureStatus() << std::endl;
                    std::cout << "\t\t\tCertificateStatus: " << svi.certificateStatus() << std::endl;
                    if (svi.signerName().isEmpty() == false)
                        std::cout << "\t\t\tSignerName: " << svi.signerName() << std::endl;
                    else
                        std::cout << "\t\t\tSignerName: "
                                  << "(null)" << std::endl;
                    // http://doc.qt.io/qt-5/qdatetime.html#fromTime_t-1
                    // Requires Qt 5.2 -> configure.ac update
                    // QDateTime::fromTime_t(svi->signingTime(), Qt::UTC).toString();
                    QDateTime sviTime;
                    sviTime.setTime_t(svi.signingTime());
                    std::cout << "\t\t\tSigningTime: " << sviTime.toString() << std::endl;
                } break;
                }
            }
            qDeleteAll(forms);
            delete page;
        }
    }
    delete doc;
}
