#include "Calibrationpara.h"
#include <QDebug>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
#include <QByteArray>
#include <QFile>
#include <algorithm>


ParaItem::ParaItem(){

     max_volume = 0;   // L per unit
     keep_time = 0;    // s

     outermax = 0;
     outermin = 0;
     outerkp = 0;
     outerki = 0;
     outerkd = 0;

     innermax = 0;
     innermin = 0;
     innerkp = 0;
     innerki = 0;
     innerkd = 0;
     innermaxI = 0;
     innerIstep = 0;
     innerDstep = 0;

}
ParaItem::~ParaItem()
{

}

bool sortByVolume(const ParaItem& item1, const ParaItem& item2){
    return (item1.max_volume < item2.max_volume); //升序排列
}

static CalibrationPara* _calibrationPara = nullptr;

CalibrationPara::CalibrationPara(QObject* parent)
    :QObject(parent)
{

}

CalibrationPara::~CalibrationPara(){

}

CalibrationPara* CalibrationPara::instance(){
    if(nullptr == _calibrationPara){
        _calibrationPara = new CalibrationPara();
    }

    return _calibrationPara;
}

void CalibrationPara::init()
{
    m_paras.clear();
    load_parameters();
    if(m_paras.empty()){
        add_default();
        save_parameters();
    }

    std::sort(m_paras.begin(), m_paras.end(), sortByVolume);
}

QJsonObject CalibrationPara::get_paras()
{
    QJsonArray pidArray;
    //QJsonArray itemArray;
    QJsonObject rootObj;

    int count = static_cast<int>(m_paras.size());
    //if(count > 5){
    //    count=5;
    //}
    qDebug() << "m_paras.size:" << count;

    rootObj.insert("count", count);

    for(int i=0; i < count; i++){

        QJsonObject stdObj;

        stdObj.insert("max_volume", m_paras[i].max_volume);
        stdObj.insert("keep_time", m_paras[i].keep_time);

        stdObj.insert("outermax", m_paras[i].outermax);
        stdObj.insert("outermin", m_paras[i].outermin);
        stdObj.insert("outerkp", m_paras[i].outerkp);
        stdObj.insert("outerki", m_paras[i].outerki);
        stdObj.insert("outerkd", m_paras[i].outerkd);

        stdObj.insert("innermax", m_paras[i].innermax);
        stdObj.insert("innermin", m_paras[i].innermin);
        stdObj.insert("innerkp", m_paras[i].innerkp);
        stdObj.insert("innerki", m_paras[i].innerki);
        stdObj.insert("innerkd", m_paras[i].innerkd);
        stdObj.insert("innermaxI", m_paras[i].innermaxI);
        stdObj.insert("innerIstep", m_paras[i].innerIstep);
        stdObj.insert("innerDstep", m_paras[i].innerDstep);

        pidArray.append(stdObj);
    }

    rootObj.insert("pids", pidArray);

    return rootObj;

}

bool CalibrationPara::set_paras(const QString& jsonStr)
{
    QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonStr.toUtf8());
    QJsonObject jsonObj = jsonDoc.object();

    QJsonArray  jsonArray;

    if(jsonObj.contains("pids") && jsonObj.value("pids").isArray()){
        jsonArray = jsonObj.value("pids").toArray();

         m_paras.clear();

        for(int i=0; i < jsonArray.size(); i++){

            ParaItem para;
            QJsonObject childObj = jsonArray[i].toObject();

            if(childObj.contains("max_volume") && childObj.value("max_volume").isDouble()){
                para.max_volume = childObj.value("max_volume").toDouble();
            }

            if(childObj.contains("keep_time") && childObj.value("keep_time").isDouble()){
                para.keep_time = childObj.value("keep_time").toDouble();
            }


            if(childObj.contains("outermax") && childObj.value("outermax").isDouble()){
                para.outermax = childObj.value("outermax").toDouble();
            }
            if(childObj.contains("outermin") && childObj.value("outermin").isDouble()){
                para.outermin = childObj.value("outermin").toDouble();
            }
            if(childObj.contains("outerkp") && childObj.value("outerkp").isDouble()){
                para.outerkp = childObj.value("outerkp").toDouble();
            }
            if(childObj.contains("outerki") && childObj.value("outerki").isDouble()){
                para.outerki = childObj.value("outerki").toDouble();
            }
            if(childObj.contains("outerkd") && childObj.value("outerkd").isDouble()){
                para.outerkd = childObj.value("outerkd").toDouble();
            }


            if(childObj.contains("innermax") && childObj.value("innermax").isDouble()){
                para.innermax = childObj.value("innermax").toDouble();
            }
            if(childObj.contains("innermin") && childObj.value("innermin").isDouble()){
                para.innermin = childObj.value("innermin").toDouble();
            }
            if(childObj.contains("innerkp") && childObj.value("innerkp").isDouble()){
                para.innerkp = childObj.value("innerkp").toDouble();
            }
            if(childObj.contains("innerki") && childObj.value("innerki").isDouble()){
                para.innerki = childObj.value("innerki").toDouble();
            }
            if(childObj.contains("innerkd") && childObj.value("innerkd").isDouble()){
                para.innerkd = childObj.value("innerkd").toDouble();
            }
            if(childObj.contains("innermaxI") && childObj.value("innermaxI").isDouble()){
                para.innermaxI = childObj.value("innermaxI").toDouble();
            }
            if(childObj.contains("innerIstep") && childObj.value("innerIstep").isDouble()){
                para.innerIstep = childObj.value("innerIstep").toDouble();
            }
            if(childObj.contains("innerDstep") && childObj.value("innerDstep").isDouble()){
                para.innerDstep = childObj.value("innerDstep").toDouble();
            }

            m_paras.push_back(para);

        }

        std::sort(m_paras.begin(), m_paras.end(), sortByVolume);
        qDebug() << "m_paras.size:" << m_paras.size();

    }else{
        return false;
    }

    save_parameters();

    return true;
}

void CalibrationPara::load_parameters()
{
    QFile file("./Calibration.json");
    if(!file.open(QIODevice::ReadOnly|QIODevice::Text)){
        //QJsonObject jsonObject;
        //jsonObject["name"] =""
        qDebug() << "load_parameters file open failed ";
        return ;
    }

    QTextStream jsonstream(&file);

    jsonstream.setCodec("UTF-8");

    //jsonstream.readAll();
    qDebug() << "load_parameters enter";

    QString jsonString = jsonstream.readAll();
    file.close();


    QJsonParseError json_error;
    QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonString.toUtf8(), &json_error);
    if(json_error.error == QJsonParseError::NoError){
        QJsonObject rootObj = jsonDoc.object();
        QJsonArray  jsonArray;

        if(rootObj.contains("pids") && rootObj.value("pids").isArray()){
            jsonArray = rootObj.value("pids").toArray();
            for(int i=0; i < jsonArray.size(); i++){

                ParaItem para;

                QJsonObject childObj = jsonArray[i].toObject();

                if(childObj.contains("max_volume") && childObj.value("max_volume").isDouble()){
                    para.max_volume = childObj.value("max_volume").toDouble();
                }

                if(childObj.contains("keep_time") && childObj.value("keep_time").isDouble()){
                    para.keep_time = childObj.value("keep_time").toDouble();
                }


                if(childObj.contains("outermax") && childObj.value("outermax").isDouble()){
                    para.outermax = childObj.value("outermax").toDouble();
                }
                if(childObj.contains("outermin") && childObj.value("outermin").isDouble()){
                    para.outermin = childObj.value("outermin").toDouble();
                }
                if(childObj.contains("outerkp") && childObj.value("outerkp").isDouble()){
                    para.outerkp = childObj.value("outerkp").toDouble();
                }
                if(childObj.contains("outerki") && childObj.value("outerki").isDouble()){
                    para.outerki = childObj.value("outerki").toDouble();
                }
                if(childObj.contains("outerkd") && childObj.value("outerkd").isDouble()){
                    para.outerkd = childObj.value("outerkd").toDouble();
                }


                if(childObj.contains("innermax") && childObj.value("innermax").isDouble()){
                    para.innermax = childObj.value("innermax").toDouble();
                }
                if(childObj.contains("innermin") && childObj.value("innermin").isDouble()){
                    para.innermin = childObj.value("innermin").toDouble();
                }
                if(childObj.contains("innerkp") && childObj.value("innerkp").isDouble()){
                    para.innerkp = childObj.value("innerkp").toDouble();
                }
                if(childObj.contains("innerki") && childObj.value("innerki").isDouble()){
                    para.innerki = childObj.value("innerki").toDouble();
                }
                if(childObj.contains("innerkd") && childObj.value("innerkd").isDouble()){
                    para.innerkd = childObj.value("innerkd").toDouble();
                }
                if(childObj.contains("innermaxI") && childObj.value("innermaxI").isDouble()){
                    para.innermaxI = childObj.value("innermaxI").toDouble();
                }
                if(childObj.contains("innerIstep") && childObj.value("innerIstep").isDouble()){
                    para.innerIstep = childObj.value("innerIstep").toDouble();
                }
                if(childObj.contains("innerDstep") && childObj.value("innerDstep").isDouble()){
                    para.innerDstep = childObj.value("innerDstep").toDouble();
                }

                m_paras.push_back(para);

            }
        }


    }else{
        qDebug() << "json_error.error occurred";
    }

    qDebug() << "load_parameters m_paras.size:" << m_paras.size();
}

void CalibrationPara::save_parameters()
{
    QJsonArray pidArray;
    //QJsonArray itemArray;
    QJsonObject rootObj;


    for(int i=0; i < static_cast<int>(m_paras.size()); i++){

        QJsonObject stdObj;

        stdObj.insert("max_volume", m_paras[i].max_volume);
        stdObj.insert("keep_time", m_paras[i].keep_time);

        stdObj.insert("outermax", m_paras[i].outermax);
        stdObj.insert("outermin", m_paras[i].outermin);
        stdObj.insert("outerkp", m_paras[i].outerkp);
        stdObj.insert("outerki", m_paras[i].outerki);
        stdObj.insert("outerkd", m_paras[i].outerkd);

        stdObj.insert("innermax", m_paras[i].innermax);
        stdObj.insert("innermin", m_paras[i].innermin);
        stdObj.insert("innerkp", m_paras[i].innerkp);
        stdObj.insert("innerki", m_paras[i].innerki);
        stdObj.insert("innerkd", m_paras[i].innerkd);
        stdObj.insert("innermaxI", m_paras[i].innermaxI);
        stdObj.insert("innerIstep", m_paras[i].innerIstep);
        stdObj.insert("innerDstep", m_paras[i].innerDstep);

        pidArray.append(stdObj);
    }

    rootObj.insert("pids", pidArray);

    QJsonDocument jsonDoc;
    jsonDoc.setObject(rootObj);

    QByteArray byteArray = jsonDoc.toJson(QJsonDocument::Indented);
    QFile file("./Calibration.json");
    if(!file.open(QIODevice::WriteOnly | QIODevice::Text))
    {
        qDebug() << QString("fail to open the file: %1, %2, %3").arg(__FILE__).arg(__LINE__).arg(__FUNCTION__);
        return;
    }
    QTextStream out(&file);
    out.setCodec("UTF-8");
    out << byteArray;
    file.close();

}


void CalibrationPara::add_default()
{
    ParaItem para;

#if 0
    para.max_volume = 0;
    para.keep_time = 0;

    para.outermax = 0;
    para.outermin = 0;
    para.outerkp = 0;
    para.outerki = 0;
    para.outerkd = 0;

    para.innermax = 0;
    para.innermin = 0;
    para.innerkp = 0;
    para.innerki = 0;
    para.innerkd = 0;
    para.innermaxI = 0;
    para.innerIstep = 0;
    para.innerDstep = 0;

    m_paras.push_back(para);

    para.max_volume = 0;
    para.keep_time = 0;

    para.outermax = 0;
    para.outermin = 0;
    para.outerkp = 0;
    para.outerki = 0;
    para.outerkd = 0;

    para.innermax = 0;
    para.innermin = 0;
    para.innerkp = 0;
    para.innerki = 0;
    para.innerkd = 0;
    para.innermaxI = 0;
    para.innerIstep = 0;
    para.innerDstep = 0;

    m_paras.push_back(para);
#endif
    // <= 200  主要是系统自测
    para.max_volume = 200;
    para.keep_time = 30;

    para.outermax = 0.1;
    para.outermin = 0;
    para.outerkp = 0.05;
    para.outerki = 0;
    para.outerkd = 0;

    para.innermax = 200;
    para.innermin = -50;
    para.innerkp = 1000;
    para.innerki = 80;
    para.innerkd = 60;
    para.innermaxI = 10;
    para.innerIstep = 1;
    para.innerDstep = 10;

    m_paras.push_back(para);

    //<= 1000
    para.max_volume = 1000;
    para.keep_time = 30;

    para.outermax = 0.1;
    para.outermin = 0;
    para.outerkp = 0.05;
    para.outerki = 0;
    para.outerkd = 0;

    para.innermax = 200;
    para.innermin = -40;
    para.innerkp = 1000;
    para.innerki = 80;
    para.innerkd = 60;
    para.innermaxI = 10;
    para.innerIstep = 1;
    para.innerDstep = 10;

    m_paras.push_back(para);

    //<= 5000
    para.max_volume = 5000;
    para.keep_time = 60; //1 min

    para.outermax = 0.1;
    para.outermin = 0;
    para.outerkp = 0.1;
    para.outerki = 0;
    para.outerkd = 0;

    para.innermax = 800;
    para.innermin = -30;
    para.innerkp = 3000;
    para.innerki = 80;
    para.innerkd = 60;
    para.innermaxI = 15;
    para.innerIstep = 2;
    para.innerDstep = 10;

    m_paras.push_back(para);

    //<= 10000
    para.max_volume = 10000;
    para.keep_time = 120; //2 min

    para.outermax = 0.1;
    para.outermin = 0;
    para.outerkp = 0.2;
    para.outerki = 0;
    para.outerkd = 0;

    para.innermax = 800;
    para.innermin = -15;
    para.innerkp = 5000;
    para.innerki = 80;
    para.innerkd = 60;
    para.innermaxI = 30;
    para.innerIstep = 8;
    para.innerDstep = 30;

    m_paras.push_back(para);

    //<= 20000
    para.max_volume = 20000;
    para.keep_time = 180;  // 3min

    para.outermax = 0.1;
    para.outermin = 0;
    para.outerkp = 0.2;
    para.outerki = 0;
    para.outerkd = 0;

    para.innermax = 800;
    para.innermin = -15;
    para.innerkp = 8000;
    para.innerki = 80;
    para.innerkd = 60;
    para.innermaxI = 40;
    para.innerIstep = 15;
    para.innerDstep = 30;

    m_paras.push_back(para);

    //<= 30000
    para.max_volume = 30000;
    para.keep_time = 240;  // 4min

    para.outermax = 0.1;
    para.outermin = 0;
    para.outerkp = 0.4;
    para.outerki = 0;
    para.outerkd = 0;

    para.innermax = 800;
    para.innermin = 0;
    para.innerkp = 8000;
    para.innerki = 80;
    para.innerkd = 60;
    para.innermaxI = 40;
    para.innerIstep = 20;
    para.innerDstep = 60;

    m_paras.push_back(para);

    //<= 40000
    para.max_volume = 40000;
    para.keep_time = 300;  // 5min

    para.outermax = 0.1;
    para.outermin = 0;
    para.outerkp = 0.4;
    para.outerki = 0;
    para.outerkd = 0;

    para.innermax = 800;
    para.innermin = 0;
    para.innerkp = 8000;
    para.innerki = 80;
    para.innerkd = 60;
    para.innermaxI = 40;
    para.innerIstep = 20;
    para.innerDstep = 60;

    m_paras.push_back(para);

    //<= 1000000  最大
    para.max_volume = 1000000;
    para.keep_time = 300;  // 5min

    para.outermax = 0.1;
    para.outermin = 0;
    para.outerkp = 0.4;
    para.outerki = 0;
    para.outerkd = 0;

    para.innermax = 800;
    para.innermin = 0;
    para.innerkp = 8000;
    para.innerki = 80;
    para.innerkd = 60;
    para.innermaxI = 40;
    para.innerIstep = 20;
    para.innerDstep = 60;

    m_paras.push_back(para);

    std::sort(m_paras.begin(), m_paras.end(), sortByVolume);
}

bool CalibrationPara::get_pidPara(int volume, ParaItem& item)
{
    int min_volme = 0;
    bool ret_value = false;

   #if 0

    std::sort(m_paras.begin(), m_paras.end(), sortByVolume);

    for(std::vector<ParaItem>::iterator it = m_paras.begin(); it != m_paras.end(); it++){
        if(it->max_volume >= volume){
            item = *it;
            ret_value = true;
        }
    }

   #else

    for(std::vector<ParaItem>::iterator it = m_paras.begin(); it != m_paras.end(); it++){
        if(it->max_volume >= volume){

            if(0 == min_volme){
                min_volme = it->max_volume;
            }else{

                if(it->max_volume < min_volme){
                    min_volme = it->max_volume;
                }
            }

        }
    }

    for(std::vector<ParaItem>::iterator it = m_paras.begin(); it != m_paras.end(); it++){
        if(it->max_volume == min_volme){
            item = *it;
            ret_value = true;
        }
    }
#endif
    return ret_value;
}

