
#include "AdasMapParser.h"

#include <assert.h>

#include "CoordSys.h"
#include "FileSys.h"
#include "LogicEvaluator.h"
#include "Utils.h"
#include "ajson.hpp"
#include "shapefil.h"

const char *c_cAdasIndFacility_dbf = "IndFacility.dbf";
const char *c_cAdasIndFacility_shp = "IndFacility.shp";
const char *c_cAdasJunction_dbf = "Junction.dbf";
const char *c_cAdasJunction_shp = "Junction.shp";
const char *c_cAdasRADAS_csv = "RADAS.csv";
const char *c_cAdasRoad_dbf = "Road.dbf";
const char *c_cAdasRoad_shp = "Road.shp";
const char *c_cAdasSpeed_dbf = "Speed.dbf";

using namespace jf;
using namespace adas;

void AdasMapParser::ParseAll(const std::string &strPath, MapIdNode &mapIdNode, MapIdWay &mapIdWay, ShpBound &stBound) {
    // jf_log_info("[AdasMapParser::parse] file : {}", strPath);
    map<JfDataId, Road> mapIdRoad = {};
    map<JfDataId, Speed> mapIdSpeed = {};
    map<JfDataId, IndFacility> mapIdIndFacility = {};
    map<JfDataId, Junction> mapIdJunction = {};

    ParseJunction(strPath, mapIdJunction);
    ParseRoad(strPath, mapIdJunction, mapIdRoad, stBound);
    ParseRADAS(strPath, mapIdRoad);

    ParseSpeed(strPath, mapIdSpeed);
    ParseIndFacility(strPath, mapIdSpeed, mapIdRoad);

    MakeSdData(mapIdRoad, mapIdNode, mapIdWay);
}

void AdasMapParser::ParseJunction(const string &strPath, map<JfDataId, Junction> &mapIdJunction) {
    string strJunction_dbf = strPath + c_cAdasJunction_dbf;
    string strJunction_shp = strPath + c_cAdasJunction_shp;

    DBFHandle dbhJunction = DBFOpen(strJunction_dbf.c_str(), "rb");
    SHPHandle shhJunction = SHPOpen(strJunction_shp.c_str(), "rb");
    if ((nullptr == shhJunction) || (nullptr == dbhJunction)) {
        // jf_log_error("AdasMapParser::ParseJunction(), invalid map file path, could not open.");
        return;
    }

    int nEntities = 0;
    int nShapeType = 0;
    double dMinBound[4] = {};
    double dMaxBound[4] = {};
    SHPGetInfo(shhJunction, &nEntities, &nShapeType, dMinBound, dMaxBound);
    int nRcdCnt = DBFGetRecordCount(dbhJunction);
    if (nEntities != nRcdCnt) {
        // jf_log_error("AdasMapParser::ParseJunction(), invalid map file content, could not parse.");
        return;
    }

    static const int sc_nFieldID_JunctionId = 0;

    for (int i = 0; i < nRcdCnt; ++i) {
        // read a shp data object
        SHPObject *pshpJunctionObj = SHPReadObject(shhJunction, i);

        // read a dbf data object
        Junction stJunction = {};
        stJunction.lId = Utils::StringToNum<JfDataId>(DBFReadStringAttribute(dbhJunction, i, sc_nFieldID_JunctionId));

        // nParts is 0, so ignore it
        // for (int j = 0; j < pshpJunctionObj->nParts; ++j)
        {
            for (int k = 0; k < pshpJunctionObj->nVertices; ++k) {
                double dLon = pshpJunctionObj->padfX[k];
                double dLat = pshpJunctionObj->padfY[k];
                double dAlt = pshpJunctionObj->padfZ[k];
                // double dM = pshpJunctionObj->padfM[k];
                stJunction.vctGeoLink.push_back(vector<double>{dLon, dLat, dAlt});
            }
        }

        mapIdJunction[stJunction.lId] = stJunction;

        SHPDestroyObject(pshpJunctionObj);
    }

    DBFClose(dbhJunction);
    SHPClose(shhJunction);
}

void AdasMapParser::ParseRoad(const string &strPath, const map<JfDataId, Junction> &mapIdJunction, map<JfDataId, Road> &mapIdRoad, ShpBound &stBound) {
    string strRoad_dbf = strPath + c_cAdasRoad_dbf;
    string strRoad_shp = strPath + c_cAdasRoad_shp;

    DBFHandle dbhRoad = DBFOpen(strRoad_dbf.c_str(), "rb");
    SHPHandle shhRoad = SHPOpen(strRoad_shp.c_str(), "rb");
    if ((nullptr == shhRoad) || (nullptr == dbhRoad)) {
        // jf_log_error("AdasMapParser::ParseRoad(), invalid map file path, could not open.");
        return;
    }

    int nEntities = 0;
    int nShapeType = 0;
    double dMinBound[4] = {};
    double dMaxBound[4] = {};
    SHPGetInfo(shhRoad, &nEntities, &nShapeType, dMinBound, dMaxBound);
    int nRcdCnt = DBFGetRecordCount(dbhRoad);
    if (nEntities != nRcdCnt) {
        // jf_log_error("AdasMapParser::ParseRoad(), invalid map file content, could not parse.");
        return;
    }

    static const int sc_nFieldID_RoadId = 0;
    static const int sc_nFieldID_SJcId = 1;
    static const int sc_nFieldID_EJcId = 2;
    static const int sc_nFieldID_Length = 3;
    static const int sc_nFieldID_LaneNum = 4;
    static const int sc_nFieldID_Direction = 5;
    static const int sc_nFieldID_RoadRank = 6;
    static const int sc_nFieldID_RoadType = 7;
    static const int sc_nFieldID_Form = 8;
    static const int sc_nFieldID_StartAngle = 9;
    static const int sc_nFieldID_Speed = 10;
    static const int sc_nFieldID_Urban = 11;

    LogicEvaluator logic_evaluator(MapConfig::map_road_filer_logic);

    for (int i = 0; i < nRcdCnt; ++i) {
        // read a shp data object
        SHPObject *pshpRoadObj = SHPReadObject(shhRoad, i);

        // read a dbf data object
        Road stRoad = {};
        JfDataId lRoadId = Utils::StringToNum<JfDataId>(DBFReadStringAttribute(dbhRoad, i, sc_nFieldID_RoadId));
        JfDataId lSJcId = Utils::StringToNum<JfDataId>(DBFReadStringAttribute(dbhRoad, i, sc_nFieldID_SJcId));
        Utils::MapValue(mapIdJunction, lSJcId, stRoad.jctStart);
        JfDataId lEJcId = Utils::StringToNum<JfDataId>(DBFReadStringAttribute(dbhRoad, i, sc_nFieldID_EJcId));
        Utils::MapValue(mapIdJunction, lEJcId, stRoad.jctEnd);
        stRoad.dLength = DBFReadDoubleAttribute(dbhRoad, i, sc_nFieldID_Length);
        stRoad.nLaneNum = DBFReadIntegerAttribute(dbhRoad, i, sc_nFieldID_LaneNum);
        stRoad.nDirection = DBFReadIntegerAttribute(dbhRoad, i, sc_nFieldID_Direction);
        stRoad.nRank = DBFReadIntegerAttribute(dbhRoad, i, sc_nFieldID_RoadRank);
        stRoad.nType = DBFReadIntegerAttribute(dbhRoad, i, sc_nFieldID_RoadType);
        stRoad.nForm = DBFReadIntegerAttribute(dbhRoad, i, sc_nFieldID_Form);
        stRoad.dStartAngle = DBFReadDoubleAttribute(dbhRoad, i, sc_nFieldID_StartAngle);
        stRoad.nSpeed = DBFReadIntegerAttribute(dbhRoad, i, sc_nFieldID_Speed);
        stRoad.nUrban = DBFReadIntegerAttribute(dbhRoad, i, sc_nFieldID_Urban);

        // nParts must be 1
        for (int j = 0; j < pshpRoadObj->nParts; ++j) {
            for (int k = 0; k < pshpRoadObj->nVertices; ++k) {
                double dLon = pshpRoadObj->padfX[k];
                double dLat = pshpRoadObj->padfY[k];
                double dAlt = pshpRoadObj->padfZ[k];
                // double dM = pshpRoadObj->padfM[k];
                stRoad.vctGeoLink.push_back(vector<double>{dLon, dLat, dAlt});
            }
        }

        stRoad.dLength = DBFReadDoubleAttribute(dbhRoad, i, sc_nFieldID_Length);
        stRoad.nLaneNum = DBFReadIntegerAttribute(dbhRoad, i, sc_nFieldID_LaneNum);
        stRoad.nDirection = DBFReadIntegerAttribute(dbhRoad, i, sc_nFieldID_Direction);
        stRoad.nRank = DBFReadIntegerAttribute(dbhRoad, i, sc_nFieldID_RoadRank);
        stRoad.nType = DBFReadIntegerAttribute(dbhRoad, i, sc_nFieldID_RoadType);
        stRoad.nForm = DBFReadIntegerAttribute(dbhRoad, i, sc_nFieldID_Form);
        stRoad.dStartAngle = DBFReadDoubleAttribute(dbhRoad, i, sc_nFieldID_StartAngle);
        stRoad.nSpeed = DBFReadIntegerAttribute(dbhRoad, i, sc_nFieldID_Speed);
        stRoad.nUrban = DBFReadIntegerAttribute(dbhRoad, i, sc_nFieldID_Urban);

        // 道路逻辑过滤
        std::map<std::string, std::string> logic_map{{"rank", jf::to_string(stRoad.nRank)},  {"length", jf::to_string(stRoad.dLength)}, {"lane_num", jf::to_string(stRoad.nLaneNum)},       {"direction", jf::to_string(stRoad.nDirection)},
                                                     {"type", jf::to_string(stRoad.nType)},  {"form", jf::to_string(stRoad.nForm)},     {"dStartAngle", jf::to_string(stRoad.dStartAngle)}, {"speed", jf::to_string(stRoad.nSpeed)},
                                                     {"urban", jf::to_string(stRoad.nUrban)}};
        if (logic_evaluator.IsValid(logic_map)) {
            mapIdRoad[lRoadId] = stRoad;
        }

        SHPDestroyObject(pshpRoadObj);
    }

    DBFClose(dbhRoad);
    SHPClose(shhRoad);
}

void AdasMapParser::ParseRADAS(const string &strPath, map<JfDataId, Road> &mapIdRoad) {
    string strRADAS_csv = strPath + c_cAdasRADAS_csv;

    ifstream ifsRADAS(strRADAS_csv, ios::in);
    if (!ifsRADAS) {
        // jf_log_error("sdGlParser::ParseRADAS(), invalid map file path, could not open.");
        return;
    }

    int i = 0;
    string line = {};
    string field = {};

    getline(ifsRADAS, line);  // 将整行数据读取到sin流中，第一行是是说明文，不用

    while (getline(ifsRADAS, line))  // 按行读取csv文件中的数据
    {
        line = line.substr(0, line.length() - 1);  // 去掉最后的换行符

        RADAS stRadas = {};

        istringstream sin(line);  // 将整行数据读取到sin流中

        getline(sin, field, ',');  // 将字符串流sin中的的字符读取到field中，以逗号分割，第一列为id
        JfDataId lRoadID = Utils::StringToNum<JfDataId>(field);

        getline(sin, field, ',');  // 将字符串流sin中的的字符读取到field中，以逗号分割，第三列为Latitude
        stRadas.dLatitude = Utils::StringToNum<double>(field);

        getline(sin, field, ',');  // 将字符串流sin中的的字符读取到field中，以逗号分割，第四列为Longitude
        stRadas.dLongitude = Utils::StringToNum<double>(field);

        getline(sin, field, ',');  // 将字符串流sin中的的字符读取到field中，以逗号分割，第五列为outCurvature
        stRadas.dOutCurvature = Utils::StringToNum<double>(field);

        getline(sin, field, ',');  // 将字符串流sin中的的字符读取到field中，以逗号分割，第六列为outRoll
        stRadas.dOutRoll = Utils::StringToNum<double>(field);

        getline(sin, field, ',');  // 将字符串流sin中的的字符读取到field中，以逗号分割，第七列为outPitch
        stRadas.dOutPitch = Utils::StringToNum<double>(field);

        getline(sin, field, ',');  // 将字符串流sin中的的字符读取到field中，以逗号分割，第八列为outAzminuth
        stRadas.dOutAzimuth = Utils::StringToNum<double>(field);

        mapIdRoad[lRoadID].vctRADASes.push_back(stRadas);
    }

    ifsRADAS.close();
}

void AdasMapParser::ParseSpeed(const string &strPath, map<JfDataId, Speed> &mapIdSpeed) {
    string strSpeed_dbf = strPath + c_cAdasSpeed_dbf;

    DBFHandle dbhSpeed = DBFOpen(strSpeed_dbf.c_str(), "rb");
    if (nullptr == dbhSpeed) {
        // jf_log_error("AdasMapParser::ParseSpeed(), invalid map file path, could not open.");
        return;
    }

    int nRcdCnt = DBFGetRecordCount(dbhSpeed);
    if (0 == nRcdCnt) {
        // jf_log_error("AdasMapParser::ParseSpeed(), invalid map file content, could not parse.");
        return;
    }

    static const int sc_nFieldID_IndId = 0;
    static const int sc_nFieldID_Direction = 1;
    static const int sc_nFieldID_SPM = 2;
    static const int sc_nFieldID_SPType = 3;
    static const int sc_nFieldID_EffLane = 4;
    static const int sc_nFieldID_VeclLimit = 5;
    static const int sc_nFieldID_EffType = 6;

    for (int i = 0; i < nRcdCnt; ++i) {
        // read a dbf data object
        Speed stSpeed = {};
        stSpeed.lId = Utils::StringToNum<JfDataId>(DBFReadStringAttribute(dbhSpeed, i, sc_nFieldID_IndId));
        stSpeed.nDirection = DBFReadIntegerAttribute(dbhSpeed, i, sc_nFieldID_Direction);
        stSpeed.nSPM = DBFReadIntegerAttribute(dbhSpeed, i, sc_nFieldID_SPM);
        stSpeed.nSPType = DBFReadIntegerAttribute(dbhSpeed, i, sc_nFieldID_SPType);
        stSpeed.nEffLane = DBFReadIntegerAttribute(dbhSpeed, i, sc_nFieldID_EffLane);
        string strVeclLimit = DBFReadStringAttribute(dbhSpeed, i, sc_nFieldID_VeclLimit);
        AnalyseStrNums<int>(strVeclLimit, stSpeed.vctnVeclLimit);
        stSpeed.nEffType = DBFReadIntegerAttribute(dbhSpeed, i, sc_nFieldID_EffType);

        mapIdSpeed[stSpeed.lId] = stSpeed;
    }

    DBFClose(dbhSpeed);
}

void AdasMapParser::ParseIndFacility(const string &strPath, const map<JfDataId, Speed> &mapIdSpeed, map<JfDataId, Road> &mapIdRoad) {
    string strIndFacility_dbf = strPath + c_cAdasIndFacility_dbf;
    string strIndFacility_shp = strPath + c_cAdasIndFacility_shp;

    DBFHandle dbhIndFacility = DBFOpen(strIndFacility_dbf.c_str(), "rb");
    SHPHandle shhIndFacility = SHPOpen(strIndFacility_shp.c_str(), "rb");
    if ((nullptr == shhIndFacility) || (nullptr == dbhIndFacility)) {
        // jf_log_error("AdasMapParser::ParseIndFacility(), invalid map file path, could not open.");
        return;
    }

    int nEntities = 0;
    int nShapeType = 0;
    double dMinBound[4] = {};
    double dMaxBound[4] = {};
    SHPGetInfo(shhIndFacility, &nEntities, &nShapeType, dMinBound, dMaxBound);
    int nRcdCnt = DBFGetRecordCount(dbhIndFacility);
    if (0 == nRcdCnt) {
        // jf_log_error("AdasMapParser::ParseIndFacility(), invalid map file content, could not parse.");
        return;
    }

    static const int sc_nFieldID_IndId = 0;
    static const int sc_nFieldID_Type1 = 1;
    static const int sc_nFieldID_Type2 = 2;
    static const int sc_nFieldID_Value = 3;
    static const int sc_nFieldID_SpeedId = 4;
    static const int sc_nFieldID_RoadId = 5;

    for (int i = 0; i < nRcdCnt; ++i) {
        // read a shp data object
        SHPObject *pshpIndFacility = SHPReadObject(shhIndFacility, i);

        // read a dbf data object
        IndFacility stIndFacility = {};
        stIndFacility.lId = Utils::StringToNum<JfDataId>(DBFReadStringAttribute(dbhIndFacility, i, sc_nFieldID_IndId));
        stIndFacility.nType1 = DBFReadIntegerAttribute(dbhIndFacility, i, sc_nFieldID_Type1);
        stIndFacility.nType1 = DBFReadDoubleAttribute(dbhIndFacility, i, sc_nFieldID_Type2);
        stIndFacility.dValue = DBFReadDoubleAttribute(dbhIndFacility, i, sc_nFieldID_Value);
        string strSpeedId = DBFReadStringAttribute(dbhIndFacility, i, sc_nFieldID_SpeedId);
        if (strSpeedId.size() != 0) {
            vector<JfDataId> vctIds;
            AnalyseStrNums<JfDataId>(strSpeedId, vctIds);
            for (JfDataId lId : vctIds) {
                if (Utils::Contains(mapIdSpeed, lId)) {
                    stIndFacility.vctSpeed.push_back(mapIdSpeed.at(lId));
                }
            }
        }
        JfDataId lRoadId = Utils::StringToNum<JfDataId>(DBFReadStringAttribute(dbhIndFacility, i, sc_nFieldID_RoadId));

        // nParts must be 1
        for (int j = 0; j < pshpIndFacility->nParts; ++j) {
            for (int k = 0; k < pshpIndFacility->nVertices; ++k) {
                double dLon = pshpIndFacility->padfX[k];
                double dLat = pshpIndFacility->padfY[k];
                double dAlt = pshpIndFacility->padfZ[k];
                stIndFacility.vctGeoLink.push_back(vector<double>{dLon, dLat, dAlt});
            }
        }

        mapIdRoad[lRoadId].vctIndFacilitys.push_back(stIndFacility);
    }

    DBFClose(dbhIndFacility);
}

void AdasMapParser::MakeSdData(const map<JfDataId, Road> &mapIdRoad, MapIdNode &mapIdNode, MapIdWay &mapIdWay) {
    for (auto iter = mapIdRoad.begin(); iter != mapIdRoad.end(); ++iter) {
        BaseWay stWay = {};
        stWay.id = iter->first;
        stWay.level = iter->second.nRank;
        stWay.direction = iter->second.nDirection;

        int i = 0;
        for (std::vector<double> vctPoint : iter->second.vctGeoLink) {
            SdNode stNode = {};
            stNode.id_p = SdNodeId{Point(vctPoint)};
            // stNode.p = vctPoint;
            stNode.speed_limit_big = iter->second.nSpeed;
            stNode.speed_limit_small = iter->second.nSpeed;
            stNode.level = stWay.level;
            // stNode.direction = stWay.direction;
            const Road &r = mapIdRoad.at(int64_t(stWay.id));
            if (r.vctRADASes.size() > 0) {
                const RADAS &radas = r.vctRADASes[i++];
                stNode.radas = {radas.dOutCurvature, radas.dOutRoll, radas.dOutPitch, radas.dOutAzimuth};
            }

            stWay.nodes.push_back(stNode.id_p.p);

            mapIdNode[stNode.id_p] = stNode;
        }

        mapIdWay[stWay.id] = stWay;
    }
}

void AdasMapParser::GetTids(const std::vector<std::vector<double>> &vctvdGeoLink, std::vector<TileId> &vctOutTids) {
    std::set<TileId> setTids = {};
    for (const std::vector<double> &vctdGeoLink : vctvdGeoLink) {
        TileId tid = P2TId(Point(vctdGeoLink));
        setTids.insert(tid);
    }

    vctOutTids = Utils::Set2Vector(setTids);

    return;
}

int AdasMapParser::Factorial(int n) {
    // 计算阶乘
    int nResult = 1;
    for (size_t i = 2; i <= n; i++) {
        nResult *= i;
    }

    return nResult;
}