#include "CivHydrCalc.h"
#include "CivDbConnection.h"
#include "CivTypes.h"
#include "CivCommonUtils.h"
#include "Epanet2.h"
#include "CivInp.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);
    // 计算
    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);

        // 获取当前节点和管段数据
        getNodeResult(iTime);
        getLinkResult(iTime);
        iTime++;   
    } while (tstep > 0);

    return true;
}

bool CivHydrCalc::qualitySimulation(char* inpFile, char* rptFile, char* binOutFile)
{
    return true;
}

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

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

    bool patren = mDbConn->createTable(PATTERNTABLE, "public", CivDbConnection::PATTERN_TABLE);
    bool curve = mDbConn->createTable(CURVETABLE, "public", CivDbConnection::CURVE_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];
        if (PIPELINE == table)
            continue;

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

    // 设置模式
    Componets comps =  mDbConn->getComponets(PATTERNTABLE);
    size_t total = comps.size();
    for (int i = 0; i < total; i++)
    {

    }
    
    // 设置曲线
    Componets comps = 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 lineFilter =  filter.find(PIPELINE)->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;
            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;

        int total = comps.size();
        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);
        }
        inp.writeComponet(jounItems,_JUNCTIONS);
        inp.writeComponet(nodeCoordItems, _COORDS);
    }
    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);
     }
  
}

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

    NodeResultItems nodeItems;
    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);

        nodeItems.push_back(nodeItem);
    }
   mDbConn->updateNode(nodeItems);
}

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

    LinkResultItems linkItems;
    for (int i = 1; i <= nLinkCount; i++)
    {
        LinkResultItem linkItem;
        // 编号
        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, "开启");

        linkItems.push_back(linkItem);
    }
   mDbConn->updateLink(linkItems);
}
