/*
 *  Copyright (C) 2011 Andriy Rysin (rysin@kde.org)
 *
 *  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, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include <QtTest>

#include "../xkb_rules.h"

#include <qdom.h>
#include <qxml.h>

static const Rules::ExtrasFlag readExtras = Rules::NO_EXTRAS;

class RulesTest : public QObject
{
    Q_OBJECT

    Rules *rules;

private Q_SLOTS:
    void initTestCase()
    {
        rules = Rules::readRules(readExtras);
    }

    void cleanupTestCase()
    {
        delete rules;
    }

    void testRules()
    {
        QVERIFY(rules != nullptr);
        QVERIFY(rules->modelInfos.size() > 0);
        QVERIFY(rules->layoutInfos.size() > 0);
        QVERIFY(rules->optionGroupInfos.size() > 0);
    }

    void testModel()
    {
        foreach (const ModelInfo *modelInfo, rules->modelInfos) {
            QVERIFY(modelInfo != nullptr);
            QVERIFY(modelInfo->name.length() > 0);
            QVERIFY(modelInfo->description.length() > 0);
            //        	QVERIFY( ! modelInfo->vendor.isEmpty() );
        }
    }

    void testLayouts()
    {
        foreach (const LayoutInfo *layoutInfo, rules->layoutInfos) {
            QVERIFY(layoutInfo != nullptr);
            QVERIFY(!layoutInfo->name.isEmpty());
            //        	const char* desc = layoutInfo->name.toUtf8() ;
            //        	qDebug() << layoutInfo->name;
            QVERIFY(!layoutInfo->description.isEmpty());

            foreach (const VariantInfo *variantInfo, layoutInfo->variantInfos) {
                QVERIFY(variantInfo != nullptr);
                QVERIFY(!variantInfo->name.isEmpty());
                QVERIFY(!variantInfo->description.isEmpty());
            }
            foreach (const QString &language, layoutInfo->languages) {
                QVERIFY(!language.isEmpty());
            }
        }
    }

    void testOptionGroups()
    {
        foreach (const OptionGroupInfo *optionGroupInfo, rules->optionGroupInfos) {
            QVERIFY(optionGroupInfo != nullptr);
            QVERIFY(!optionGroupInfo->name.isEmpty());
            QVERIFY(!optionGroupInfo->description.isEmpty());
            // optionGroupInfo->exclusive

            foreach (const OptionInfo *optionInfo, optionGroupInfo->optionInfos) {
                QVERIFY(optionInfo != nullptr);
                QVERIFY(!optionInfo->name.isEmpty());
                QVERIFY(!optionInfo->description.isEmpty());
            }
        }
    }

    void testExtras()
    {
        Rules *rulesWithExtras = Rules::readRules(Rules::READ_EXTRAS);
        QVERIFY2(rulesWithExtras->layoutInfos.size() > rules->layoutInfos.size(), "Rules with extras should have more layouts");

        foreach (const LayoutInfo *layoutInfo, rules->layoutInfos) {
            QVERIFY(!layoutInfo->fromExtras);
        }

        bool foundFromExtras = false, foundNonExtras = false;
        foreach (const LayoutInfo *layoutInfo, rulesWithExtras->layoutInfos) {
            if (layoutInfo->fromExtras)
                foundFromExtras = true;
            if (!layoutInfo->fromExtras)
                foundNonExtras = true;
            layoutInfo->languages.size(); // make sure we can access all merged objects
            layoutInfo->variantInfos.size(); // make sure we can access all merged objects
        }
        QVERIFY(foundNonExtras);
        QVERIFY(foundFromExtras);
    }

    void testWriteNewXml()
    {
        QDomDocument doc(QStringLiteral("xkbConfigRegistry"));
        QDomElement root = doc.createElement(QStringLiteral("xkbConfigRegistry"));
        root.setAttribute(QStringLiteral("version"), QStringLiteral("2.0"));
        doc.appendChild(root);

        QDomElement modelList = doc.createElement(QStringLiteral("modelList"));
        root.appendChild(modelList);
        foreach (const ModelInfo *modelInfo, rules->modelInfos) {
            QDomElement model = doc.createElement(QStringLiteral("model"));
            model.setAttribute(QStringLiteral("name"), modelInfo->name);
            model.setAttribute(QStringLiteral("description"), modelInfo->description);
            model.setAttribute(QStringLiteral("vendor"), modelInfo->vendor);
            modelList.appendChild(model);
        }

        QDomElement layoutList = doc.createElement(QStringLiteral("layoutList"));
        foreach (const LayoutInfo *layoutInfo, rules->layoutInfos) {
            QDomElement layout = doc.createElement(QStringLiteral("layout"));
            layout.setAttribute(QStringLiteral("name"), layoutInfo->name);
            layout.setAttribute(QStringLiteral("description"), layoutInfo->description);

            QDomElement langList = doc.createElement(QStringLiteral("languageList"));
            foreach (const QString &lang, layoutInfo->languages) {
                QDomElement langNode = doc.createElement(QStringLiteral("lang"));
                langNode.setAttribute(QStringLiteral("iso639Id"), lang);
                langList.appendChild(langNode);
            }
            if (langList.hasChildNodes()) {
                layout.appendChild(langList);
            }

            QDomElement variantList = doc.createElement(QStringLiteral("variantList"));
            foreach (const VariantInfo *variantInfo, layoutInfo->variantInfos) {
                QDomElement variant = doc.createElement(QStringLiteral("variant"));
                variant.setAttribute(QStringLiteral("name"), variantInfo->name);
                variant.setAttribute(QStringLiteral("description"), variantInfo->description);

                QDomElement langList = doc.createElement(QStringLiteral("languageList"));
                foreach (const QString &lang, variantInfo->languages) {
                    QDomElement langNode = doc.createElement(QStringLiteral("lang"));
                    langNode.setAttribute(QStringLiteral("iso639Id"), lang);
                    langList.appendChild(langNode);
                }
                if (langList.hasChildNodes()) {
                    variant.appendChild(langList);
                }

                variantList.appendChild(variant);
            }
            if (variantList.hasChildNodes()) {
                layout.appendChild(variantList);
            }

            layoutList.appendChild(layout);
        }
        root.appendChild(layoutList);

        QDomElement optionGroupList = doc.createElement(QStringLiteral("optionList"));
        foreach (const OptionGroupInfo *optionGroupInfo, rules->optionGroupInfos) {
            QDomElement optionGroup = doc.createElement(QStringLiteral("optionGroup"));
            optionGroup.setAttribute(QStringLiteral("name"), optionGroupInfo->name);
            optionGroup.setAttribute(QStringLiteral("description"), optionGroupInfo->description);
            optionGroup.setAttribute(QStringLiteral("exclusive"), optionGroupInfo->exclusive);

            foreach (const OptionInfo *optionGroupInfo, optionGroupInfo->optionInfos) {
                QDomElement option = doc.createElement(QStringLiteral("option"));
                option.setAttribute(QStringLiteral("name"), optionGroupInfo->name);
                option.setAttribute(QStringLiteral("description"), optionGroupInfo->description);
                optionGroup.appendChild(option);
            }

            optionGroupList.appendChild(optionGroup);
        }
        root.appendChild(optionGroupList);

        QFile file(QStringLiteral("base2.xml"));
        if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) {
            qWarning() << "Failed to open layout memory xml file for writing" << file.fileName();
            QFAIL("failed");
        }

        QTextStream out(&file);
        out << doc.toString();
    }

    void testRulesVersion()
    {
        QVERIFY(!rules->version.isEmpty());

        Rules *rules10 = new Rules();
        Rules::readRules(rules10, QStringLiteral("config/base.xml"), false);
        QCOMPARE(rules10->version, QString("1.0"));
        delete rules10;

        Rules *rules11 = new Rules();
        Rules::readRules(rules11, QStringLiteral("config/base.1.1.xml"), false);
        QCOMPARE(rules11->version, QString("1.1"));

        foreach (const LayoutInfo *layoutInfo, rules11->layoutInfos) {
            QVERIFY(layoutInfo != nullptr);
            QVERIFY(!layoutInfo->name.isEmpty());
            QVERIFY(!layoutInfo->description.isEmpty());

            foreach (const VariantInfo *variantInfo, layoutInfo->variantInfos) {
                QVERIFY(variantInfo != nullptr);
                QVERIFY(!variantInfo->name.isEmpty());
                QVERIFY(!variantInfo->description.isEmpty());
            }
        }

        delete rules11;
    }

    void loadRulesBenchmark()
    {
        QBENCHMARK {
            Rules *rules = Rules::readRules(readExtras);
            delete rules;
        }
    }
};

// need kde libs for config-workspace.h used in xkb_rules.cpp
QTEST_MAIN(RulesTest)

#include "xkb_rules_test.moc"
