#include "CivHydrSimulation.h"
#include "CivPgConn.h"
#include "CivNewInp.h"
#include "CivBuilder.h"
#include "CivInpDirector.h"
#include "CivSysLog.h"
#include "epanet2.h"
#include "../pandaDbManager/CivCommonUtils.h"
#include "CivTrackingResultCache.h"


CivHydrSimulation::CivHydrSimulation()
   :mDbConn(nullptr),
	mInpFile("test.inp"),
	mRptFile("test.rpt"),
	mBinFile("test.bin")
{

}

CivHydrSimulation::CivHydrSimulation(CivDbConn* dbConn)
	:mDbConn(dbConn),
	mInpFile("test.inp"),
	mRptFile("test.rpt"), 
	mBinFile("test.bin")
{
    if (dbConn)
        dbConn->checkHyDrNetTable();
}

CivHydrSimulation::~CivHydrSimulation()
{

}

char* CivHydrSimulation::getInpFile()
{ 
	return const_cast<char*>(mInpFile.c_str()); 
}

void CivHydrSimulation::setInpFile(const std::string& inpFile)
{ 
	mInpFile = inpFile; 
}

char* CivHydrSimulation::getRptFile()
{ 
	return const_cast<char*>(mRptFile.c_str()); 
}

void CivHydrSimulation::setRptFile(const std::string& rptFile)
{ 
	mRptFile = rptFile; 
}

char* CivHydrSimulation::getBinFile()
{
	return const_cast<char*>(mBinFile.c_str());
}

std::string CivHydrSimulation::getAnalysisResults()
{
    std::string result;
    std::ifstream myfile(getRptFile());

    if (myfile.is_open())
    {
        return result;
    }

    while (!myfile.eof())
    {
        myfile >> result;
    }

    myfile.close();

    return result;
}

void CivHydrSimulation::setBinFile(const std::string& binFile)
{ 
	mBinFile = binFile; 
}

void CivHydrSimulation::registDb(CivDbConn* dbConn)
{
	mDbConn = dbConn;
    if (mDbConn)
        mDbConn->checkHyDrNetTable();
}

bool CivHydrSimulation::convertInp(const QualityAnalyType analyType, const std::string& qulityName)
{
	CivBuilder* builder = new CivInpBuilder();
    builder->setQualityType(analyType, qulityName);
	builder->registDb(mDbConn);

	CivInpDirector director;
	director.setBuilder(builder);
	director.create();

	CivNewInp* newInp = builder->getNewInp();
	char* inpFileName = getInpFile();

	if (inpFileName 
		&& (*inpFileName !='\0') 
		&& !newInp->writeToFile(inpFileName))
	{
		delete builder;
		return false;
	}

	delete builder;
	return true;
}

bool CivHydrSimulation::hdyrCompute()
{
    short hydCode = 1;
    long  t(0), tstep(0);
    int   iTime(0);

    int  errcode = 0;

    char* fileName = getInpFile();
    char* rptFile = getRptFile();
    char* binOutFile = getBinFile();

    errcode = ENopen(fileName, rptFile, binOutFile);
    if (errcode > 0)
    {
        ENclose();
        CivSysLog::getInstance()->error("ENopen inp 失败", "CivHydrCalc", __FUNCTION__);
        return false;
    }
    CivSysLog::getInstance()->info("ENopen inp 成功", "CivHydrCalc", __FUNCTION__);
    if (ENopenH() > 0)
    {
        ENclose();
        CivSysLog::getInstance()->error("ENopenH inp 失败", "CivHydrCalc", __FUNCTION__);
        return false;
    }

    if (ENinitH(0) > 0)
    {
        ENclose();
        CivSysLog::getInstance()->error("ENinitH inp 失败", "CivHydrCalc", __FUNCTION__);
        return false;
    }
    CivSysLog::getInstance()->info("ENinitH inp 成功", "CivHydrCalc", __FUNCTION__);

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

    ENcloseH();
    ENclose();

    return true;
}

void CivHydrSimulation::getDataByTime(int time, NodeResultItems& nodeItems, LinkResultItems& linkItems)
{
    mResultCache.getResultByTime(time, nodeItems, linkItems);
}

bool CivHydrSimulation::hdyrInstantSimulation()
{
    CivBuilder* builder = new CivInpBuilder();
    builder->setDuration(0);
    builder->setQualityType(ANALYSIS_RESERVE, "");
    builder->registDb(mDbConn);

    CivInpDirector director;
    director.setBuilder(builder);
    director.create();

    CivNewInp* newInp = builder->getNewInp();
    char* inpFileName = getInpFile();

    if (inpFileName && (*inpFileName != '\0') && !newInp->writeToFile(inpFileName))
    {
        delete builder;
        return false;
    }

    delete builder;


    // 水力计算
    if (!hdyrCompute())
    {
        CivSysLog::getInstance()->error("水力分析结果存储失败", "CivHydrCalc", __FUNCTION__);
        return false;
    }

    return true;
}


bool CivHydrSimulation::hdyrSimulation()
{

    CivBuilder* builder = new CivInpBuilder();
    builder->setQualityType(ANALYSIS_RESERVE, "");
    builder->registDb(mDbConn);

    CivInpDirector director;
    director.setBuilder(builder);
    director.create();

    CivNewInp* newInp = builder->getNewInp();
    char* inpFileName = getInpFile();

    if (inpFileName && (*inpFileName != '\0') && !newInp->writeToFile(inpFileName))
    {
        delete builder;
        return false;
    }

    delete builder;


    // 水力计算
    if (!hdyrCompute())
    {
        CivSysLog::getInstance()->error("水力分析结果存储失败", "CivHydrCalc", __FUNCTION__);
        return false;
    }
        
    //模拟数据刷新
    if (!brushingResult())
    {
        CivSysLog::getInstance()->error("水力分析结果存储失败", "CivHydrCalc", __FUNCTION__);
        return false;
    }
      
    return true;
}

bool CivHydrSimulation::brushingResult()
{
    clearSamePeriodValue(mDbConn, CurrentDate);

    // 更新数据
    if (!mResultCache.updateToDb(mDbConn))
    {
        CivSysLog::getInstance()->error("水力分析结果存储失败", "CivHydrCalc", __FUNCTION__);
        return false;
    }

    // 更新管网数据库数据
    updateLastestDataToGisNet(mDbConn, "0");


    return true;
}

bool  CivHydrSimulation::clearSamePeriodValue(CivDbConn* dbConn, const std::string& date)
{
    if (dbConn == nullptr)
        return false;

    dbConn->deleteByField(NODERESULTTABLE, "日期", date);
    dbConn->deleteByField(PIPERESULTTABLE, "日期", date);

    return true;
}

bool CivHydrSimulation::updateLastestDataToGisNet(CivDbConn* dbConn, const std::string& time)
{
    if (dbConn == nullptr)
        return false;

    dbConn->updateNodeByInterval(CurrentDate, time);
    dbConn->updateLinkByInterval(CurrentDate, time);

    return true;
}

bool CivHydrSimulation::qualitySimulation()
{
    CivSysLog::getInstance()->error("开始水质分析计算", "qualitySimulation", __FUNCTION__);
    if (!convertInp(ANALYSIS_AGE, ""))
    {
        CivSysLog::getInstance()->error("写入inp文件失败", "qualitySimulation", __FUNCTION__);
        return false;
    }
    CivSysLog::getInstance()->info("写入inp成功", "qualitySimulation", __FUNCTION__);

    // 水质计算
    if (!qualityCompute())
    {
        CivSysLog::getInstance()->error("水质分析计算失败", "CivHydrCalc", __FUNCTION__);
        return false;
    }

    //模拟数据刷新
    if (!brushingResult())
    {
        CivSysLog::getInstance()->error("水质分析结果存储失败", "CivHydrCalc", __FUNCTION__);
        return false;
    }
    return true;
}

bool  CivHydrSimulation::trackingSimulation(char* snNode, int hour)
{
    CivSysLog::getInstance()->error("开始水质分析计算", "qualitySimulation", __FUNCTION__);
    setQualityAnalyType(ANALYSIS_TRACE);
    if (!convertInp(ANALYSIS_TRACE, snNode))
    {
        CivSysLog::getInstance()->error("写入inp文件失败", "qualitySimulation", __FUNCTION__);
        return false;
    }
    CivSysLog::getInstance()->info("写入inp成功", "qualitySimulation", __FUNCTION__);

    // 水质计算
    if (!qualityCompute())
    {
        CivSysLog::getInstance()->error("追踪分析计算失败", "CivHydrCalc", __FUNCTION__);
        return false;
    }
      
    // 扩散分析不存储数据
    //模拟数据刷新
    if (!brushingResult())
    {
        CivSysLog::getInstance()->error("追踪分析计算失败", "CivHydrCalc", __FUNCTION__);
        return false;
    }


    return true;
}

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

    int  errcode = 0;

    char* fileName = getInpFile();
    char* rptFile = getRptFile();
    char* binOutFile = getBinFile();

    errcode = ENopen(fileName, rptFile, binOutFile);
    if (errcode > 0)
    {
        ENclose();
        CivSysLog::getInstance()->error("ENopen inp 失败", "CivHydrCalc", __FUNCTION__);
        return false;
    }
    CivSysLog::getInstance()->info("ENopen inp 成功", "CivHydrCalc", __FUNCTION__);
    if (ENopenH() > 0)
    {
        ENclose();
        CivSysLog::getInstance()->error("ENopenH inp 失败", "CivHydrCalc", __FUNCTION__);
        return false;
    }

    if (ENinitH(EN_SAVE) > 0)
    {
        ENclose();
        CivSysLog::getInstance()->error("ENinitH inp 失败", "CivHydrCalc", __FUNCTION__);
        return false;
    }
    CivSysLog::getInstance()->info("ENinitH inp 成功", "CivHydrCalc", __FUNCTION__);

    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);
        }*/

        errcode = ENrunH(&t);	// errcode > 100 是错误
        if (errcode > 100)
        {
            ENcloseH();
            ENclose();
            return 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();

    CivSysLog::getInstance()->info("结束水质分析计算", "CivHydrCalc", __FUNCTION__);
    return true;
}

void  CivHydrSimulation::getTrackingResult(int hours, std::string& newresult)
{

    std::string result;
    result.append("[");

    // 缓存节点和管段空间信息
    CivTrackingResultCache trackingCache;
    trackingCache.cacheInfoFromDb(mDbConn);

    std::stringstream stream;
    for (int i = 0; i < hours; i++)
    {
        NodeResultItems nodeItemsMap;
        LinkResultItems linkItemsMap;
        mResultCache.getResultByTime(i, nodeItemsMap, linkItemsMap);

        // 查找节点追踪的节点编号
        std::string nodeTempText;
        nodeTempText.append("{\"type\":\"污染源\",\"location\":[");

        auto nodeIter = nodeItemsMap.begin();
        size_t nodeTotal = nodeItemsMap.size();
        while (nodeIter != nodeItemsMap.end())
        {

            if (nodeIter->second.dQuality > 0)
            {
                nodeTempText.append("{\"value\":[");
                std::string sn = nodeIter->first;

                Junctions junction;
                trackingCache.getNodeInfoBySn(sn, junction);
             
                nodeTempText.append(junction.xCoord);
                nodeTempText.append(",");
                nodeTempText.append(junction.yCoord);
                nodeTempText.append("],");

                nodeTempText.append("\"code\":");
                nodeTempText.append(sn.c_str());
                nodeTempText.append("},");
            }

            nodeIter++;
        }
        std::string newNodeTempText = nodeTempText.substr(0, nodeTempText.length() - 1);
        newNodeTempText.append("]}");

        // 追踪管段编号
        auto linkIter = linkItemsMap.begin();
        size_t total = linkItemsMap.size();
        int index = 1;

        std::string linkTempText;
        linkTempText.append("{\"type\":\"污染管段\",\"location\":[");

        while (linkIter != linkItemsMap.end())
        {
            if (linkIter->second.dQuality > 0)
            {
                std::string sn = linkIter->first;
                linkTempText.append("{\"coords\":[");


                Pipes pipe;
                trackingCache.getPipeInfoBySn(sn, pipe);

                linkTempText.append("[");
                linkTempText.append(pipe.startX);
                linkTempText.append(",");
                linkTempText.append(pipe.startY);
                linkTempText.append("],");

                linkTempText.append("[");
                linkTempText.append(pipe.endX);
                linkTempText.append(",");
                linkTempText.append(pipe.endY);
                linkTempText.append("]");

                linkTempText.append("]},");
            }
            linkIter++;
        }
        std::string newLinkTempText = linkTempText.substr(0, linkTempText.length() - 1);
        newLinkTempText.append("]}");
  
       result.append("[");
       result.append(newNodeTempText);
       result.append(",");
       result.append(newLinkTempText);
       result.append("],");
    }
    newresult = result.substr(0, result.length() -1 );
    newresult.append("]");
}

void CivHydrSimulation::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);

        nodeItem.dInterval = time;

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

void CivHydrSimulation::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 CivHydrSimulation::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);

        int trueQuality = *quality;
        if (mQuaLityType == ANALYSIS_TRACE)
            trueQuality *= 100;

        mResultCache.addNodeQuality(trueQuality, time, No);
        free(quality);
    }
}

void CivHydrSimulation::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);

        int trueQuality = *quality;
        if (mQuaLityType == ANALYSIS_TRACE)
            trueQuality *= 100;

        mResultCache.addLinkQuality(trueQuality, time, lnkNo);
    }
    free(typeCode);
    free(quality);
}
