/***
   This file is part of libfly-ocr.

   Copyright (C) 2021 LLC "RusBITech-Astra" (https://astralinux.ru)

   libfly-ocr is free software: you can redistribute it and/or modify
   it under the terms of the GNU Lesser 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 Lesser General Public
   License along with libfly-ocr.  If not, see <https://www.gnu.org/licenses/>.
***/

#include <QPluginLoader>
#include <QFileInfo>
#include <QSettings>
#include <QTranslator>
#include <QApplication>
#include "fly-ocr.h"
#include "ocrengine.h"

QList<OcrEngineFactory*>* OcrEngine::m_factories = nullptr;
QHash<QString, QString> OcrEngine::m_langLookup;

OcrEngine::OcrEngine(QObject* parent) : QObject(parent)
{}

void OcrEngine::loadPlugins()
{
    if (m_factories)
        return;

    m_factories = new QList<OcrEngineFactory*>();

    for (const QString& filePath : FlyOcr::findPlugins()) {
        QPluginLoader loader(filePath);
        QObject* plugin = loader.instance();
        if (loader.isLoaded())
            qDebug("OcrEngine: loaded plugin %s", qPrintable(QFileInfo(filePath).filePath()));
        else
            qWarning("OcrEngine: %s", qPrintable(loader.errorString ()));

        OcrEngineFactory* f = plugin ? qobject_cast<OcrEngineFactory*>(plugin) : nullptr;
        if (f) {
            m_factories->append(f);
            QTranslator* translator = new QTranslator(qApp);
            if (translator->load(QLocale(), f->shortName() + QStringLiteral("_plugin"), QLatin1String("_"), QLatin1String(":/")))
                qApp->installTranslator(translator);
            else
                delete translator;
        }
    }
}

OcrEngine* OcrEngine::create(QObject* parent, const QList<FlyOcr::Lang>& langs, bool analyseOnly)
{
    QStringList langArray;
    for (const FlyOcr::Lang& lang : qAsConst(langs))
        langArray << lang.shortName;

    return create(parent, langArray, analyseOnly);
}


OcrEngine* OcrEngine::create(QObject* parent, const QStringList& langs, bool analyseOnly)
{
    loadPlugins();
    OcrEngineFactory* fact = selected();

    if (!fact)
        return nullptr;

    OcrEngine* engine = fact->create(parent); //TODO add option
    if (!engine) {
        qWarning("OcrEngine: unable to create ocr engine");
        return nullptr;
    }

    if (!engine->initialize(langs, analyseOnly)) {
        qWarning("OcrEngine: unable to initialize ocr engine");
        delete engine;
        return nullptr;
    }
    return engine;
}

OcrEngineFactory* OcrEngine::selected()
{
    loadPlugins();
    if (m_factories->isEmpty()) {
        qWarning("OcrEngine: unable to find any ocr engine");
        return nullptr;
    }

    QSettings settings;
    QString shortName = settings.value("OCR/engine").toString();
    for (OcrEngineFactory* f : qAsConst(*m_factories)) {
        if (f->shortName() == shortName)
            return f;
    }
    return m_factories->first();
}

QList<FlyOcr::Lang> OcrEngine::languages()
{
    OcrEngineFactory* f = selected();
    return f ? f->languages() : QList<FlyOcr::Lang>();
}

OcrEngineFactory::Formats OcrEngine::supportedExportFormats()
{
    OcrEngineFactory* f = selected();
    return f ? f->supportedExportFormats() : OcrEngineFactory::Formats();
}

OcrEngineFactory::Formats OcrEngine::supportedPageDataFormats()
{
    OcrEngineFactory* f = selected();
    return f ? f->supportedPageDataFormats() : OcrEngineFactory::Formats();
}

void OcrEngine::select(const QString& shortName)
{
    QSettings settings;
    settings.setValue("OCR/engine", shortName);
    m_langLookup.clear();
}

const QList<OcrEngineFactory*>& OcrEngine::factories()
{
    loadPlugins();
    return *m_factories;
}

QString OcrEngine::lookupLangCode(const QString& prefix)
{
    if (m_langLookup.isEmpty()) {
        for (const FlyOcr::Lang& lang : languages())
            m_langLookup.insert(lang.name, lang.code);
    }
    return m_langLookup.value(prefix);
}
