#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"


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")
{

}

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

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

void CivHydrSimulation::registDb(CivDbConn* dbConn)
{
	mDbConn = dbConn;
}

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;
		delete newInp;
		return false;
	}

	delete builder;
	delete newInp;
	return true;
}

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

    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();
    //模拟数据刷新
    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__);
    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)
        {
            ENopenQ();
            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();

    // 清除当前之前的模拟的数据
    clearSamePeriodValue(mDbConn, CurrentDate);

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

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

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

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

    result.append("[");
    for (int i = 0; i < hours; i++)
    {
        NodeResultItems nodeItemsMap;
        LinkResultItems linkItemsMap;
        mResultCache.getResultByTime(i, nodeItemsMap, linkItemsMap);

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

        // 查找节点追踪的节点编号
        auto nodeIter = nodeItemsMap.begin();
        size_t nodeTotal = nodeItemsMap.size();
        while (nodeIter != nodeItemsMap.end())
        {
            if (nodeIter->second.dQuality < 0.8)
            {
                nodeTempText.append(nodeIter->first.c_str());
                nodeTempText.append(",");
            }
               
            nodeIter++;
        }
        nodeTempText.append("]");


        // 追踪管段编号
        auto linkIter = linkItemsMap.begin();
        size_t total = linkItemsMap.size();
        int index = 1;
        std::string linkTempText;
        linkTempText.append("[");
        while (linkIter != linkItemsMap.end())
        {
            if (linkIter->second.dQuality > 0)
            {
                linkTempText.append(linkIter->first.c_str());
                if (index != total)
                {
                    linkTempText.append(",");
                }    
            }
            linkIter++;
            index++;
        }
        linkTempText.append("]");
       /* result.append(tempText);
        if (i <(hours-1))
        {
            result.append("|");
        }*/
    }
    result.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);

        mResultCache.addNodeQuality(*quality, 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);
        mResultCache.addLinkQuality(*quality, time, lnkNo);
    }
    free(typeCode);
    free(quality);
}