/***************************************************************************
 *   Copyright (C) 2020 by S. MANKOWSKI / G. DE BURE support@mankowski.fr  *
 *                                                                         *
 *   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 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 <https://www.gnu.org/licenses/>  *
 ***************************************************************************/
/** @file
 * This file is Skrooge plugin for PDF import / export.
 *
 * @author Stephane MANKOWSKI / Guillaume DE BURE
 */
#include "skgimportpluginpdf.h"

#include <kpluginfactory.h>

#include <qdiriterator.h>
#include <qfileinfo.h>
#include <qprocess.h>
#include <qstandardpaths.h>
#include <qtemporaryfile.h>

#include "skgbankincludes.h"
#include "skgimportexportmanager.h"
#include "skgservices.h"
#include "skgtraces.h"

/**
 * This plugin factory.
 */
K_PLUGIN_FACTORY(SKGImportPluginPDFFactory, registerPlugin<SKGImportPluginPDF>();)

SKGImportPluginPDF::SKGImportPluginPDF(QObject* iImporter, const QVariantList& iArg)
    : SKGImportPlugin(iImporter)
{
    SKGTRACEINFUNC(10)
    Q_UNUSED(iArg)
}

SKGImportPluginPDF::~SKGImportPluginPDF()
    = default;

bool SKGImportPluginPDF::isImportPossible()
{
    SKGTRACEINFUNC(10)
    return (m_importer == nullptr ? true : m_importer->getFileNameExtension() == QStringLiteral("PDF"));
}

QString SKGImportPluginPDF::extract(const QStringList& iLine, const QString& iSyntax)
{
    QString output;
    int currentIndex = -1;
    bool setpossible = true;

    // Interpret the syntax
    auto items = SKGServices::splitCSVLine(iSyntax, '|', false);
    for (const auto& item : items) {
        if (item.startsWith(QLatin1String("REGEXPCAP:"))) {
            QRegExp regexp(item.right(item.length() - 10));
            if (output.isEmpty()) {
                for (const auto& line : iLine) {
                    if (regexp.indexIn(line) > -1) {
                        output = regexp.cap(1);
                        break;
                    }
                }
            } else {
                if (regexp.indexIn(output) > -1) {
                    output = regexp.cap(1);
                }
            }
        } else if (item.startsWith(QLatin1String("REGEXP:"))) {
            setpossible = false;
            QRegExp regexp(item.right(item.length() - 7));
            int nb = iLine.count();
            for (int i = 0; i < nb; ++i) {
                if (regexp.indexIn(iLine.at(i)) > -1) {
                    currentIndex = i;
                    setpossible = true;
                    break;
                }
            }
        } else if (item.startsWith(QLatin1String("LINEOFFSET:"))) {
            currentIndex += SKGServices::stringToInt(item.right(item.length() - 11));
            if (currentIndex >= 0 && currentIndex < iLine.count()) {
                output = iLine.at(currentIndex);
            }
        } else if (item.startsWith(QLatin1String("SET:")) && setpossible) {
            QString s = item.right(item.length() - 4);
            if (s.contains(QLatin1String("%1"))) {
                output = s.arg(output);
            } else {
                output = s;
            }
        }
    }

    return output;
}

SKGError SKGImportPluginPDF::importFile()
{
    if (m_importer == nullptr) {
        return SKGError(ERR_ABORT, i18nc("Error message", "Invalid parameters"));
    }
    SKGError err;
    SKGTRACEINFUNCRC(2, err)

    // Begin transaction
    err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import %1 file", "PDF"), 2);
    IFOK(err) {
        // Open file
        IFOK(err) {
            // Extract text from PDF
            QString file = m_importer->getLocalFileName();
            QTemporaryFile txtFile;
            txtFile.open();
            QStringList args = QStringList() << file << txtFile.fileName();

            QProcess p;
            p.start(QStringLiteral("pdftotext"), args);
            if (!p.waitForFinished(1000 * 60 * 2) || p.exitCode() != 0) {
                QString cmd = "pdftotext " % args.join(QStringLiteral(" "));
                err.setReturnCode(ERR_FAIL).setMessage(i18nc("Error message",  "The following command line failed with code %2:\n'%1'", cmd, p.exitCode()));

            } else {
                // Step 1 done
                IFOKDO(err, m_importer->getDocument()->stepForward(1))

                // Read the text file
                QStringList lines;
                QTextStream stream(&txtFile);
                while (!stream.atEnd()) {
                    // Read line
                    lines.push_back(stream.readLine());
                }

                // Search extractors
                QStringList listOfExtractors;
                bool found = false;
                QString a = QStringLiteral("skrooge/extractors");
                const auto dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, a, QStandardPaths::LocateDirectory);
                for (const auto& dir : dirs) {
                    QDirIterator it(dir, QStringList() << QStringLiteral("*.extractor"));
                    while (it.hasNext() && !found) {
                        // Read extractor
                        QString fileName = it.next();
                        QString extractor = QFileInfo(fileName).baseName().toUpper();
                        listOfExtractors.push_back(extractor);
                        QHash< QString, QString > properties;
                        err = SKGServices::readPropertyFile(fileName, properties);
                        IFOK(err) {
                            // Check if this extractor is done for this file
                            QString payee = extract(lines, properties[QStringLiteral("payee")]);
                            if (!payee.isEmpty()) {
                                // Search the date
                                QString date = extract(lines, properties[QStringLiteral("date")]);
                                QString dateFormat = properties[QStringLiteral("dateformat")];
                                auto d = QDate::fromString(date, dateFormat);
                                if (!d.isValid()) {
                                    d = QDate::fromString(date);
                                    if (!d.isValid()) {
                                        SKGTRACE << "WARNING: Impossible to parse the date [" << date << "] with [" << dateFormat << "]" << Qt::endl;
                                    }
                                }
                                if (!dateFormat.contains(QStringLiteral("yyyy")) && d.year() < 2000) {
                                    d = d.addYears(100);
                                }

                                // Search the amount
                                double amount = SKGServices::stringToDouble(extract(lines, properties[QStringLiteral("amount")]));

                                // Search the comment
                                QString comment = extract(lines, properties[QStringLiteral("comment")]);

                                // Search the number
                                QString number = extract(lines, properties[QStringLiteral("number")]);

                                // Search the mode
                                QString mode = extract(lines, properties[QStringLiteral("mode")]);

                                // Get account
                                SKGAccountObject account;
                                SKGOperationObject act;
                                m_importer->getDocument()->getObject(QStringLiteral("v_account_display"), QStringLiteral("t_close='N' AND t_type='C' ORDER BY i_NBOPERATIONS DESC LIMIT 1"), act);
                                if (act.exist()) {
                                    account = act;
                                    IFOKDO(err, m_importer->getDocument()->sendMessage(i18nc("An information message",  "Using account '%1' for import", account.getName())))
                                } else {
                                    IFOKDO(err, m_importer->getDefaultAccount(account))
                                }

                                if (d.isValid() && !qFuzzyCompare(1 + amount, 1.0)) {
                                    // Get unit
                                    SKGUnitObject unit;
                                    IFOKDO(err, m_importer->getDefaultUnit(unit))

                                    // Create operation
                                    SKGOperationObject operation;
                                    IFOKDO(err, account.addOperation(operation, true))
                                    IFOKDO(err, operation.setDate(d))


                                    IFOKDO(err, operation.setUnit(unit))
                                    SKGPayeeObject payeeObj;
                                    IFOKDO(err, SKGPayeeObject::createPayee(m_importer->getDocument(), payee, payeeObj))
                                    IFOKDO(err, operation.setPayee(payeeObj))
                                    IFOKDO(err, operation.setComment(comment))
                                    IFOKDO(err, operation.setMode(mode))
                                    IFOKDO(err, operation.setImportID(QStringLiteral("PDF-") % extractor % QStringLiteral("-") % number))
                                    // This is normal. PDF inport is for only one operation, so no check if already imported
                                    IFOKDO(err, operation.setAttribute(QStringLiteral("t_imported"), QStringLiteral("Y")))
                                    IFOKDO(err, operation.save(false))

                                    SKGSubOperationObject subop;
                                    IFOKDO(err, operation.addSubOperation(subop))
                                    IFOKDO(err, subop.setComment(comment))
                                    IFOKDO(err, subop.setQuantity(-amount))
                                    IFOKDO(err, subop.save(false, false))

                                    // Add file
                                    IFOKDO(err, err = operation.setProperty(i18n("Invoice"), file, file))

                                    found = true;
                                }
                            }
                        }
                    }
                }

                if (!found) {
                    IFOKDO(err, m_importer->getDocument()->sendMessage(i18nc("An information message",  "Invoice %1 has not been imported because it is not recognized (List of recognized extractors: %2).", file, listOfExtractors.join(',')), SKGDocument::Error))
                }

                // Step 2 done
                IFOKDO(err, m_importer->getDocument()->stepForward(2))
            }
        }
    }
    SKGENDTRANSACTION(m_importer->getDocument(),  err)

    return err;
}

QString SKGImportPluginPDF::getMimeTypeFilter() const
{
    return "*.pdf|" % i18nc("A file format", "PDF file (invoice)");
}

#include <skgimportpluginpdf.moc>
