#include "xmlconfig.h"

#include <QFile>
#include <QDebug>

using namespace Config;

class Impl_ParseException : public ParseException
{
public:
    Impl_ParseException(const QString &title, const QString &reason)
    {
        temp_string = QString("%1：%2").arg(title, reason);
        title_length = title.length();
    }
    virtual ~Impl_ParseException() = default;
    // exception interface
public:
    virtual const char *what() const override{
        return temp_string.toLocal8Bit();
    }

    // ParseException interface
public:
    virtual QString reason() const override{ return temp_string.mid(title_length+1);}
    virtual QString title() const override{ return temp_string.mid(0, title_length);}

private:
    int title_length;
    QString temp_string;
};



XMLConfig::XMLConfig(QObject *parent)
    : Configration(parent)
{
}

void XMLConfig::loadFile(const QString &path)
{
    file_path = path;
    QFile config(path);
    if(!config.exists()){
        if(!config.open(QIODevice::WriteOnly|QIODevice::Text))
            throw new Impl_ParseException("解析错误", QString("指定路径无法创建配置文件：%1").arg(path));

        QTextStream tout(&config);
        tout << "<?xml version='1.0'?>" << endl;
        tout << "<config />" << endl;

        tout.flush();
        config.flush();
        config.close();
    }


    if(!config.open(QIODevice::Text|QIODevice::ReadOnly))
        throw new Impl_ParseException("解析错误", QString("指定路径：%1，配置文件无法打开").arg(path));

    QString err_str; int err_row, err_col;
    if(!doc_ins.setContent(&config, false, &err_str, &err_row, &err_col))
        throw new Impl_ParseException("解析错误", QString("指定配置文件：%1，row:%2,col:%3,格式错误。").arg(err_col).arg(err_row).arg(err_col));
}

QDir XMLConfig::currentDir() const
{
    return QFileInfo(file_path).dir();
}

void XMLConfig::save() const
{
    QFile fout(this->file_path);
    if(!fout.open(QIODevice::WriteOnly | QIODevice::Text))
        throw  new  Impl_ParseException("配置文件保存错误", QString("指定配置文件%1保存过程中，无法打开").arg(file_path));

    QTextStream tout(&fout);
    tout << this->doc_ins.toString(4);
    tout.flush();
    fout.flush();
    fout.close();
}

void XMLConfig::deleteX(const QList<QString> &path)
{
    auto elm = local_exists_elm(doc_ins.documentElement(), path);
    if(elm.isNull())
        return;

    elm.parentNode().removeChild(elm);
}

void XMLConfig::setConfig(const QList<QString> &path, const QString &value)
{
    auto doc = doc_ins.documentElement();
    auto elm = rebuild_exists_elms(doc, path);

    elm.setAttribute("value", value);
}

QString XMLConfig::getConfig(const QList<QString> &path) const
{
    auto elm = local_exists_elm(doc_ins.documentElement(), path);
    if(elm.isNull())
        return "";

    return elm.attribute("value");
}

void XMLConfig::setList(const QList<QString> &path, const QList<QString> &list)
{
    auto root = doc_ins.documentElement();
    auto telm = rebuild_exists_elms(root, path);
    auto childs = telm.childNodes();

    for(auto idx= 0; idx<childs.count(); ++idx){
        auto node = childs.at(idx);

        if(node.isElement() && node.toElement().tagName() == "list")
            telm.removeChild(node);
    }

    for(auto &it : list){
        auto nelm = doc_ins.createElement("list");
        nelm.setAttribute("value", it);
        telm.appendChild(nelm);
    }
}

QList<QString> XMLConfig::getList(const QList<QString> &path) const
{
    auto elm = local_exists_elm(doc_ins.documentElement(), path);

    QList<QString> rets;
    auto childs = elm.childNodes();
    for(auto idx=0; idx<childs.count(); ++idx){
        auto node = childs.at(idx);

        if(node.isElement() && node.toElement().tagName() == "list"){
            rets << node.toElement().attribute("value");
        }
    }

    return rets;
}

void XMLConfig::setMap(const QList<QString> &path, const QHash<QString, QString> &map)
{
    auto root = doc_ins.documentElement();
    auto telm = rebuild_exists_elms(root, path);
    auto childs = telm.childNodes();

    QList<QDomNode> nodes;
    for(auto idx=0; idx<childs.count(); ++idx){
        auto child = childs.at(idx);
        if(child.isElement() && child.toElement().tagName() == "map")
            telm.removeChild(child);
    }

    for(auto &key : map.keys()){
        auto nelm = doc_ins.createElement("map");
        nelm.setAttribute("key", key);
        nelm.setAttribute("value", map[key]);
        telm.appendChild(nelm);
    }
}

QHash<QString, QString> XMLConfig::getMap(const QList<QString> &path) const
{
    auto elm = local_exists_elm(doc_ins.documentElement(), path);

    QHash<QString, QString> rets;
    auto childs = elm.childNodes();
    for(auto idx=0; idx<childs.count(); ++idx){
        auto node = childs.at(idx);
        if(node.isElement()){
            auto elm = node.toElement();

            if(elm.tagName() == "map")
                rets[elm.attribute("key")] = elm.attribute("value");
        }
    }

    return rets;
}

QDomElement XMLConfig::local_exists_elm(const QDomElement &elm_base, const QList<QString> &path, int level) const
{
    auto child = elm_base.childNodes();
    for(auto idx=0; idx<child.count(); ++idx){
        auto ins = child.at(idx);
        if(ins.isElement()){
            auto elm = ins.toElement();
            if(elm.tagName() == "path" && elm.attribute("key") == path[level]){

                // 完成过滤，进行匹配
                if(level == path.size()-1)
                    return elm;

                return local_exists_elm(elm, path, level+1);
            }
        }
    }
    return QDomElement();
}

QDomElement XMLConfig::rebuild_exists_elms(QDomElement &elm_base, const QList<QString> &path, int level)
{
    auto childnodes = elm_base.childNodes();
    for(auto idx=0; idx<childnodes.count(); ++idx){
        auto ins = childnodes.at(idx);
        if(ins.isElement()){
            auto eins = ins.toElement();
            if(eins.tagName() == "path" && eins.attribute("key") == path[level]){
                // 完成过滤，进行匹配

                if(level == path.size() - 1)
                    return eins;

                return rebuild_exists_elms(eins, path, level+1);
            }
        }
    }
    auto nelm = elm_base.ownerDocument().createElement("path");
    nelm.setAttribute("key", path[level]);
    elm_base.appendChild(nelm);

    if(level < path.count() -1)
        return rebuild_exists_elms(nelm, path, level+1);
    return nelm;
}





















