#define NOMINMAX
#include "./date/date.h"
#include "HIKNvr.h"
#include <ctime>
#include <iostream>
#include <chrono>

HIKNvr::HIKNvr(){

}

/**
 * @brief Initializes the NVR with the given login information and channel.
 * 
 * @param ip The IP address of the NVR.
 * @param port The port number of the NVR.
 * @param username The username for logging into the NVR.
 * @param password The password for logging into the NVR.
 * @param channel The channel number to set for the NVR.
 */
bool HIKNvr::InitNvr(std::string ip, std::string port, std::string username, std::string password, int channel)
{
    if (Login(ip, port, username, password) == 0){
        m_Channel = channel + 32;
        return true;
    }
    return false;
}

bool HIKNvr::GetNvrUTCDiff(){
    NET_DVR_NETAPPCFG struNAC = {0};
    DWORD ZoneSize = 0;
    NET_DVR_GetDVRConfig(LoginID, NET_DVR_GET_NETAPPCFG, 0, &struNAC, sizeof(NET_DVR_NETAPPCFG), &ZoneSize);
    int nDiffHour = struNAC.struNtpClientParam.cTimeDifferenceH;
    int nDiffMin = struNAC.struNtpClientParam.cTimeDifferenceM;

    nDiffTotalMin = (nDiffHour < 0 ? -1 : 1) * (abs(nDiffHour) * 60 + nDiffMin);
    return true;
}

/**
 * @brief 用年和月查询日
 * 
 * @param select_time The selected time.
 * @param available_date_vec The available date vector.
 * @return true if the time region is found, false otherwise.
 */
int HIKNvr::CheckTimeRegionWithMonth(DateTime select_time,std::vector<int>& available_date_vec)
{
    DateTime DateNow(select_time);

    // 查当月
    NET_DVR_MRD_SEARCH_PARAM struSearchParam = {0};
    NET_DVR_MRD_SEARCH_RESULT struSearchResult = {0};
    struSearchParam.dwSize = sizeof(NET_DVR_MRD_SEARCH_PARAM);
    struSearchParam.wYear = DateNow.year();
    struSearchParam.byMonth = DateNow.month();
    struSearchParam.struStreamInfo.dwChannel = m_Channel; // channel

    struSearchParam.byLocalOrUTC = true; // 0:local time, 1:UTC time (时区)
    struSearchParam.byDrawFrame = 0; // 0:不抽帧，1:抽帧
    struSearchParam.byStreamType = 0;// 0:主码流，1:子码流
    // 查记录
    if (!NET_DVR_GetDeviceConfig(this->LoginID, NET_DVR_GET_MONTHLY_RECORD_DISTRIBUTION, 0, &struSearchParam, sizeof(struSearchParam), NULL, &struSearchResult, sizeof(struSearchResult)))
    {
        int n = NET_DVR_GetLastError();
        return n == NET_DVR_NETWORK_FAIL_CONNECT ? NVR_ERR_CONNECT_7 : NVR_ERR_HIK;
    }

    for (int i = 0; i < 32; i++)
    {
        if (struSearchResult.byRecordDistribution[i])
        {
            available_date_vec.push_back(i + 1);
        }
    }
    return NVR_SUC;
}


/**
 * @brief Check if a given time falls within the available time range for the current day.
 * 
 * @param select_time The time to check.
 * @param available_time_vec A vector of available time ranges for the current day.
 * @return true if the given time falls within the available time range, false otherwise.
 */
int HIKNvr::CheckTimeRegionWithDay(DateTime select_time, std::vector<TimeRecord> &available_time_vec)
{
    int nRet = NVR_SUC;
    GetNvrUTCDiff();

    DateTime DateNow(select_time);

    int nYear = DateNow.year();
    int nMonth = DateNow.month();
    int nDay = DateNow.day();

    NET_DVR_FILECOND FileCond;

    FileCond.dwFileType = 0xff;// 0xff:所有文件
    FileCond.dwIsLocked = 0xff;// 0xff:所有文件
    FileCond.dwUseCardNo = 0; //不使用卡号

    FileCond.lChannel = m_Channel;//通道号

    FileCond.struStartTime.dwYear = DateNow.year();
    FileCond.struStartTime.dwMonth = DateNow.month();
    FileCond.struStartTime.dwDay = DateNow.day();   
    FileCond.struStartTime.dwHour = 0;
    FileCond.struStartTime.dwMinute = 0;
    FileCond.struStartTime.dwSecond = 0;

    FileCond.struStopTime.dwYear = DateNow.year();
    FileCond.struStopTime.dwMonth = DateNow.month();
    FileCond.struStopTime.dwDay = DateNow.day();
    FileCond.struStopTime.dwHour = 23;
    FileCond.struStopTime.dwMinute = 59;
    FileCond.struStopTime.dwSecond = 59;

    available_time_vec.clear();
    LONG hFindHandle = NET_DVR_FindFile_V30(LoginID, &FileCond);
    if (-1 == hFindHandle)
    {
        nRet = NET_DVR_GetLastError();
        return nRet == NET_DVR_NETWORK_FAIL_CONNECT?NVR_ERR_CONNECT_7:NVR_ERR_HIK;
    }
    else
    {
        NET_DVR_FINDDATA_V30 FindData; // 查找到的文件信息
        nRet = NET_DVR_FindNextFile_V30(hFindHandle, &FindData);
        int ItemIndex = 0;
        while (nRet > 0)
        {
            if (NET_DVR_FILE_EXCEPTION == nRet)//查找文件时异常
            {
                break;
            }
            else if (NET_DVR_FILE_NOFIND == nRet)//没有文件
            {
                break;
            }
            else if (NET_DVR_NOMOREFILE == nRet) // 查找结束
            {
                break;
            }
            else if (NET_DVR_ISFINDING == nRet) // 正在查找
            {
                nRet = NET_DVR_FindNextFile_V30(hFindHandle, &FindData);
                //请求过快，会出读错误。同时也是参考海康sdk写法。
                std::this_thread::sleep_for(std::chrono::milliseconds(5));
            }
            else if (NET_DVR_FILE_SUCCESS == nRet) // 获取文件信息成功
            {
                // 保存文件信息
                TimeRecord tR;

                if (nDay != FindData.struStartTime.dwDay)//起始不在今日
                {
                    tR.TimeStart.SetDateTime(FindData.struStartTime.dwYear, FindData.struStartTime.dwMonth, nDay, 0, 0, 0);
                }
                else//起始在今日
                {
                    tR.TimeStart.SetDateTime(FindData.struStartTime.dwYear, FindData.struStartTime.dwMonth, FindData.struStartTime.dwDay, FindData.struStartTime.dwHour, FindData.struStartTime.dwMinute, FindData.struStartTime.dwSecond);
                }
                if (nDay != FindData.struStopTime.dwDay)//结束不在今日
                {
                    tR.TimeEnd.SetDateTime(FindData.struStartTime.dwYear, FindData.struStartTime.dwMonth, FindData.struStartTime.dwDay, 23, 59, 59);
                }
                else//结束在今日
                {
                    tR.TimeEnd.SetDateTime(FindData.struStopTime.dwYear, FindData.struStopTime.dwMonth, FindData.struStopTime.dwDay,
                                           FindData.struStopTime.dwHour, FindData.struStopTime.dwMinute, FindData.struStopTime.dwSecond);
                }

                date::sys_days original_date_start{date::year{tR.TimeStart.year()} / date::month{static_cast<unsigned int>(tR.TimeStart.month())} / date::day{static_cast<unsigned int>(tR.TimeStart.day())}};
                date::sys_time<std::chrono::seconds> original_time_start{date::sys_days{original_date_start} + std::chrono::hours{tR.TimeStart.hour()} + std::chrono::minutes{tR.TimeStart.minute()} + std::chrono::seconds{tR.TimeStart.minute()}};
                date::sys_time<std::chrono::seconds> utc_plus_8_time_start = original_time_start - std::chrono::minutes{nDiffTotalMin};

                date::sys_days original_date_end{date::year{tR.TimeEnd.year()} / date::month{static_cast<unsigned int>(tR.TimeEnd.month())} / date::day{static_cast<unsigned int>(tR.TimeEnd.day())}};
                date::sys_time<std::chrono::seconds> original_time_end{date::sys_days{original_date_end} + std::chrono::hours{tR.TimeEnd.hour()} + std::chrono::minutes{tR.TimeEnd.minute()} + std::chrono::seconds{tR.TimeEnd.minute()}};
                date::sys_time<std::chrono::seconds> utc_plus_8_time_end = original_time_end - std::chrono::minutes{nDiffTotalMin};

                // 计算时间点到1970-01-01 00:00:00 UTC的时间间隔，即时间戳
                std::chrono::seconds timestamp = std::chrono::duration_cast<std::chrono::seconds>(utc_plus_8_time_start.time_since_epoch());
                tR.tmTimeStart = timestamp.count();
                timestamp = std::chrono::duration_cast<std::chrono::seconds>(utc_plus_8_time_end.time_since_epoch());
                tR.tmTimeEnd = timestamp.count();

                available_time_vec.emplace_back(tR);
                nRet = NET_DVR_FindNextFile_V30(hFindHandle, &FindData);
            }
        }
        // 关闭查找，释放句柄
        NET_DVR_FindClose_V30(hFindHandle);
    }

    return NVR_SUC;
}

/**
 * @brief Get the time region based on the selected time and value.
 * 
 * @param SelectTime The selected time.
 * @param Value The value used to calculate the time region.
 * @param TimeStart The start time of the time region.
 * @param TimeEnd The end time of the time region.
 * @return true if the time region is found, false otherwise.
 */
bool HIKNvr::GetTimeRegion(DateTime SelectTime, int Value, DateTime &TimeStart, DateTime &TimeEnd)
{
    int nYear = SelectTime.year();
    int nMonth = SelectTime.month();
    int nDay = SelectTime.day();

    // 时间格式 转换，找时间区间
    int nHour = Value / 3600;
    int nMinute = Value / 60 - 60 * nHour;
    int nSecond = Value - (nMinute + 60 * nHour) * 60;

    TimeStart = SelectTime;
    TimeStart.hour() = nHour;
    TimeStart.minute() = nMinute;
    TimeStart.second() = nSecond;    

    bool bHave = false;
    for (auto it : m_TimeRecords)
    {
        if (it.IsInRecord(TimeStart))
        {
            bHave = true;
            TimeEnd = it.TimeEnd;
            break;
        }
    }

    return bHave;
}

/**
 * @brief Turn the time to json.
 * 
 * @param Time The time to be turned to json.
 * @param pAvailableDateVec The available date vector.
 * @return nlohmann::json& The json object.
 */
nlohmann::json HIKNvr::TimeToJson(DateTime Time, std::vector<int> *pAvailableDateVec)
{
    nlohmann::json Message;

    //year and month
    Message["year"] = Time.year();
    Message["month"] = Time.month();
    
    //date
    if (pAvailableDateVec != nullptr)
    {
        nlohmann::json data;
        for (auto date : *pAvailableDateVec)
        {
            data.push_back(date);
        }
        Message["day"] = data;
    }
    return Message;
}

/**
 * @brief Converts available time slots in a day to JSON format.
 * 
 * @param Time The date and time of the day.
 * @param pAvailableTimeVec A pointer to a vector of available time slots.
 * @return nlohmann::json& A reference to the JSON object containing the converted time slots.
 */
nlohmann::json HIKNvr::TimeToJsonInDay(DateTime Time, std::vector<TimeRecord> *pAvailableTimeVec)
{
    nlohmann::json Message;
    if(pAvailableTimeVec != nullptr)
    {
        for(auto time : *pAvailableTimeVec)
        {
            nlohmann::json data;
            data["begin"] = time.tmTimeStart;
            data["end"] = time.tmTimeEnd;
            Message.push_back(data);
        }
    }
    return Message;
}



