#include "MyYaml.h"

namespace ai_matrix
{
    MyYaml *MyYaml::pInstance_ = nullptr;
    MyYaml::GarbageCollector MyYaml::gc_;
    std::mutex MyYaml::mx_;

    MyYaml *MyYaml::GetIns()
    {
        //双层锁，确保线程安全
        if (pInstance_ == nullptr)
        {
            std::lock_guard<std::mutex> guard(mx_); //防止异常发生不能解锁
            if (pInstance_ == nullptr)
            {
                pInstance_ = new MyYaml();
            }
        }
        return pInstance_;
    }

    int MyYaml::ReadYaml(std::string &strPath)
    {
        try
        {
            strConfigYamlPath_ = strPath;
            config_ = YAML::LoadFile(strPath);
            //退出程序
            if (config_.IsNull())
            {
                printf("config.yaml no find");
                return -1;
            }

            //数据源参数
            std::string strDataSource = GetStringValue("gc_data_source");
            if (config_[strDataSource.c_str()].IsDefined())
            {
                int iDataSourceId = 0;
                for (YAML::const_iterator it = config_[strDataSource.c_str()].begin(); it != config_[strDataSource.c_str()].end(); it++)
                {
                    YAML::Node configTemp = it->second;

                    DataSourceConfig dataSourceConfig;
                    dataSourceConfig.bUse = GetBoolValue("use", &configTemp);
                    dataSourceConfig.strUrl = GetStringValue("url", &configTemp);
                    dataSourceConfig.iSkipInterval = GetIntValue("skipInterval", &configTemp);
                    dataSourceConfig.strTarget = GetStringValue("target", &configTemp);
                    dataSourceConfig.iDirection = GetIntValue("direction", &configTemp);
                    dataSourceConfig.iLeftFirst = GetIntValue("left_first", &configTemp);
                    dataSourceConfig.iRightFirst = GetIntValue("right_first", &configTemp);
                    std::vector<std::string> vecAreas;
                    std::stringstream strStream(GetStringValue("identify_areas", &configTemp));
                    std::string strItem;
                    while (std::getline(strStream, strItem, ','))
                    {
                        vecAreas.push_back(strItem);
                    }
                    if (vecAreas.size() == 4)
                    {
                        dataSourceConfig.fIdentifyAreasLTX = atof(vecAreas[0].c_str());
                        dataSourceConfig.fIdentifyAreasLTY = atof(vecAreas[1].c_str());
                        dataSourceConfig.fIdentifyAreasRBX = atof(vecAreas[2].c_str());
                        dataSourceConfig.fIdentifyAreasRBY = atof(vecAreas[3].c_str());
                    }

                    dataSourceConfig.iRotate = 0;
                    if (configTemp["rotate"].IsDefined())
                    {
                        dataSourceConfig.iRotate = GetIntValue("rotate", &configTemp);
                    }

                    //框最小高度参数设置，过滤远股道识别到的信息
                    if (configTemp["classid_minheight"].IsDefined())
                    {
                        std::stringstream strStream(GetStringValue("classid_minheight", &configTemp));
                        std::string strItem;
                        while (std::getline(strStream, strItem, ','))
                        {
                            size_t iPos = strItem.find(":");
                            if (iPos != std::string::npos)
                            {
                                dataSourceConfig.mapClassMinH.insert(std::make_pair(atoi(strItem.substr(0, iPos).c_str()), atoi(strItem.substr(iPos + 1).c_str())));
                            }
                        }
                    }

                    mapDataSourceConfig_[iDataSourceId] = dataSourceConfig;
                    if (dataSourceConfig.bUse)
                    {
                        mapUseDataSourceConfig_[iDataSourceId] = dataSourceConfig;
                    }
                    iDataSourceId++;
                }
                printf("data source:[%s], allcount:[%d], usecount[%d]\n",
                       strDataSource.c_str(), mapDataSourceConfig_.size(), mapUseDataSourceConfig_.size());
            }
            //模型参数
            if (config_["model"].IsDefined())
            {
                for (YAML::const_iterator it = config_["model"].begin(); it != config_["model"].end(); it++)
                {
                    YAML::Node configTemp = it->second;
                    std::string strModelName = it->first.as<std::string>();
                    mapModelConfig_[strModelName].strOmPath = GetStringValue("om_path", &configTemp);
                    mapModelConfig_[strModelName].strModelInfoPath = GetStringValue("modelinfo_path", &configTemp);
                    mapModelConfig_[strModelName].strModelType = GetStringValue("model_type", &configTemp);
                    mapModelConfig_[strModelName].fScoreThreshold = GetFloatValue("score_threshold", &configTemp);
                    mapModelConfig_[strModelName].fNMSTreshold = GetFloatValue("nms_threshold", &configTemp);
                }
            }
        }
        catch (std::exception& strex) //捕获所有异常
        {
            printf(strex.what());
            return -1;
        }
        return 0;
    }

    int MyYaml::WriteYaml()
    {
        try
        {
            std::ofstream of(strConfigYamlPath_);
            of << config_;
            of.close();
        }
        catch (...) //捕获所有异常
        {
            printf("yaml文件不存在\n");
            return -1;
        }
        return 0;
    }

    std::string MyYaml::GetStringValue(const char *pszKey, const YAML::Node *pConfig/*=nullptr*/) const
    {
        if(nullptr == pConfig)
        {
            pConfig = &config_;
        }
        if (!(*pConfig)[pszKey].IsDefined())
        {
            printf("string key:[%s] not exist!\n", pszKey);
        }
        return (*pConfig)[pszKey].as<std::string>();
    }

    int MyYaml::GetIntValue(const char *pszKey, const YAML::Node *pConfig/*=nullptr*/) const
    {
        if (nullptr == pConfig)
        {
            pConfig = &config_;
        }
        if (!(*pConfig)[pszKey].IsDefined())
        {
            printf("int key:[%s] not exist!\n", pszKey);
        }
        return (*pConfig)[pszKey].as<int>();
    }

    bool MyYaml::GetBoolValue(const char *pszKey, const YAML::Node *pConfig/*=nullptr*/) const
    {
        if (nullptr == pConfig)
        {
            pConfig = &config_;
        }
        if (!(*pConfig)[pszKey].IsDefined())
        {
            printf("bool key:[%s] not exist!\n", pszKey);
        }
        return (*pConfig)[pszKey].as<bool>();
    }

    float MyYaml::GetFloatValue(const char *pszKey, const YAML::Node *pConfig/*=nullptr*/) const
    {
        if (nullptr == pConfig)
        {
            pConfig = &config_;
        }
        if (!(*pConfig)[pszKey].IsDefined())
        {
            printf("float key:[%s] not exist!\n", pszKey);
        }
        return (*pConfig)[pszKey].as<float>();
    }

    std::string MyYaml::GetPathValue(const char *pszKey, const YAML::Node *pConfig /*=nullptr*/) const
    {
        if (nullptr == pConfig)
        {
            pConfig = &config_;
        }
        if (!(*pConfig)[pszKey].IsDefined())
        {
            printf("path key:[%s] not exist!\n", pszKey);
        }
        std::string strTmp = (*pConfig)[pszKey].as<std::string>();
        if (strTmp.back() != '/')
        {
            strTmp += "/";
        }
        return strTmp;
    }

    ModelConfig MyYaml::GetModelConfig(const std::string &strModleName) const
    {
        if(mapModelConfig_.find(strModleName) == mapModelConfig_.end())
        {
            printf("model key:[%s] not exist!\n", strModleName.c_str());
        }
        return mapModelConfig_.at(strModleName);
    }

    size_t MyYaml::GetDataSourceCount() const
    {     
        return mapDataSourceConfig_.size();
    }

    DataSourceConfig MyYaml::GetDataSourceConfigById(int iEngineId) const
    {
        if (mapDataSourceConfig_.find(iEngineId) == mapDataSourceConfig_.end())
        {
            printf("GetDataSourceConfigById engineId:[%d] Err\n", iEngineId);
        }
        return mapDataSourceConfig_.at(iEngineId);
    }

    std::map<int, DataSourceConfig> MyYaml::GetUseDataSourceConfig() const
    {
        return mapUseDataSourceConfig_;
    }
}
