#include "CivHydrCalc.h"
#include "CivDbConnection.h"
#include "CivTypes.h"
#include "Epanet2.h"
#include "CivInp.h"
#include "../pandaDbManager/CivCommonUtils.h"
#include <time.h> 

#include <iostream>

CivHydrCalc::CivHydrCalc(char* uri, std::string netName)
    : mNetname(netName)
{
    mDbConn = new CivDbConnection(uri);
}

CivHydrCalc::~CivHydrCalc()
{
    delete mDbConn;
}

bool CivHydrCalc::hydrSimulation(char* fileName, char* rptFile, char* binOutFile)
{
    if (!exportInp(fileName))
        return false;

    short hydCode = 1;
    long  t(0), tstep(0);
    int   iTime(0);

    int  errcode = 0;
    errcode = ENopen(fileName, rptFile, binOutFile);
    if (errcode > 0)
    {
        ENclose();
        return false;
    }
  
    if (ENopenH() > 0)
    {
        ENclose();
        return false;
    }
 
    if (ENinitH(0) > 0) 
    {
        ENclose();
        return false;
    }

    float dHStep;
    ENGetVal(4, &dHStep);

   //  hydrCompute(errcode);
    bool isFirst = true;
    // 计算
    do
    {
        int nLinkCount;
        ENgetcount(EN_LINKCOUNT, &nLinkCount);
        /*for (int i = 1; i <= nLinkCount; i++)
        {
            int linkType;
            char id[128] = "";
            ENgetlinktype(i, &linkType);
            ENgetlinkid(i, id);
        }*/

        if (ENrunH(&t) > 100)	// errcode > 100 是错误
        {
            ENclose();
            return false;
        }
        ENnextH(&tstep);
        if (isFirst)
        {
            mDbConn->deleteByDate(NODERESULTTABLE, "日期", CurrentDate);
            mDbConn->deleteByDate(PIPERESULTTABLE, "日期", CurrentDate);
            isFirst = false;
        }
        // 获取当前节点和管段数据
        getNodeResult(iTime);
        getLinkResult(iTime);
        iTime++;   
    } while (tstep > 0);

    // 更新管网数据库数据
    (void)mDbConn->updateNodeByInterval(CurrentDate, "0");
    (void)mDbConn->updateLinkByInterval(CurrentDate,"0");

    ENcloseH();
    ENclose();
    return true;
}

bool CivHydrCalc::qualitySimulation(char* inpFile, char* rptFile, char* binOutFile)
{
    if (!exportInp(inpFile))
        return false;


    // 运行水质运算需要先进行水力运算,没有直接采用ENSolveH 是为了考虑减压阀的压力曲线设置
    long  t(0), tstep(0);
    int   iTime(0);
 
    int  errcode = 0;


    errcode = ENopen(inpFile, rptFile, binOutFile);
    if (errcode > 0)
    {
  
        ENclose();
        return false;
    }

    errcode = ENopenH();
    if (errcode > 0)
    {
        ENclose();
        return false;
    }
    
    // 根据类型 更改值
    errcode = ENinitH(EN_SAVE);
    if (errcode > 0)
    {
        ENcloseH();
        ENclose();
        return false;
    }
    bool isFirst = true;
    do
    {
        int nLinkCount;
        ENgetcount(EN_LINKCOUNT, &nLinkCount);
     
        errcode = ENrunH(&t);	// errcode > 100 是错误
        if (errcode > 100)
        {
            ENopenQ();
            ENclose();
            return false;
        }
        if (isFirst)
        {
            mDbConn->deleteByDate(NODERESULTTABLE, "日期", CurrentDate);
            mDbConn->deleteByDate(PIPERESULTTABLE, "日期", CurrentDate);
            isFirst = false;
        }
        // 获取当前节点和管段数据
        getNodeResult(iTime);
        getLinkResult(iTime);
        ENnextH(&tstep);

        iTime++;

    } while (tstep > 0);

    ENcloseH();

    // 进行水质分析
    ENopenQ();
    ENinitQ(1);

    t = 0;
    tstep = 0;
    iTime = 0;
    do
    {
        errcode = ENrunQ(&t);	// errcode > 100 是错误
        if (errcode > 100)
        {
            ENcloseQ();
            ENclose();
            return false;
        }
  
        // 获取水质模拟结果
        getNodeQuality(iTime);
        getLinkQuality(iTime);

        ENstepQ(&tstep);

        iTime++;

    } while (tstep > 0);


    ENcloseQ();
    ENclose();

    // 更新数据
    if (!mResultCache.updateToDb(mDbConn))
        return false;


    // 更新管网数据库数据
    (void)mDbConn->updateNodeByInterval(CurrentDate, "0");
    (void)mDbConn->updateLinkByInterval(CurrentDate, "0");
    
    return true;
}

bool CivHydrCalc::exportInp(char* fileName)
{
    //1 ,获取管网组件
    if (!mDbConn)
        return false;

    if (!mDbConn->open())
        return false;


   /*bool pattern = mDbConn->createTable(*CivPatternTableTemp());
   bool  cii = mDbConn->createTable(OPTIONTTABLE, "public", CivTableTemp::PARAMETER_TABLE);
   bool curve = mDbConn->createTable(CURVETABLE, "public", CivTableTemp::CURVE_TABLE);
   bool node = mDbConn->createTable(NODERESULTTABLE, "public", CivTableTemp::NODE_TABLE);
   bool link = mDbConn->createTable(PIPERESULTTABLE, "public", CivTableTemp::PIPE_TABLE);*/

    // 获取表格
    Tables tables = mDbConn->getTables(mNetname);

    //2,解析管网组件
    // 解析管网组件顺序: 管线放最后
    CivInp civInp(CivInp::HOURS);

    int total = tables.size();
    for (int i=0;i<total;i++)
    {
        std::string table = tables[i];
        std::cout << table << std::endl;
        if (PIPELINE == table)
            continue;

        assemble(table,civInp);
    }
    assemble(PIPELINE, civInp);

 
    // 设置曲线
    // Componets curves = mDbConn->getComponets(CURVETABLE);

    // 初始化参数
    Options options;
    options.units = Options::UNITS::LPS;
    options.TRIALS = "40";
    options.ACCURACY = "0.001 ";
    options.EMITTEREXPONENT = "0.5";
    options.SpecificGravity = "0.998";
    options.headLoss = Options::HEADLOSS::HW;
    options.TOLERANCE = "0.0166666666666667 ";
    options.quality = Options::QUALITY::AGE;

     std::map<std::string, std::string> optMap;
    optMap.insert(std::pair<std::string, std::string>("DAMPLIMIT","0"));
    optMap.insert(std::pair<std::string, std::string>("CHECKFREQ", "2"));
    optMap.insert(std::pair<std::string, std::string>("MAXCHECK", "10"));

    civInp.setOption(options, optMap);

    // 设置
    civInp.setDuration(24);
    civInp.setTimestep("PATTERN", 1);
    civInp.setTimestep("HYDRAULIC", 1);
    civInp.setTimestep("QUALITY", 1);

    civInp.setTimeStart("REPORT", 1);

    civInp.writeToFile(fileName);
    return true;
}

void CivHydrCalc::assemble(const std::string& table, CivInp& inp)
{    
    // 根据组建类型解析字段
    if (PIPELINE == table)
    {
        // 管线
        //;ID  Node1   Node2   Length   Diameter  	Roughness  MinorLoss  Status
        // 必备参数
        Componets comps = mDbConn->getComponets(table);
        PipeFields pipes;
        Civitems items;
        int total = comps.size();
        for (int i =0 ;i< total;i++)
        {
            Componet comp = comps[i];

            // 过滤掉与阀门和水泵连接的管线
            Str number = comp.find("id")->second;
            FiledFilter filter = mDbConn->getFilter();
            auto iter = filter.find(PIPELINE);
            if (iter != filter.end())
            {
                auto lineFilter = iter->second;
                if (lineFilter.count(number) > 0)
                    continue;
            }
           
            Str id = comp.find(pipes.ID)->second;
            Str startNode = comp.find(pipes.Node1)->second;
            Str endNode = comp.find(pipes.Node2)->second;
            Str length = comp.find(pipes.Length)->second;
            Str diameter = comp.find(pipes.Diameter)->second;
            Str roughness = comp.find(pipes.Roughness)->second;
            Str minorLoss = comp.find(pipes.MinorLoss)->second;
            Str status = comp.find(pipes.Status)->second;

            // 注意顺序
            CivItem civItem;
            if(id==""||id.empty())
                id = "GD" + comp.find("id")->second;

            civItem.push_back(id);
            civItem.push_back(startNode);
            civItem.push_back(endNode);
            civItem.push_back(length);
            civItem.push_back(diameter);
            civItem.push_back(roughness);
            civItem.push_back(minorLoss);
            civItem.push_back(status);
            items.push_back(civItem);
        }
        inp.writeComponet(items,_PIPES);
       
    }
    else if (PIPENODE == table)
    {
        Componets comps = mDbConn->getComponets(table);
        // 节点参数
        // ;ID              	Elev        	Demand      	Pattern
         // 坐标  
        //;Node            	X-Coord         	Y-Coord
        JunctionsFields jontionFields;
        Civitems jounItems;
        Civitems nodeCoordItems;
        Civitems patternItems;
        Civitems initQaulityItems;

        int total = comps.size();
        std::set<Str> patterNams;
        for (int i = 0; i < total; i++)
        {
            Componet comp = comps[i];
            Str id = comp.find(jontionFields.ID)->second;
            Str elevation = comp.find(jontionFields.Elev)->second;
            Str demand = comp.find(jontionFields.Demand)->second;
            Str pattern = comp.find(jontionFields.Pattern)->second;
            Str x_coord = comp.find(jontionFields.X_Coord)->second;
            Str y_coord = comp.find(jontionFields.Y_Coord)->second;

            // 注意顺序
            //1, 节点
            CivItem civItem;
            civItem.push_back(id);
            civItem.push_back(elevation);
            civItem.push_back(demand);
            civItem.push_back(pattern);
      
            jounItems.push_back(civItem);

            // 2, 节点坐标
            CivItem nodeCoordItem;
            nodeCoordItem.push_back(id);
            nodeCoordItem.push_back(x_coord);
            nodeCoordItem.push_back(y_coord);

            nodeCoordItems.push_back(nodeCoordItem);
            if (!pattern.empty())
            {
                patterNams.insert(pattern);
            }

            // 初始水质
            Str initQual = comp.find(jontionFields.InitQual)->second;
            CivItem initQualItem;
            initQualItem.push_back(id);
            initQualItem.push_back(initQual);

            initQaulityItems.push_back(initQualItem);           
        }

        Componets patterns = mDbConn->getComponets(PATTERNTABLE);
        PatternFields fileds;
        size_t patternSize = patterns.size();
        for (int i = 0; i < patternSize; i++)
        {
            Componet pattern = patterns[i];
            Str paternName = pattern.find(fileds.Name)->second;
            Str paternValue = pattern.find(fileds.Value)->second;

            CivItem item;
            item.push_back(paternName);
            item.push_back(paternValue);

            patternItems.push_back(item);
        }
        inp.writeComponet(jounItems,_JUNCTIONS);
        inp.writeComponet(nodeCoordItems, _COORDS);
        inp.writeComponet(patternItems, _PATTERNS);
        inp.writeComponet(initQaulityItems, _QUALITY);
    }
    else if (RESIVOIR == table) // 水源
    {
        Componets comps = mDbConn->getComponets(table);
        //;ID              	Head        	Pattern
        Civitems coordItems;
        Civitems resirvoirItems;

        ResivoirFields fields;
        int total = comps.size();
        for (int i = 0; i < total; i++)
        {
            Componet comp = comps[i];
            Str id = comp.find(fields.ID)->second;
            Str elevation = comp.find(fields.Elev)->second;
            Str flow = comp.find(fields.Flow)->second;
            Str head = comp.find(fields.Head)->second;
            Str pattern = comp.find(fields.Pattern)->second;
            Str x_coord = comp.find(fields.X_Coord)->second;
            Str y_coord = comp.find(fields.Y_Coord)->second;

            // 注意顺序
            //1, coord
            CivItem civItem;
            civItem.push_back(id);
            civItem.push_back(head);
            civItem.push_back(pattern);

            resirvoirItems.push_back(civItem);

            // 2, 节点坐标
            CivItem nodeCoordItem;
            nodeCoordItem.push_back(id);
            nodeCoordItem.push_back(x_coord);
            nodeCoordItem.push_back(y_coord);

            coordItems.push_back(nodeCoordItem);
        }

        inp.writeComponet(coordItems, _COORDS);
        inp.writeComponet(resirvoirItems, _RESERVOIRS);

    }
    else if (PUMP == table) // 水泵
    {
        //;ID              	Node1           	Node2           	Parameters
        Componets comps = mDbConn->getLikelyPipes(table);
        PumpFields fields;

        Civitems pumpItems;
        int total = comps.size();
        for (int i = 0; i < total; i++)
        {
            Componet comp = comps[i];
            Str id = comp.find(fields.ID)->second;
            Str node1 = comp.find(fields.node1)->second;
            Str node2 = comp.find(fields.node2)->second;

            std::vector<Str> vec;
            Str head = comp.find(fields.head)->second;
            if (!head.empty())
                vec.push_back("HEAD " + head);

            Str power = comp.find(fields.power)->second;
            if (!power.empty())
                vec.push_back("POWER " + power);

            Str pattern = comp.find(fields.pattern)->second;
            if (!pattern.empty())
                vec.push_back("PATTERN " + pattern);

            Str speed = comp.find(fields.speed)->second;
            if (!speed.empty())
                vec.push_back("SPEED " + speed);

            // 水池数据
            CivItem tankNode;
            tankNode.push_back(id);
            tankNode.push_back(node1);
            tankNode.push_back(node2);

            // 构建参数
            char param[128] = " ";
            int total = vec.size();
            for(int i=0;i<total;i++)
            {
                strcat_s(param, vec[i].c_str());
            }
            tankNode.push_back(param);
            pumpItems.push_back(tankNode);
        }
        inp.writeComponet(pumpItems, _PUMPS);
    }
    else if (TANK == table) // 水池
    {
        Componets comps = mDbConn->getComponets(table);
        // ; ID              	Elevation   	InitLevel   	MinLevel    	MaxLevel    	Diameter    	MinVol      	VolCurve
        TanksFields tkfFileds;
        Civitems nodeCoordItems;
        Civitems tankItems;
        int total = comps.size();
        for (int i = 0; i < total; i++)
        {
            Componet comp = comps[i];
            Str id = comp.find(tkfFileds.ID)->second;
            Str elevation = comp.find(tkfFileds.Elev)->second;
            Str initLevel = comp.find(tkfFileds.InitLevel)->second;
            Str minLevel = comp.find(tkfFileds.MinLevel)->second;
            Str maxLevel = comp.find(tkfFileds.MaxLevel)->second;
            Str diametor = comp.find(tkfFileds.Diameter)->second;

            Str minVol = comp.find(tkfFileds.MinVol)->second;
       
            Str x_coord = comp.find(tkfFileds.X_Coord)->second;
            Str y_coord = comp.find(tkfFileds.Y_Coord)->second;
          
            // 水池数据
            CivItem tankNode;
            tankNode.push_back(id);
            tankNode.push_back(elevation);
            tankNode.push_back(initLevel);
            tankNode.push_back(minLevel);
            tankNode.push_back(maxLevel);
            tankNode.push_back(diametor);
            tankNode.push_back(minVol);
       
            tankItems.push_back(tankNode);

            // 2, 节点坐标
            CivItem nodeCoordItem;
            nodeCoordItem.push_back(id);
            nodeCoordItem.push_back(x_coord);
            nodeCoordItem.push_back(y_coord);

            nodeCoordItems.push_back(nodeCoordItem);   
        }
        inp.writeComponet(nodeCoordItems, _COORDS);
        inp.writeComponet(tankItems, _TANKS);
    }
    else
    {   
          // 阀门
          //;ID              	Node1           	Node2           	Diameter    	Type	Setting     	MinorLoss  
          Componets comps = mDbConn->getLikelyPipes(table);
          ValvesFields fields;

          Civitems vavleItems;
          int total = comps.size();
          for (int i = 0; i < total; i++)
          {
              Componet comp = comps[i];
              Str id = comp.find(fields.ID)->second;
              Str node1 = comp.find(fields.node1)->second;
              Str node2 = comp.find(fields.node2)->second;
              Str diametor = comp.find(fields.Diameter)->second;
              Str label = comp.find(fields.type)->second;
              Str typeSeting = comp.find(fields.setting)->second;
              Str minorLoss = comp.find(fields.minorLoss)->second;

              // 阀门数据
              CivItem vavleNode;
              vavleNode.push_back(id);
              vavleNode.push_back(node1);
              vavleNode.push_back(node2);
              vavleNode.push_back(diametor);
              vavleNode.push_back(label);
              vavleNode.push_back(typeSeting);
              vavleNode.push_back(minorLoss);

              vavleItems.push_back(vavleNode);
          }
          inp.writeComponet(vavleItems, _VALVES);
     }
}

bool CivHydrCalc::getDataByInterval(char* date, char* interval)
{
    if (!mDbConn->open())
        return false;

    if (!mDbConn->updateNodeByInterval(date, interval))
        return false;

    if (!mDbConn->updateLinkByInterval(date, interval))
        return false;

    return true;
}

void CivHydrCalc::getNodeResult(short time)
{
    int nNodeCount;
    ENgetcount(EN_NODECOUNT, &nNodeCount);

    for (int i = 1; i <= nNodeCount; i++)
    {
        NodeResultItem nodeItem;
        // 编号
        ENgetnodeid(i, nodeItem.szNo);
        // 流量
        ENgetnodevalue(i, EN_DEMAND, &nodeItem.dFlow);
        // 水头
        ENgetnodevalue(i, EN_HEAD, &nodeItem.dHead);
        // 压力
        ENgetnodevalue(i, EN_PRESSURE, &nodeItem.dPressure);
        // 标高
        ENgetnodevalue(i, EN_ELEVATION, &nodeItem.dElevation);
        // 实际需水量
        ENgetnodevalue(i, EN_DEMAND, &nodeItem.dDemand);
        // 水池容量
        ENgetnodevalue(i, EN_TANKVOLUME, &nodeItem.dTankVolume);
        // 水池最大容量
        ENgetnodevalue(i, EN_MAXVOLUME, &nodeItem.dTankMaxVolume);
        // 液位高度
        ENgetnodevalue(i, EN_TANKLEVEL, &nodeItem.dTankLevel);
        //// 水质
        //ENgetnodevalue(i, EN_QUALITY, &nodeItem.dQuality);

        nodeItem.dInterval = time;

         // 放到缓存类中 
        mResultCache.addNodeItems(nodeItem);
    }
}

void CivHydrCalc::getLinkResult(short time)
{
    int  nLinkCount;
    ENgetcount(EN_LINKCOUNT, &nLinkCount);

    for (int i = 1; i <= nLinkCount; i++)
    {
        LinkResultItem linkItem;

        int *typeCode = (int*)malloc(sizeof(int));
     
        ENgetlinktype(i,typeCode );
        if (*typeCode != EN_PIPE)
        {
            free(typeCode);
            continue;
        }
        free(typeCode);

        // 编号
        ENgetlinkid(i, linkItem.szNo);
        // 流量 
        ENgetlinkvalue(i, EN_FLOW, &linkItem.dFlow);
        // 流向
        linkItem.nFlowDirect = (linkItem.dFlow >= 0) ? 0 : 1;
        // 流速
        ENgetlinkvalue(i, EN_VELOCITY, &linkItem.dVelocity);
        // 水头损失
        ENgetlinkvalue(i, EN_HEADLOSS, &linkItem.dHeadloss);
        // 单位水头损失
        float dPipeLen = 0.0f;;
        ENgetlinkvalue(i, EN_LENGTH, &dPipeLen);
        linkItem.dUnitHeadloss = (linkItem.dHeadloss / dPipeLen) * 1000;
        // 获取上点和本点
        int node1 = 0;
        int node2 = 0;
        ENgetlinknodes(i, &node1, &node2);
        // 上点水头
        ENgetnodevalue(node1, EN_HEAD, &linkItem.dFromNodHeadloss);
        // 本点水头
        ENgetnodevalue(node2, EN_HEAD, &linkItem.dToNodHeadloss);
     
        // 局部水头损失
        float fCoeff;
        ENgetlinkvalue(i, EN_MINORLOSS, &fCoeff);
        double dMinorloss = fCoeff * linkItem.dHeadloss;
        linkItem.dLocalHeadloss = dMinorloss;// 局部水头损失 

        // 摩擦水头损失
        linkItem.dFrictionHeadloss = linkItem.dHeadloss - linkItem.dLocalHeadloss;// 局部水头损失 

        // 状态
        float fState = 0;
        ENgetlinkvalue(i, EN_STATUS, &fState);
        if ((int)fState == 0)
            strcpy_s(linkItem.szStatus, "关闭");
        else
            strcpy_s(linkItem.szStatus, "开启");

        linkItem.dInterval = time;
        // 放到缓存类中 
        mResultCache.addLinkItems(linkItem);
    }
}

void CivHydrCalc::getNodeQuality(short time)
{
    int nNodeCount;
    ENgetcount(EN_NODECOUNT, &nNodeCount);

  
    for (int i = 1; i <= nNodeCount; i++)
    {
        char No[35];
        // 编号
        ENgetnodeid(i, No);
        float* quality = (float*)malloc(sizeof(float));
        // 水质
        ENgetnodevalue(i, EN_QUALITY, quality);
    
        mResultCache.addNodeQuality(*quality, time, No);
        free(quality);
    }
}

void CivHydrCalc::getLinkQuality(short time)
{
    int  nLinkCount;
    ENgetcount(EN_LINKCOUNT, &nLinkCount);
    int* typeCode = (int*)malloc(sizeof(int));
    float* quality = (float*)malloc(sizeof(float));
    for (int i = 1; i <= nLinkCount; i++)
    {
      
        ENgetlinktype(i, typeCode);
        if (*typeCode != EN_PIPE)
        {
            continue;
        }
        char lnkNo[35];
        // 编号
        ENgetlinkid(i, lnkNo);
        
        // 水质
        ENgetlinkvalue(i, EN_LINKQUAL, quality);  
        mResultCache.addLinkQuality(*quality, time, lnkNo);       
    } 
    free(typeCode);
    free(quality);
}
