#include <iostream>
#include <fstream>
#include <windows.h>
#include <process.h>
#include <vector>
#include <string>
#include "dhnetsdk.h"
#include "dhconfigsdk.h"

#pragma comment(lib, "dhnetsdk.lib")
#pragma comment(lib, "dhconfigsdk.lib")

using namespace std;

void CALLBACK DisConnectFunc(LLONG lLoginID, char *pchDVRIP, LONG nDVRPort, LDWORD dwUser)
{
    cout << "Disconnect ..." << endl;
    return;
}

void CALLBACK HaveReConnectFunc(LLONG lLoginID, char *pchDVRIP, LONG nDVRPort, LDWORD dwUser)
{
    cout << "Reconnect ..." << endl;
    return;
}

BOOL CALLBACK MessageCallBack(LONG lCommand, LLONG lLoginID, char *pBuf, DWORD dwBufLen, char *pchDVRIP, LONG nDVRPort, LDWORD dwUser)
{
    cout << "lCommand = " << lCommand << endl;
    return TRUE;
}

void CALLBACK CB_NotifyTalkState(LLONG lAttachHandle, NET_TALK_STATE* pstuState, int nLen, LDWORD dwUser)
{
    cout << "Receive talk state notify." << endl;
    cout << "szCallID: " << pstuState->szCallID << endl;
    cout << "nAudioPort: " << pstuState->nAudioPort << endl;
    cout << "nVideoPort: " << pstuState->nVideoPort << endl;
    cout << "szMediaAddr: " << pstuState->szMediaAddr << endl;
    cout << "emState: " << pstuState->emState << endl;
    cout << "nNumberCount: " << pstuState->nNumberCount << endl;
    for (int i = 0; i < pstuState->nNumberCount; i++)
    {
        cout << i + 1 << ", szNumbers: " << pstuState->szNumbers[i] << endl;
    }
}

void CALLBACK CB_pfAudioDataCallBack(LLONG lTalkHandle, char *pDataBuf, DWORD dwBufSize, BYTE byAudioFlag, LDWORD dwUser)
{
    if (NET_TALK_DATA_RESPOND == byAudioFlag)
    {
        cout << "NET_TALK_DATA_RESPOND" << endl;
    }
    else if (NET_TALK_DATA_LOCAL_AUDIO == byAudioFlag)
    {
        //cout << "NET_TALK_DATA_LOCAL_AUDIO" << endl;
        DWORD dwSendLen = CLIENT_TalkSendData(lTalkHandle, pDataBuf, dwBufSize);
    }
    else if (NET_TALK_DATA_RECV_AUDIO == byAudioFlag)
    {
        //cout << "NET_TALK_DATA_RECV_AUDIO" << endl;
        CLIENT_AudioDecEx(lTalkHandle, pDataBuf, dwBufSize);
    }
}

unsigned __stdcall RefuseCall(void* pArguments)
{
    NET_VT_TALK_PARAM stVtParam = {sizeof(stVtParam)};
    stVtParam.nValidFlag = EM_VT_PARAM_VALID_ACTION;
    stVtParam.emAction = EM_NEWCALL_ACTION_REFUSE;
    BOOL bRet = CLIENT_SetDeviceMode((LLONG)pArguments, DH_TALK_VT_PARAM, &stVtParam);
    return 0;
}

unsigned __stdcall AcceptCall(void* pArguments)
{
    LLONG lTalk = CLIENT_StartTalkEx((LLONG)pArguments, CB_pfAudioDataCallBack, 0);
    if (0 == lTalk)
    {
        return 0;
    }
    BOOL bRet = CLIENT_RecordStartEx((LLONG)pArguments);
    int nCount = 60;
    while (nCount--)
    {
        Sleep(1000);
    }
    bRet = CLIENT_RecordStopEx((LLONG)pArguments);
    bRet = CLIENT_StopTalkEx(lTalk);
    return 0;
}

bool bTalking = false;
bool bStop = false;

struct ThreadArgs
{
    LLONG lLogin;
    LLONG lTalk;
};
ThreadArgs stuArgs;

unsigned __stdcall HangupCall(void* pArguments)
{
    ThreadArgs *pargs = (ThreadArgs *)pArguments;
    CLIENT_RecordStopEx(pargs->lLogin);
    CLIENT_StopTalkEx(pargs->lTalk);
    bTalking = false;
    return 0;
}

int CALLBACK CB_pfVtEventCallBack(LLONG instId, LLONG ulRegisterId, LLONG ulSessionId, int nEvent, char *pDataBuf, DWORD dwBufSize, LDWORD dwUser)
{
    if (EM_AUDIO_CB_FLAG_NEWCALL == nEvent)
    {
        HANDLE hThread;
        unsigned threadID;
        hThread = (HANDLE)_beginthreadex(NULL, 0, &AcceptCall, (void *)dwUser, 0, &threadID);
    }
    if (EM_AUDIO_CB_FLAG_REMOTE_HANGUP == nEvent)
    {
        HANDLE hThread;
        unsigned threadID;
        stuArgs.lLogin = dwUser;
        stuArgs.lTalk = ulSessionId;
        hThread = (HANDLE)_beginthreadex(NULL, 0, &HangupCall, (void *)&stuArgs, 0, &threadID);
    }
    return 0;
}

const int ATTACH_WAIT_TIME      = 60000;
const int FUNCTION_WAIT_TIME    = 3000;
const int SDK_API_WAITTIME      = 5000;

int main()
{
    BOOL bRet = FALSE;
    NET_VT_TALK_PARAM stVtParam = {sizeof(stVtParam)};
    std::vector<LLONG> vecId;
    std::string operation;

	/* init first */
    CLIENT_Init(DisConnectFunc, 0);
	
    /* set reconnect callback function */
    CLIENT_SetAutoReconnect(HaveReConnectFunc, 0);

	CLIENT_SetDVRMessCallBack(MessageCallBack, NULL);

	/* Login */
    char szAddr[64] = {"172.23.2.70"};
    //char szAddr[64] = {"172.23.1.184"};
    int nPort = 37777;
    cout << "Input device's IP Address and Port, e.g: 172.23.2.70 37777" << endl;
    cin >> szAddr >> nPort;

    char szUserName[64] = "admin";
    char szPassword[64] = "admin";
    cout << "Input user name and password, e.g: admin admin" << endl;
    cin >> szUserName >> szPassword;

    NET_DEVICEINFO stDevInfo = {0};
    int nError = 0;
    //LLONG lLoginHandle = CLIENT_Login(szAddr, nPort, szUserName, szPassword, &stDevInfo, &nError);
	LLONG lLoginHandle = CLIENT_LoginEx(szAddr, nPort, szUserName, szPassword, 0, NULL, &stDevInfo, &nError);
	if (0 == lLoginHandle)
	{
        cout << "Login failed! error code " << nError << endl;
        goto DEMO_END;
    }

    // 设置参数
    stVtParam.nValidFlag = EM_VT_PARAM_VALID_EVENT_CB | EM_VT_PARAM_VALID_USER_DATA | EM_VT_PARAM_VALID_MID_NUM 
        | EM_VT_PARAM_VALID_WAITTIME | EM_VT_PARAM_VALID_VIDEOWND | EM_VT_PARAM_VALID_CSMODE 
        | EM_VT_PARAM_VALID_AUDIO_ENCODE;
    stVtParam.pfEventCb = CB_pfVtEventCallBack;
    stVtParam.dwUser = lLoginHandle;
    strcpy(stVtParam.szPeerMidNum, "10110101");
    stVtParam.nWaitTime = 3000;
    stVtParam.hVideoWnd = (HWND)0x01;
    stVtParam.bClient = TRUE;
    stVtParam.stAudioEncode.encodeType = DH_TALK_PCM;
    stVtParam.stAudioEncode.nAudioBit = 16;
    stVtParam.stAudioEncode.dwSampleRate = 16000;
    stVtParam.stAudioEncode.nPacketPeriod = 25;
    bRet = CLIENT_SetDeviceMode(lLoginHandle, DH_TALK_VT_PARAM, &stVtParam);
    if (!bRet)
    {
        cout << "CLIENT_SetDeviceMode failed" << endl;
        goto DEMO_END;
    }

    // 注册到VTO
    while (1)
    {
        char szAddrVto[64] = {"172.23.2.70"};
        int nPortVto = 13801;
        cout << "Register to VTO, please input VTO ip and port, e.g: 172.23.2.70 13801" << endl;
        cin >> szAddr >> nPort;
        LLONG lRegister = CLIENT_VT_RegisterVto(szAddrVto, nPortVto);
        if (0 == lRegister)
        {
            cout << "CLIENT_VT_RegisterVto failed" << endl;
            continue;
        }
        vecId.push_back(lRegister);

        cout << "Input end to end register or continue to continue register, e.g: end" << endl;
        cin >> operation;
        if ("continue" == operation)
        {
            continue;
        }
        else
        {
            break;
        }
    }

    //while (1)   // 测试用
    {
        cout << "Input one mid number, e.g: 10110101" << endl;
        cin >> operation;
        if (8 != operation.size())
        {
            cout << "mid number length is error" << endl;
            goto DEMO_END;
        }

        stVtParam.nValidFlag = EM_VT_PARAM_VALID_MID_NUM;
        strncpy(stVtParam.szPeerMidNum, operation.c_str(), sizeof(stVtParam.szPeerMidNum) - 1);
        CLIENT_SetDeviceMode(lLoginHandle, DH_TALK_VT_PARAM, &stVtParam);

        LLONG lTalk = CLIENT_StartTalkEx(lLoginHandle, CB_pfAudioDataCallBack, 0);
        if (!lTalk)
        {
            cout << "CLIENT_StartTalkEx failed" << endl;
            goto DEMO_END;
        }

        bRet = CLIENT_RecordStartEx(lLoginHandle);
        if (!bRet)
        {
            cout << "CLIENT_RecordStartEx failed" << endl;
            CLIENT_StopTalkEx(lTalk);
            goto DEMO_END;
        }

        cout << "talk will stop after 60s" << endl;
        int nCount = 60;
        while (nCount--)
        {
            Sleep(1000);
        }
      
        bRet = CLIENT_RecordStopEx(lLoginHandle);
        if (!bRet)
        {
            cout << "CLIENT_RecordStopEx failed" << endl;
            CLIENT_StopTalkEx(lTalk);
            goto DEMO_END;
        }

        bRet = CLIENT_StopTalkEx(lTalk);
        if (!bRet)
        {
            cout << "CLIENT_StopTalkEx failed" << endl;
            goto DEMO_END;
        }
    }

DEMO_END:
    std::vector<LLONG>::const_iterator itV = vecId.begin();
    while (itV != vecId.end())
    {
        CLIENT_VT_UnRegisterVto(*itV);
        itV++;
    }

    /* Logout */
	bRet = CLIENT_Logout(lLoginHandle);
	if (TRUE != bRet)
	{
        cout << "Logout failed!" << endl;
    }
    CLIENT_Cleanup();

    system("PAUSE");
	return 0;
}

