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

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) {
        mConn = NULL;
        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 utf8NetName =  TransUTFCoding(netName); // 中文乱码
   std::string sql = "select \"name\" from public.__layer_info__ where net_code in(select code from public.__layer_net_info__ where \"name\" ='" + utf8NetName + "' 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 = UTFTransCoding(item);
           tables.push_back(ss);
       }
   }

   PQclear(mResult);
   return tables;
}

bool CivDbConnection::deleteByDate(StrQuote table, StrQuote filedName, StrQuote fieldValue)
{
    if (!isValid())
        return false;

    char delSql[256] = "delete from \"";
    strcat_s(delSql, table.c_str());
    strcat_s(delSql, "\" where ");
    strcat_s(delSql, filedName.c_str());
    strcat_s(delSql, "='");
    strcat_s(delSql, fieldValue.c_str());
    strcat_s(delSql, "'");

    std::string utf8Sql = TransUTFCoding(delSql);
    if (!execSql(utf8Sql))
        return false;

    return true;
}

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

    std::stringstream os;
    NodeFieldName filedNames;

    os << "INSERT INTO public."<< NODERESULTTABLE<<" ( ";
    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 << filedNames.dDate << ",";
    os << filedNames.dInterval<<",";
    os << filedNames.dQuality<< ",";
    os << filedNames.dModifyTime;
    os<< ") VALUES"; 

    std::string preSql = os.str();
    os.flush();
    os.clear();
    os.str("");

    // 2, 参数值
    for(auto iter= nodeItems.begin();iter!=nodeItems.end();iter++)
    { 
        NodeResultItem item = iter->second;
        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 <<"'"<< CurrentDate <<"',";
        os << item.dInterval<<",";
        os << item.dQuality<< ",";
        os << "'"<< CurrentTime <<"'";

        auto subiter = iter;
        ++subiter;
        if (subiter == nodeItems.end())
        {
            os << ")";              
        }
        else
        {
            os << "),";
        }
    }
    std::string sql = TransUTFCoding(preSql) + os.str();
    os.clear();
    os.str("");
    if (!execSql(sql))
        return false;
     
    
    return true;
}

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

    std::stringstream os;
    LinkFiledName filedNames;
    std::string currData = CurrentDate;

    os<< "INSERT INTO public."<< PIPERESULTTABLE <<"( ";
    os<< filedNames.szNo<< ",";
    os<< filedNames.dFlow<< ",";
    os<< filedNames.nFlowDirect<< ",";
    os<< filedNames.dVelocity<< ",";
    os<< filedNames.dHeadloss<< ",";
    os<< filedNames.dUnitHeadloss<< ",";
    os<< filedNames.dFromNodHeadloss<< ",";
    os<< filedNames.dToNodHeadloss<< ",";
    os<< filedNames.dLocalHeadloss<< ",";
    os<< filedNames.dFrictionHeadloss<<",";
    os << filedNames.szStatus << ",";
    os << filedNames.dDate << ",";
    os << filedNames.dInterval<<",";
    os << filedNames.dQuality << ",";
    os << filedNames.dModifyTime;
    os<< ") VALUES";

    std::string preSql = os.str();
    os.clear();
    os.str("");

    // 2, 参数值
    for(auto iter = lineItems.begin();iter!=lineItems.end();iter++)
    {
        LinkResultItem item =iter->second;
        os<<"('" <<item.szNo<<"'"<< ",";
        os<< item.dFlow<< ",";
        os<< item.nFlowDirect<<",";
        os<< item.dVelocity<< ",";
        os<< item.dHeadloss<< ",";
        os<< item.dUnitHeadloss<< ",";
        os<< item.dFromNodHeadloss<< ",";
        os<< item.dToNodHeadloss<< ",";
        os<< item.dLocalHeadloss<< ",";
        os<< item.dFrictionHeadloss<< ",";
        os<< "'"<<TransUTFCoding(item.szStatus)<<"'"<<",";
        os <<"'"<< CurrentDate << "',";
        os<< item.dInterval<<",";
        os << item.dQuality << ",";
        os <<"'" << CurrentTime <<"'";

        auto subIte = iter;
        ++subIte;
        if (subIte == lineItems.end())
            os << ")";
        else
            os << "),";
    }
    std::string utf8Sql = TransUTFCoding(preSql);
    std::string sql = utf8Sql + os.str();

    os.clear();
    os.str("");

    if (!execSql(sql))
        return false;

    return true;
}

bool CivDbConnection::updateNodeByInterval(StrQuote currDate, StrQuote interval)
{
    if (!isValid())
        return false;

    std::stringstream os;
    NodeFieldName filedNames;

    os << "UPDATE  "
        << TransUTFCoding(PIPENODE) << "  AS  tb1 set ("
        << TransUTFCoding("总水头") << ","
        << TransUTFCoding("压力") << ","
        << TransUTFCoding("实际需水量")<<","
        <<TransUTFCoding("水质")
        << ")=( "
        << "tb2." << TransUTFCoding(filedNames.dHead) << ","
        << "tb2." << TransUTFCoding(filedNames.dPressure) << ","
        << "tb2." << TransUTFCoding(filedNames.dDemand) << ","
        << "tb2." << TransUTFCoding(filedNames.dQuality) << ")" << " from "
        << TransUTFCoding(NODERESULTTABLE) << " AS tb2 where tb1."
        << TransUTFCoding(filedNames.Number) << "= tb2." 
        << TransUTFCoding(filedNames.Number) << " AND tb2."
        << TransUTFCoding(filedNames.dDate) << "='" << currDate << "'  AND tb2." 
        << TransUTFCoding(filedNames.dInterval) << "=" << interval;

    std::string updateSql = os.str();
    std::cout << updateSql << std::endl;
    os.clear();
    os.str("");

    if (!execSql(updateSql))
        return false;

    return true;
}

bool CivDbConnection::updateLinkByInterval(StrQuote currDate, StrQuote interval)
{
    if (!isValid())
        return false;

    std::stringstream os;
    LinkFiledName filedNames;

    os << "UPDATE  " 
        << TransUTFCoding(PIPELINE) << "  AS  tb1 set (" 
        << TransUTFCoding("流量") << "," 
        << TransUTFCoding("流速") << ","
        << TransUTFCoding("单位水头损") <<","
        << TransUTFCoding("水头总损失")<< ","
        << TransUTFCoding("摩擦水头损")<<","
        << TransUTFCoding("上点水头")<<","
        << TransUTFCoding("本点水头")<<","
        << TransUTFCoding("水质") << ","
        << TransUTFCoding("局部水头损")
        <<")=( " 
        << "tb2." <<TransUTFCoding(filedNames.dFlow) << ","
        << "tb2." << TransUTFCoding(filedNames.dVelocity) << ","
        << "tb2." << TransUTFCoding(filedNames.dUnitHeadloss)<<","
        << "tb2." << TransUTFCoding(filedNames.dHeadloss)<<","
        << "tb2." << TransUTFCoding(filedNames.dFrictionHeadloss) << ","
        << "tb2." << TransUTFCoding(filedNames.dFromNodHeadloss) << ","
        << "tb2." << TransUTFCoding(filedNames.dToNodHeadloss) << ","
        << "tb2." << TransUTFCoding(filedNames.dQuality) << ","
        << "tb2." << TransUTFCoding(filedNames.dLocalHeadloss) <<")"<< "  from "
        << TransUTFCoding(PIPERESULTTABLE) << " AS tb2 where tb1."
        << TransUTFCoding(filedNames.szNo) << "= tb2."
        << TransUTFCoding(filedNames.szNo) << " AND tb2."
        << TransUTFCoding(filedNames.dDate) << "='" << currDate << "'  AND tb2."
         << TransUTFCoding(filedNames.dInterval) << "=" << interval;

    std::string updateSql = os.str();
    os.clear();
    os.str("");

    if (!execSql(updateSql))
        return false;

    return true;
}

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

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

    Componets nodes;

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

    return nodes;
}

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

    std::string utf8Name = TransUTFCoding(tableName);
    std::string utf8Where = TransUTFCoding(where);
    char sql[128] = "select * from public."; 
    strcat_s(sql, utf8Name.c_str());
    strcat_s(sql, utf8Where.c_str());

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

    Componets comps;

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

    return comps;
}

void CivDbConnection::getData(Componets& comp)
{
    if (mResult == NULL)
        return;

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

            std::string itemStr = UTFTransCoding(item);
            std::string fieldStr = UTFTransCoding(fieldName);

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

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

    
    char sql[256] = "select *  from public.\"";
    strcat_s(sql, TransUTFCoding(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(componets);
    return componets;
}

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

    char sql[256] = "select *  from public.\"";
    strcat_s(sql, TransUTFCoding(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(componets);

    return componets;
}

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

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

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

    Componets nodes;
    getData(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() )
        {
            Str ID = componet.find("id")->second;
            Str type = mValvesMap.find(tableName)->second;
            Str newbendian = type + ID;
            nodes[i]["编号"] = newbendian;
        }
        else {
            nodes[i]["编号"] = bendianhao;
        }
        
        Str code = componet.find("code")->second;

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

        if (lTotal == 2)
        {
            Componet lin = lines[0];

            nodes[i]["上点号"] = lin.find("本点号")->second;
            nodes[i]["本点号"] = lin.find("上点号")->second;
           
           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(CivTableTemp& temp)
{
    if (&temp == NULL)
        return false;

   Str sql = temp.createSql();
   Str finalSql = TransUTFCoding(sql);

   return execSql(finalSql);
}

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

    mResult = PQexec(mConn, sql.c_str());
    char* error = PQresultErrorMessage(mResult);
    std::string errorStr = error;

    if ( !errorStr.empty())
    {
        mLastError = error;
        std::cout << mLastError << std::endl;
        PQclear(mResult);
        return false;
       
    }
    
    return true;  
}

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