#include <iostream>
#include <cstring>
#include <bitset>
#include <unistd.h>
#include <time.h>
#include "HCNetSDK.h"
#include "HkStreamer.h"
#include "convert.h"
using namespace std;
const int outcache_size=1024;
void CALLBACK fPlayDataCallBack(LONG lPlayHandle, DWORD dwDataType, BYTE *pBuffer, DWORD dwBufSize, void* dwUser)
{
    DWORD remained = dwBufSize;
    char cache[outcache_size]={0};
    BYTE *cp; // cp head point
    cp = pBuffer;
    while(remained>0){
        int cplen = outcache_size;
        if(remained<cplen){
            cplen=remained;
        }
        memcpy(cache, cp, cplen);
        cout.write(cache, outcache_size);
        if(cplen<remained){
            cp+=cplen;
        }
       remained-=cplen;
    }
    cout.flush();

}

bool checkResult(bool result, string msg){
  if(result){
    cerr<<msg<<":"<<NET_DVR_GetLastError()<<endl;
    return true;
  }
  return false;
}

int login(LPLoginInfo loginInfo)
{
  NET_DVR_Init();
  long lUserID;
  // login
  NET_DVR_USER_LOGIN_INFO struLoginInfo = {0};
  NET_DVR_DEVICEINFO_V40 struDeviceInfoV40 = {0};
  struLoginInfo.bUseAsynLogin = false;

  struLoginInfo.wPort = 8000;
  memcpy(struLoginInfo.sDeviceAddress, loginInfo->sDeviceAddress, NET_DVR_DEV_ADDRESS_MAX_LEN);
  memcpy(struLoginInfo.sUserName, loginInfo->sUserName, NAME_LEN);
  memcpy(struLoginInfo.sPassword, loginInfo->sPassword, NAME_LEN);
  std::cerr << struLoginInfo.sDeviceAddress << " " << struLoginInfo.sUserName << " " << struLoginInfo.sPassword << std::endl;
  lUserID = NET_DVR_Login_V40(&struLoginInfo, &struDeviceInfoV40);
  return lUserID;
}

{
  nvrTime->dwYear = date.dwYear;
  nvrTime->dwMonth = date.dwMonth;
  nvrTime->dwDay = date.dwDay;
  nvrTime->dwHour = date.dwHour;
  nvrTime->dwMinute = date.dwMinute;
  nvrTime->dwSecond = date.dwSecond;
}

int getCfg(LPLoginInfo loginInfo)
{
  long lUserID = login(loginInfo);
  if (lUserID < 0)
  {
    std::cerr << "pyd1---Login error" << NET_DVR_GetLastError() << std::endl;
    return HPR_ERROR;
  }
  NET_DVR_IPPARACFG_V40 ipcfg;
  DWORD bytesReturned = 0;
  ipcfg.dwSize = sizeof(NET_DVR_IPPARACFG_V40);
  int iGroupNO = 0;
  bool resCode = NET_DVR_GetDVRConfig(lUserID, NET_DVR_GET_IPPARACFG_V40, iGroupNO, &ipcfg, sizeof(NET_DVR_IPPARACFG_V40), &bytesReturned);
  if (!resCode)
  {
    DWORD code = NET_DVR_GetLastError();
    std::cout << "NET_DVR_GetDVRConfig failed " << NET_DVR_GetErrorMsg((LONG *)(&code)) << std::endl;
    NET_DVR_Logout(lUserID);
    NET_DVR_Cleanup();
    return -1;
  }
  std::cout << "设备组 " << ipcfg.dwGroupNum << " 数字通道个数 " << ipcfg.dwDChanNum << " 起始通道 " << ipcfg.dwStartDChan << std::endl
            << std::endl;
  for (int i = 0; i < ipcfg.dwDChanNum; i++)
  {
    NET_DVR_PICCFG_V30 channelInfo;
    bytesReturned = 0;
    channelInfo.dwSize = sizeof(NET_DVR_PICCFG_V30);
    int channelNum = i + ipcfg.dwStartDChan;
    NET_DVR_GetDVRConfig(lUserID, NET_DVR_GET_PICCFG_V30, channelNum, &channelInfo, sizeof(NET_DVR_PICCFG_V30), &bytesReturned);
    std::cout << "通道号 " << channelNum << "\t通道名称 " << channelInfo.sChanName;
    std::cout << "\t用户名 " << ipcfg.struIPDevInfo[i].sUserName << "\t密码 " << ipcfg.struIPDevInfo[i].sPassword;
    std::cout << "\t设备ID " << (long)ipcfg.struIPDevInfo[i].szDeviceID;
    std::cout << "\tip地址 " << ipcfg.struIPDevInfo[i].struIP.sIpV4 << "\t端口 " << ipcfg.struIPDevInfo[i].wDVRPort << std::endl;
  }
  NET_DVR_Logout(lUserID);
  NET_DVR_Cleanup();
  return 0;
}

int _playback(long lUserID, int timezone,LPStream stream) {
    NET_DVR_VOD_PARA struVodPara = {0};
  convert_isotimestr_time(stream->start,timezone, &(struVodPara.struBeginTime));
  convert_isotimestr_time(stream->start,timezone, &(struVodPara.struEndTime));
  struVodPara.struIDInfo.dwChannel = stream->lChannel;
  int hPlayback = NET_DVR_PlayBackByTime_V40(lUserID, &struVodPara);
  if(checkResult(hPlayback<0,"play back error")){
    return HPR_ERROR;
  }
  char usrInfo[200];
  if(checkResult(!NET_DVR_SetPlayDataCallBack_V40(hPlayback, fPlayDataCallBack, usrInfo), "setup callback error")){
    return HPR_ERROR;
  }

  if(checkResult(!NET_DVR_PlayBackControl_V40(hPlayback, NET_DVR_PLAYSTART,NULL, 0, NULL, NULL),"start playback error")){
    return HPR_ERROR;
  }
  if(checkResult(!NET_DVR_PlayBackControl_V40(hPlayback, NET_DVR_SETSPEED,&(stream->bitrate), 0, NULL, NULL),"set plackback speed error")){
    return HPR_ERROR;
  }
  int pos = 0;
  for (pos = 0; pos < 100 && pos >= 0; pos = NET_DVR_GetDownloadPos(hPlayback))
  {
    // output progress in cerr
    std::cerr<<"Pos:"<<pos<<std::endl;
    sleep(10);
  }
  if(checkResult(!NET_DVR_StopGetFile(hPlayback),"failed to stop playback")){
    return HPR_ERROR;
  }
  return HPR_OK;
}

int _readChannelConfig(long lUserID,LPStream stream){
  NET_DVR_IPPARACFG_V40 ipcfg;
  DWORD bytesReturned = 0;
  ipcfg.dwSize = sizeof(NET_DVR_IPPARACFG_V40);
  int iGroupNO = 0;
  bool resCode = NET_DVR_GetDVRConfig(lUserID, NET_DVR_GET_IPPARACFG_V40, iGroupNO, &ipcfg, sizeof(NET_DVR_IPPARACFG_V40), &bytesReturned);
  if(checkResult(!resCode, "read channel config fail")){
    return HPR_ERROR;
  }
  int startNum = ipcfg.dwStartDChan;
  if(checkResult(stream->lChannel > ipcfg.dwDChanNum, "channel not correct")){
    return HPR_ERROR;
  }

  stream->lChannel = ipcfg.dwStartDChan + stream->lChannel -1;
  stream->bitrate = 2048; // controll speed for download, TODO: get bitrate from device;
}

int playback(LPLoginInfo loginInfo, LPStream stream)
{
  long lUserID = login(loginInfo);
  if(checkResult(lUserID<1, "device login error")){
    return HPR_ERROR;
  }
  // TODO: find timezone cfg
  int timezone = 8;
  // TODO: find actually channel
  stream->lChannel = stream->lChannel + 33/*channel start*/ - 1;
  int playbackResult = _playback(lUserID, timezone, stream);
  NET_DVR_Logout(lUserID);
  NET_DVR_Cleanup();
  return playbackResult;
}
