#include "CivDbConnection.h"
#include "CivCommonUtils.h"
#include "CivTableTemp.h"
#include<iostream>
#include <sstream>

#define MAX_BUFFER 16384

CivDbConnection::CivDbConnection(char* uri)
    : mConn(NULL), mUri(uri)
{
     
}

CivDbConnection::~CivDbConnection()
{
    close();
}

bool CivDbConnection::tableExist(StrQuote tableName)
{
    char sql[256] = "select count(*) from pg_class where relname = '";
    strcat_s(sql, tableName.c_str());
    strcat_s(sql, "'");

    if (!execSql(sql))
        return false;

    char* res = PQgetvalue(mResult, 0, 0);
    PQclear(mResult);
    int number = atoi(res);
   
    return number > 0 ? true : false;
}

bool CivDbConnection::open()
{
	mConn = PQconnectdb(mUri);
    if (PQstatus(mConn) == CONNECTION_BAD) {
        PQfinish(mConn);
        return false;
    }
    mIsOpen = true;
    return true;
}

bool CivDbConnection::isValid() const
{
    return mIsOpen;
}

void CivDbConnection::close()
{
    if(mConn != NULL)
        PQfinish(mConn);
    mIsOpen = false;
}

Tables CivDbConnection::getTables(StrQuote netName,StrQuote  schema)
{
    
    if (!isValid())
        return Tables();

   std::string sql = "select \"name\" from public.__layer_info__ where net_code in(select code from public.__layer_net_info__ where \"name\" ='" + netName + "' and \"schema\" = '"+ schema +"')";
   if (!execSql(sql))
       return Tables();

   Tables tables;
   int rows = PQntuples(mResult);
   int columns = PQnfields(mResult);
   for (int i = 0; i < rows; i++)
   {
       for (int j = 0; j < columns; j++)
       {
           char* item =PQgetvalue(mResult, i, j);
           std::string ss = CivCommonUtils::UTF8_To_string(item);
           tables.push_back(ss);
       }
   }

   PQclear(mResult);
   return tables;
}

bool CivDbConnection::updateNode(const NodeResultItems& nodeItems)
{
    if (!isValid())
        return false;

    if (createTable("__node_result__", "public", NODE_TABLE))
    {
        NodeFieldName filedNames;
       
        std::stringstream os;
        os << "INSERT INTO public.__node_result__ ( ";
        os <<filedNames.Number<<",";
        os<< filedNames.dDemand << ",";
        os<< filedNames.dHead<<",";
        os<< filedNames.dElevation<< ",";
        os<< filedNames.dPressure<< ",";
        os<< filedNames.dTankLevel<< ",";
        os<< filedNames.dTankMaxVolume<< ",";
        os<< filedNames.dTankVolume;
       /* os<< ",";
        os<< filedNames.lTime;*/
        os<< ") VALUES"; 

        // 2, 参数值
        size_t total = nodeItems.size();
        for (int i = 0; i < total; i++)
        { 
            NodeResultItem item = nodeItems[i];
            os << "('" << item.szNo << "',";
            os << item.dDemand << ",";
            os << item.dHead << ",";
            os << item.dElevation << ",";
            os << item.dPressure << ",";
            os << item.dTankLevel << ",";
            os << item.dTankMaxVolume << ",";
            os << item.dTankVolume;
         /*   os << ",";
            os << item.lTime;  */
            if (i == (total - 1))
            {
                os << ")";              
            }
            else
            {
                os << "),";
            }
        }
        std::string sql = os.str();
        os.clear();
        if (!execSql(sql))
            return false;
     }
    
    // 2, 参数值
    size_t total = nodeItems.size();
    for (int i = 0; i < total; i++)
    {
        NodeResultItem item = nodeItems[i];
        char sql[512] = "update public.";
        strcat_s(sql, "节点");
        strcat_s(sql, " as tb1 set(");
        strcat_s(sql, "总水头");
        strcat_s(sql, ",压力");
        strcat_s(sql, ",实际需水量");

        strcat_s(sql, ")");
        strcat_s(sql, "=(");

        // 拼接值
        strcat_s(sql, std::to_string(item.dHead).c_str());
        strcat_s(sql, ",");
        strcat_s(sql, std::to_string(item.dPressure).c_str());
        strcat_s(sql, ",");
        strcat_s(sql, std::to_string(item.dDemand).c_str());
        strcat_s(sql, ")");
        strcat_s(sql, "where  tb1.\"");
        strcat_s(sql, "本点号");
        strcat_s(sql, "\"='");
        strcat_s(sql, item.szNo);
        strcat_s(sql, "'");

        if (!execSql(sql))
            return false;
        }    
 

    return true;
}

bool CivDbConnection::updateLink(const LinkResultItems& lineItems)
{
    if (!isValid())
        return false;

    std::stringstream os;
    if (createTable("__pipe_result__", "public", PIPE_TABLE))
    {
        LinkFiledName filedNames;
        os<< "INSERT INTO public.__pipe_result__( ";
        os<< filedNames.szNo<< ",";
        os<< filedNames.dFlow<< ",";
      /*  os<< filedNames.nFlowDirect;
        os<< ",";*/
        os<< filedNames.dVelocity<< ",";
        os<< filedNames.dHeadloss<< ",";
        os<< filedNames.dUnitHeadloss<< ",";
        os<< filedNames.dFromNodHeadloss<< ",";
        os<< filedNames.dToNodHeadloss<< ",";
        os<< filedNames.dLocalHeadloss<< ",";
        os<< filedNames.dFrictionHeadloss;
        /*os<< ",";
        os<< filedNames.szStatus;*/
        /*os<< ",";
        os<< filedNames.lTime;*/
        os<< ") VALUES";

        // 2, 参数值
        size_t total = lineItems.size();
        for (int i = 0; i < total; i++)
        {
            LinkResultItem item = lineItems[i];
            os<<"('" <<item.szNo<<"'"<< ",";
            os<< item.dFlow<< ",";
           /* int direct = item.nFlowDirect;
            os<< direct;*/
          //  os<< ",";
            os<< item.dVelocity<< ",";
            os<< item.dHeadloss<< ",";
            os<< item.dUnitHeadloss<< ",";
            os<< item.dFromNodHeadloss<< ",";
            os<< item.dToNodHeadloss<< ",";
            os<< item.dLocalHeadloss<< ",";
            os<< item.dFrictionHeadloss;
            /*os<< ",";
            os<< "'"<<item.szStatus<<"'";*/
          /*os<< ",";
            os<< item.lTime;*/
            if (i == (total - 1))
                os << ")";
            else
                os << "),";
        }
        std::string sql = os.str();
        os.clear();
        if (!execSql(sql))
            return false;

    }
   
    {
        // 2, 参数值
        size_t total = lineItems.size();
        /*
            update
            表一
            set
            表一的字段 = 表2的字段
            from
            表一,
            表二
            where
            表一.Id = 表二.Id
          */
        std::string sql = "update public.管段 as tb1 set(";
        for (int i = 0; i < total; i++)
        {
            LinkResultItem item = lineItems[i];
            char sql[512] = "update public.管段 as tb1 set(";
            strcat_s(sql, "流量");
            strcat_s(sql, ",流速");
            strcat_s(sql, ",单位水头损");
            strcat_s(sql, ",水头总损失");
            strcat_s(sql, ",摩擦水头损");
            strcat_s(sql, ",上点水头");
            strcat_s(sql, ",本点水头");
            strcat_s(sql, ",局部水头损");
            strcat_s(sql, ")");
            strcat_s(sql, "=(");

            // 拼接值
            strcat_s(sql, std::to_string(item.dFlow).c_str());
            strcat_s(sql, ",");
            strcat_s(sql, std::to_string(item.dVelocity).c_str());
            strcat_s(sql, ",");
            strcat_s(sql, std::to_string(item.dUnitHeadloss).c_str());
            strcat_s(sql, ",");
            strcat_s(sql, std::to_string(item.dHeadloss).c_str());
            strcat_s(sql, ",");
            strcat_s(sql, std::to_string(item.dFrictionHeadloss).c_str());
            strcat_s(sql, ",");
            strcat_s(sql, std::to_string(item.dFromNodHeadloss).c_str());
            strcat_s(sql, ",");
            strcat_s(sql, std::to_string(item.dToNodHeadloss).c_str());
            strcat_s(sql, ",");
            strcat_s(sql, std::to_string(item.dLocalHeadloss).c_str());
            strcat_s(sql, ")where  tb1.\"");
            strcat_s(sql, "编号");
            strcat_s(sql, "\"='");
            strcat_s(sql, item.szNo);
            strcat_s(sql, "'");

            if (!execSql(sql))
                return false;
        }
    }
    return true;
}

Componets CivDbConnection::getComponets(StrQuote tableName)
{
    if (!isValid())
        return Nodes();

    std::string sql = "select *  from public.\"" + tableName + "\"";
    if (!execSql(sql))
    {
        return Nodes();
    }

    Componets nodes;

    // 取数据到容器中
    getData(mResult, nodes);

    return nodes;
}

Componets CivDbConnection::getComponetsByWhere(StrQuote tableName, StrQuote where)
{
    if (!isValid())
        return Componets();

    char sql[128] = "select * from table "; 
    strcat_s(sql, tableName.c_str());
    strcat_s(sql, where.c_str());

    if (!execSql(sql))
    {
        return Componets();
    }

    Componets comps;

    // 取数据到容器中
    getData(mResult, comps);

    return comps;
}

void CivDbConnection::getData(PGresult* result, Componets& comp)
{
    if (result == NULL)
        return;

    int rows = PQntuples(result);
    int columns = PQnfields(result);
    for (int i = 0; i < rows; i++)
    {
        Componet node;
        for (int j = 0; j < columns; j++)
        {
            char* item = PQgetvalue(result, i, j);
            char* fieldName = PQfname(result, j); // 获取列表字段名

            std::string itemStr = CivCommonUtils::UTF8_To_string(item);
            std::string fieldStr = CivCommonUtils::UTF8_To_string(fieldName);

            node.insert(std::pair<std::string, std::string>(fieldStr, itemStr));
        }
        comp.push_back(node);
    }
    PQclear(result);
}

Componets CivDbConnection::getNodeByCode(StrQuote table, StrQuote code)
{
    if (!isValid())
        return Nodes();

    char sql[256] = "select *  from public.\"";
    strcat_s(sql, table.c_str());
    strcat_s(sql, "\"");
    strcat_s(sql, " where code = '");
    strcat_s(sql, code.c_str());
    strcat_s(sql, "'");

    if (!execSql(sql))
    {
        return Nodes();
    }

    Componets componets;

    getData(mResult, componets);
    return componets;
}

Componets CivDbConnection::getLineByCode(StrQuote table, StrQuote code)
{
    if (!isValid())
        return Nodes();

    char sql[256] = "select *  from public.\"";
    strcat_s(sql, table.c_str());
    strcat_s(sql,"\"");
    strcat_s(sql, " where from_code = '");
    strcat_s(sql, code.c_str());
    strcat_s(sql, "' or to_code= '");
    strcat_s(sql, code.c_str());
    strcat_s(sql, "'");

    if (!execSql(sql))
    {
        return Nodes();
    }

    Componets componets;

    getData(mResult, componets);

    return componets;
}

Componets CivDbConnection::getLikelyPipes(StrQuote tableName)
{
    if (!isValid())
        return Componets();

    // 查询点数据
    char sql[256] = "select *  from \"";
    strcat_s(sql, tableName.c_str());
    strcat_s(sql,"\"");

    if (!execSql(sql))
    {
        return Componets();
    }

    Componets nodes;
    getData(mResult, nodes);


    // 查询线表
    if (nodes.size() <= 0)
        return Componets();

    // 查询:连接节点也有可能连接水源，或者水库
    std::vector<Str> tables;
    tables.push_back(PIPENODE);
    tables.push_back(RESIVOIR);
    tables.push_back(TANK);

    // 阀门类型映射
    std::map<Str, Str> mValvesMap;
    mValvesMap.insert(std::pair<Str, Str>(PRV_TYPE,"PRV"));
    mValvesMap.insert(std::pair<Str, Str>(PSV_TYPE, "PSV"));
    mValvesMap.insert(std::pair<Str, Str>(PBV_TYPE, "PBV"));
    mValvesMap.insert(std::pair<Str, Str>(FCV_TYPE, "FCV"));
    mValvesMap.insert(std::pair<Str, Str>(TCV_TYPE, "TCV"));
    mValvesMap.insert(std::pair<Str, Str>(GPV_TYPE, "PRV"));

    std::vector<Str> mVavesTables;
    mVavesTables.push_back(PRV_TYPE);
    mVavesTables.push_back(PSV_TYPE);
    mVavesTables.push_back(PBV_TYPE);
    mVavesTables.push_back(FCV_TYPE);
    mVavesTables.push_back(TCV_TYPE);
    mVavesTables.push_back(GPV_TYPE);

    // 不同阀门对应的特殊参数
    std::map<Str, Str> mParameterMap;
    mParameterMap.insert(std::pair<Str, Str>(PRV_TYPE, "恒定压力值"));
    mParameterMap.insert(std::pair<Str, Str>(PSV_TYPE, "恒定压力值"));
    mParameterMap.insert(std::pair<Str, Str>(PBV_TYPE, "恒定压力值"));
    mParameterMap.insert(std::pair<Str, Str>(FCV_TYPE, "流量设置"));
    mParameterMap.insert(std::pair<Str, Str>(TCV_TYPE, "流量设置"));
    mParameterMap.insert(std::pair<Str, Str>(GPV_TYPE, "水头损失曲"));


    // 根据点数据查询起点和终点，管径
    size_t total = nodes.size();
    for (int i = 0; i < total; i++)
    {
        Componet componet = nodes[i];
        Str bendianhao = componet.find("本点号")->second;
        if (bendianhao.empty() || bendianhao == "")
        {
            Str ID = componet.find("id")->second;
            Str type = mValvesMap.find(tableName)->second;
            Str newbendian = type + ID;
            std::string upSql = "Update  "+ tableName +" Set 本点号 ='"+ newbendian +"' Where id ='"+ID+"'";
            execSql(upSql);
            nodes[i]["编号"] = newbendian;
        }
        else {
            nodes[i]["编号"] = bendianhao;
        }
        
        Str code = componet.find("code")->second;

        Componets lines = getLineByCode(PIPELINE, code);
        size_t lTotal = lines.size();

        for (int j = 0; j < lTotal; j++)
        {
            Componet lin = lines[j];
   
            Str fromCode = lin.find("from_code")->second;
            Str toCode = lin.find("to_code")->second;

            Componets codeNodes;
            if (fromCode == code)
            {
               
                size_t size = tables.size();
                for (int i = 0; i < size; i++)
                {
                    codeNodes = getNodeByCode(tables[i], toCode);
                    if (codeNodes.size() > 0)
                        break;
                }
                
                if (codeNodes.size() > 0)
                {
                    Componet comp = codeNodes[0];
                    Str pointNo = comp.find("本点号")->second;
                    nodes[i]["本点号"] = pointNo;
                }
            }
            else if (toCode == code)
            {
                size_t size = tables.size();
                Str currTable;
                for (int i = 0; i < size; i++)
                {
                    currTable = tables[i];
                    codeNodes = getNodeByCode(tables[i], fromCode);
                    if (codeNodes.size() > 0)
                        break;
                }
                // 查询
               // codeNodes = getNodeByCode(PIPENODE, fromCode);
              
                if (codeNodes.size() > 0)
                {
                    Componet comp = codeNodes[0];
                    Str pointNo = comp.find("本点号")->second;
                    
                    if (currTable == RESIVOIR || currTable == TANK)
                    {
                        // 水源必须为起点：点转换为管线过程需要纠正方向
                        Str lastPoint = nodes[i]["本点号"];
                        nodes[i]["本点号"] = pointNo;

                        if (nodes[i]["上点号"].empty())
                            nodes[i]["上点号"] = lastPoint;
                    }
                    else
                    {
                        nodes[i]["上点号"] = pointNo;
                    }           
                }
            }

           Str diameter =  lin.find("管径")->second;
           nodes[i]["管径"] = diameter;

           Str number = lin.find("id")->second;
           addFilter(PIPELINE, number);

           // 阀门类型设置特殊字段
           std::vector<Str>::iterator iter = std::find(mVavesTables.begin(), mVavesTables.end(), tableName);
           if (iter!=mVavesTables.end())
           {
               nodes[i]["类型"] = mValvesMap.find(tableName)->second;
               Str fieldType = mParameterMap.find(tableName)->second;
               nodes[i]["参数设置"] = componet.find(fieldType)->second;
           }
        }
       
    }
    return nodes;
}

void CivDbConnection::addFilter(Str type, Str number) 
{ 
    mFilter[type].insert(number); 
}

FiledFilter CivDbConnection::getFilter() const
{ 
    return mFilter;
}

bool CivDbConnection::createTable(StrQuote tableName, StrQuote schema,TableType tableType)
{
    if (tableExist(tableName))
        return true;

    CivTableTemp tableTemp(tableName);
    
    switch (tableType)
    {
    case NODE_TABLE:
    {
        NodeFieldName filedName;
        tableTemp.addProperty(filedName.Number, CivTableTemp::FILED_VARCHAR);   
        tableTemp.addProperty(filedName.dPressure, CivTableTemp::FILED_DOUBLE);
        tableTemp.addProperty(filedName.dHead, CivTableTemp::FILED_DOUBLE);
        tableTemp.addProperty(filedName.dElevation, CivTableTemp::FILED_DOUBLE);
        tableTemp.addProperty(filedName.dDemand, CivTableTemp::FILED_DOUBLE);
        tableTemp.addProperty(filedName.dTankVolume, CivTableTemp::FILED_DOUBLE);
        tableTemp.addProperty(filedName.dTankMaxVolume, CivTableTemp::FILED_DOUBLE);
        tableTemp.addProperty(filedName.dTankLevel, CivTableTemp::FILED_DOUBLE);
        tableTemp.addProperty("类型", CivTableTemp::FILED_INT);
        tableTemp.addProperty("水质", CivTableTemp::FILED_DOUBLE);
        tableTemp.addProperty("时间", CivTableTemp::FILED_DATETIME);
    } 
        break;
    case PIPE_TABLE: 
    {
        LinkFiledName pipeFileds;
        tableTemp.addProperty(pipeFileds.szNo, CivTableTemp::FILED_VARCHAR);
        tableTemp.addProperty(pipeFileds.dFlow, CivTableTemp::FILED_DOUBLE);
        tableTemp.addProperty(pipeFileds.nFlowDirect, CivTableTemp::FILED_INT);
        tableTemp.addProperty(pipeFileds.dVelocity, CivTableTemp::FILED_DOUBLE);
        tableTemp.addProperty(pipeFileds.dHeadloss, CivTableTemp::FILED_DOUBLE);
        tableTemp.addProperty(pipeFileds.dUnitHeadloss, CivTableTemp::FILED_DOUBLE);
        tableTemp.addProperty(pipeFileds.dFromNodHeadloss, CivTableTemp::FILED_DOUBLE);
        tableTemp.addProperty(pipeFileds.dToNodHeadloss, CivTableTemp::FILED_DOUBLE);
        tableTemp.addProperty(pipeFileds.dLocalHeadloss, CivTableTemp::FILED_DOUBLE);
        tableTemp.addProperty(pipeFileds.dFrictionHeadloss, CivTableTemp::FILED_DOUBLE);
        tableTemp.addProperty(pipeFileds.szStatus, CivTableTemp::FILED_VARCHAR);
        tableTemp.addProperty("水质", CivTableTemp::FILED_DOUBLE);
        tableTemp.addProperty("时间", CivTableTemp::FILED_DATETIME);
    }   
        break;
    case PATTERN_TABLE:
    {
        PatternFields fileds;
        tableTemp.addProperty(fileds.Name, CivTableTemp::FILED_VARCHAR);
        tableTemp.addProperty(fileds.Value, CivTableTemp::FILED_JSON);
        tableTemp.addProperty(fileds.NetName, CivTableTemp::FILED_VARCHAR);
    }
        break;
    case CURVE_TABLE:
    {
        CurvesFields fileds;
        tableTemp.addProperty(fileds.Label, CivTableTemp::FILED_VARCHAR);
        tableTemp.addProperty(fileds.Type, CivTableTemp::FILED_VARCHAR);
        tableTemp.addProperty(fileds.XCoord, CivTableTemp::FILED_DOUBLE);
        tableTemp.addProperty(fileds.YCoord, CivTableTemp::FILED_DOUBLE);
    }
        break;
    default:
        break;
    }
   std::string sql = tableTemp.createSql();

   return execSql(sql);
}

bool CivDbConnection::execSql(std::string sql)
{
    if (sql == "" || sql.empty())
        return false;

    std::string sqlStr = CivCommonUtils::string_To_UTF8(sql);
    const char* finalSql = sqlStr.c_str();
    mResult = PQexec(mConn, finalSql);

    if (PQstatus(mConn) == PGRES_BAD_RESPONSE)
    {
        mLastError = PQresultErrorMessage(mResult);
        PQclear(mResult);
        return false;
       
    }
    return true;  
}

void CivDbConnection::clearResult()
{
    PQclear(mResult);
}