// HttpInterface.cpp: implementation of the CHttpInterface class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "HttpInterface.h"
#include "SimpleLiveViewer.h"

#include "TraceWin.h"
//Data Type
#define	DEF_RESPONSE_DATA		1	//CGI Response
#define	DEF_AUDIO_LIVE			2	//Audio Live Data 
#define	DEF_AUDIO_PLAYBACK		3	//Audio Playback Data 
#define	DEF_AUDIO_BACKUP		4	//Audio Backup Data 
#define	DEF_VIDEO_LIVE			10	//Video Live Data 
#define	DEF_VIDEO_PLAYBACK		11	//Video Playback Data
#define	DEF_VIDEO_BACKUP		12	//Video Live Data
#define	DEF_INFO_DATA			20	//DVR Information
#define DEF_MBACKUP_SIZE		30

#define	DEF_MODE_LIVE			0
#define DEF_MODE_LIVE_AUDIO		1
#define	DEF_MODE_PLAY			2
#define	DEF_MODE_PLAY_AUDIO		3
#define	DEF_MODE_SEARCH			4
#define	DEF_MODE_CONTROL		5

//Connection Error(1byte)
#define ERR_USER_UNKNOWN		1	//Unknown User
#define ERR_PASSWORD_MISS		2	//Password Mismatched
#define ERR_PROTOCOL_VER		3	//Protocol Version Mismatched
#define ERR_CLIENT_LIMIT		4	//No More Connection Allowed
#define ERR_SUPER_EXIST			5	//Super User Already Connected
#define ERR_NO_VIDEODATA		6	//No Video Data
#define ERR_NO_VIDEOCH			7	//No Video Channel
#define ERR_ETC_UNKNOWN			8	//Unknown Error
#define MPEG_PUSH_END			10

#define UDR_304_INFOLEN	54
#define UDR_308_INFOLEN	106
#define UDR_304		4
#define UDR_308		8
#define UDR_316		16

//##################################################
//Client Command definition
//##################################################
#define	CMD_SEARCH_DISK			1	//SearchDisk_CGI
#define	CMD_HDD_INFO			2	//RequestHDDInfo_CGI
#define	CMD_AV_CHANNEL			3	//AVChannelEnable_CGI
#define	CMD_STOP_ANDLIVE		4	//StopAndReturnLive_CGI
#define	CMD_STOP_ANDQUIT		5	//StopAndDisconnect_CGI 
#define	CMD_SEND_REQUEST		6	//RequestStoredData_CGI
#define	CMD_PTZF_CTRL			7	//PTZControl_CGI
#define	CMD_PLAYBACK_CTRL		8	//PlaybackControl_CGI
#define	CMD_PARAM_READ			9	//DVRParameterRead_CGI
#define	CMD_PARAM_WRITE			10	//DVRParameterWrite_CGI
#define CMD_RECORD				11	//DVR_RecordButton
#define CMD_SW_UPGRADE_RAMDISK	12	//DVR_SW_Upgrade_Ramdisk
#define CMD_SW_UPGRADE_KERNEL	13	//DVR_SW_Upgrade_Kernel
#define	CMD_AUDIO_STOP			14  //Audio Off
#define CMD_SEARCH_DISK_DEL		15	//Delete SearchDisk DataBase
#define CMD_QUICK_INSTALLER		16	//Quick Installer
#define	CMD_AUDIO_CHANNEL		17	//AUDIO CHANNEL Control
#define	CMD_VIDEO_CHANNEL		18	//VIDEO CHANNEL Control

BYTE g_pbybuf[MAX_PES_SIZE];
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CHttpInterface::CHttpInterface(CWnd* pWnd)
{
	m_pWnd = pWnd;
	m_pMpegThread = NULL;

	m_pDecoder = NULL;

	for(UINT i=0; i<MAX_CHANNEL;i++){
		m_bGopCode[i] = FALSE;
	}

	m_hInternetOpen = NULL;
	m_hInternetConnect = NULL;
	m_hHttpOpenRequest = NULL;

	m_bConnect = FALSE;
	m_nClientID = 0;
}

CHttpInterface::~CHttpInterface()
{

}

void CHttpInterface::ConnectDVR(CString strDVRIPAddress, CString strPort, CString strUser, CString strPassword)
{
	m_strDVRIPAddress = strDVRIPAddress;
	m_nDVRPort = atoi(strPort);
	m_strUser = strUser;
	m_strPassword = strPassword;

	m_bConnect = FALSE;
	m_bThreadBreak = FALSE;
	m_nResolution = 0xFF;
	
	for(UINT i=0; i<MAX_CHANNEL;i++){
		m_bGopCode[i] = FALSE;
	}

	if(m_pDecoder)
	{
		m_pDecoder->Deinitialize();
		delete m_pDecoder;
		m_pDecoder = NULL;
	}
	m_pDecoder = new CDecoder;
	m_pDecoder->m_pControlImageDraw = m_pWnd->GetDlgItem(IDC_IMAGEVIEW);
	m_DataParsing.InitParse();
	m_pMpegThread = AfxBeginThread(MpegThreadProc, this);
}

void CHttpInterface::DisconnectedDVR()
{
	m_bThreadBreak = TRUE;
	if(m_bConnect)
	{
		SendCGICommand(CMD_STOP_ANDQUIT);
	}

	if(m_pMpegThread)		KillMpegThread();
	if(m_pDecoder)
	{
		m_pDecoder->Deinitialize();
		delete m_pDecoder;
		m_pDecoder = NULL;
	}
	CloseOpenedHandle();
	m_bConnect = FALSE;
	m_DataParsing.DestroyParse();
}

UINT MpegThreadProc(LPVOID lParam)
{
	CHttpInterface* pHttpInterface = (CHttpInterface*)lParam;
	try{
		return pHttpInterface->MpegProcess();
	}
	catch(...){
		TRACE("Thread Error\n");
	}

	return 0;
}

UINT CHttpInterface::MpegProcess()
{
	DWORD dwAvailableDataLength = 0, dwReceiveDataLength = 0;
	BOOL bResultQueryDataAvailable;
	BOOL bResultReadFile;
	BYTE strReceiveBuffer[10000];

	MSG Message;
	m_bThreadBreak = FALSE;

	while(!m_bThreadBreak){
		if(!m_bConnect){
			if(!HttpConnect())
			{
				AfxMessageBox("Connection Failed!!");
				break;
			}
		}
		bResultQueryDataAvailable = InternetQueryDataAvailable(m_hHttpOpenRequest, &dwAvailableDataLength, 0, 0);
		if(m_bThreadBreak)	break;

		bResultReadFile = InternetReadFile(m_hHttpOpenRequest, strReceiveBuffer, dwAvailableDataLength, &dwReceiveDataLength);

		if(bResultReadFile && dwReceiveDataLength > 0){
			m_bConnect = TRUE;
			PacketParse(strReceiveBuffer, dwReceiveDataLength);
			if(m_bThreadBreak)	break;
		}

		if(PeekMessage(&Message, NULL, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&Message);
			DispatchMessage(&Message);
		}
	}
	m_bConnect = FALSE;
	return 0;
}

BOOL CHttpInterface::HttpConnect()
{
	m_hInternetOpen = InternetOpen("HTTPCONNECT", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
	if(m_hInternetOpen == NULL){
		TRACE("InternetOpen failed..\n");
		return FALSE;
	}

	m_hInternetConnect = InternetConnect(m_hInternetOpen, m_strDVRIPAddress, m_nDVRPort, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 1);
	if(m_hInternetConnect == NULL){
		CloseOpenedHandle();
		TRACE("InternetConnect failed..\n");
		return FALSE;
	}

	CString strConnectArg;
	strConnectArg = "/cgi-bin/webapp.cgi?";
	m_hHttpOpenRequest = HttpOpenRequest(m_hInternetConnect, "POST", (LPCTSTR)strConnectArg, "HTTP/1.0", NULL, NULL, INTERNET_FLAG_RELOAD | INTERNET_FLAG_DONT_CACHE, 1);
	if(m_hHttpOpenRequest == NULL){
		CloseOpenedHandle();
		TRACE("HttpOpenRequest failed..\n");
		return FALSE;
	}


	TRACE("HttpOpenRequest\n");
	CString strHeaders = _T("Content-Type: application/x-www-form-urlencoded");
	CString strQueryString;
	strQueryString.Format("MODE=%d&ID=%s&PW=%s&VER=3000&CH=FFFF", DEF_MODE_LIVE, m_strUser, m_strPassword);

	BOOL bResultHttpSendRequest = HttpSendRequest(m_hHttpOpenRequest, (LPCTSTR)strHeaders, strlen(strHeaders), 
		(LPVOID)(LPCTSTR)strQueryString, strlen(strQueryString));
	if(!bResultHttpSendRequest){
		CloseOpenedHandle();
		TRACE("HttpSendRequest failed..\n");
		return FALSE;
	}
	TRACE("HttpSendRequest\n");
	TCHAR szStatusCode[10];
	DWORD dwInfoSize = 10;
	HttpQueryInfo(m_hHttpOpenRequest, HTTP_QUERY_STATUS_CODE, szStatusCode, &dwInfoSize, NULL);

	long nStatusCode = _ttol(szStatusCode);
	if (nStatusCode != HTTP_STATUS_OK)
	{
		TRACE("HttpQueryInfo Error\n");
		return FALSE;
	}

	return TRUE;
}

void CHttpInterface::CloseOpenedHandle()
{
	if(m_hHttpOpenRequest){
		InternetCloseHandle(m_hHttpOpenRequest);
		m_hHttpOpenRequest = NULL;
	}
	if(m_hInternetConnect){
		InternetCloseHandle(m_hInternetConnect);
		m_hInternetConnect = NULL;
	}
	if(m_hInternetOpen){
		InternetCloseHandle(m_hInternetOpen);
		m_hInternetOpen = NULL;
	}
}

void CHttpInterface::PacketParse(BYTE* pszReceiveData, DWORD dwReceiveDataLength)
{
	PARSE_DATA *ParseData;
	ParseData = new PARSE_DATA;
	bool bReParsing;
	bReParsing = m_DataParsing.PacketParse(ParseData, pszReceiveData, dwReceiveDataLength);
	while(bReParsing){
		if(ParseData->nDataInfo == DEF_RESPONSE_DATA){
			if(ParseData->nDataSize == 1 && ParseData->DataBuf[0] == MPEG_PUSH_END)
				AfxMessageBox("Mpeg Push End");
			else{
				CloseOpenedHandle();	// opened handle close
				m_bThreadBreak = TRUE;
				break;
			}
		}
		else if(ParseData->nDataInfo == DEF_INFO_DATA){
			m_nClientID = m_DataParsing.GetClientID();
			DVRINFO* pDVRInfo = (DVRINFO*)ParseData->DataBuf;
			for(int i=0; i<pDVRInfo->nCHInfo; i++)
			{
				if(!(BOOL)(ntohs(pDVRInfo->bmChannel) & (WORD)(0x01 << i)))
				{
					TRACE("No authority\n");
				}
			}
		}
		else if(ParseData->nDataInfo == DEF_VIDEO_LIVE ||
			ParseData->nDataInfo == DEF_VIDEO_LIVE_JPEG ||
			ParseData->nDataInfo == DEF_VIDEO_PLAYBACK ||
			ParseData->nDataInfo == DEF_VIDEO_BACKUP){
			ParseGOPData((LPSTR)ParseData->DataBuf, ParseData->nDataSize);
		}
		bReParsing = m_DataParsing.PacketParse(ParseData);
	}
	delete ParseData;
}

void CHttpInterface::ReceiveDataErrorMessage(BYTE nError)
{
	switch(nError)
	{
		case ERR_USER_UNKNOWN:
			AfxMessageBox(IDS_USERUNKNOWN);
			break;
		case ERR_PASSWORD_MISS:
			AfxMessageBox(IDS_DVRPASSWORDMISS);
			break;
		case ERR_PROTOCOL_VER:
			AfxMessageBox(IDS_PROTOCOLERROR);
			break;
		case ERR_CLIENT_LIMIT:
			AfxMessageBox(IDS_CLIENTLIMIT);
			break;
		case ERR_SUPER_EXIST:
			AfxMessageBox(IDS_SUPEREXIST);
			break;
		case ERR_NO_VIDEODATA:
			AfxMessageBox(IDS_NOVIDEO);
			break;
		case ERR_NO_VIDEOCH:
			AfxMessageBox(IDS_NOVIDEOCH);
			break;
		case ERR_ETC_UNKNOWN:
			AfxMessageBox(IDS_ETCERROR);
			break;
	}
}

BOOL CHttpInterface::ParseGOPData(LPSTR lpszReceiveData, DWORD dwReceiveDataLength)
{
	FRAME_HEADER pFrameHeader;
	memcpy(&pFrameHeader, lpszReceiveData, sizeof(FRAME_HEADER));

	if(m_nResolution != ((UINT)((pFrameHeader.info & 0x40) >> 4) + (UINT)((pFrameHeader.info & 0x7) >> 1)))
	{
		m_nResolution = ((UINT)((pFrameHeader.info & 0x40) >> 4) + (UINT)((pFrameHeader.info & 0x7) >> 1));
		m_pDecoder->SetImageSize(m_nResolution);
		
		for(UINT i=0; i<MAX_CHANNEL; i++)
		{
			m_pDecoder->SettingResolution(i);
		}
		m_pDecoder->MemInit();
	}

	int type = (unsigned char)(( ntohl( pFrameHeader.VE_STATUS0) >> 22) & 0x03);
	if(type == I_PICTURE){
		struct timeval tv;
		
		tv.tv_sec = ntohl( pFrameHeader.VE_STATUS5);
		tv.tv_usec = ntohl( pFrameHeader.VE_STATUS6);

		DisplayTime(&tv);
	}
		
	unsigned char chid;
	chid = (unsigned char)ntohl(pFrameHeader.VE_STATUS9);

	DWORD frameSize = (ntohl( pFrameHeader.VE_STATUS0) & 0xFFFFF);

	DWORD raw_header_size = ntohl( pFrameHeader.packetsize) - ntohl( pFrameHeader.VE_STATUS10);

	if((BOOL)(ntohs(pFrameHeader.videoLoss) & (WORD)(0x01 << chid))){
		TRACE("VIDEO LOSS CH-%d\n", chid + 1);
		return TRUE;
	}
	
	ImageDisplay(chid, lpszReceiveData + (sizeof( FRAME_HEADER) + raw_header_size), frameSize, type);
	return TRUE;
}

void CHttpInterface::ImageDisplay(UINT nCH, LPSTR lpszData, DWORD dwDataLen, int type)
{
	if(dwDataLen == 0)		return;

	memcpy(g_pbybuf, lpszData, dwDataLen);
	if(type == I_PICTURE){
		m_bGopCode[nCH] = TRUE;
	}
	if(m_bGopCode[nCH])
	{
		if(type == I_PICTURE || type == B_PICTURE || type == P_PICTURE)
		{
			CRect rect;
			m_pWnd->GetDlgItem(IDC_IMAGEVIEW)->GetClientRect(&rect);
			m_pDecoder->setDisplaySize(nCH, rect.right - rect.left, rect.bottom - rect.top);
			m_pDecoder->DecodePicture( nCH, type, g_pbybuf, dwDataLen);
		}
	}
}

BOOL CHttpInterface::SendCGICommand(BYTE nCode)
{
	m_strSendCgiCommandQueryString = "";
	CString strTemp;
	switch(nCode)
	{
		case CMD_STOP_ANDQUIT:
			m_strSendCgiCommandQueryString.Format("MODE=%d&CID=%d&OP=%d", DEF_MODE_CONTROL, m_nClientID, nCode);
			break;
		default:
			break;
	}

	HANDLE hThread;
	DWORD dwThreadID;

	hThread = CreateThread(NULL, 0, SendCGICommandConnectThread, this, 0, &dwThreadID);
	DWORD dwTimeOut = 7000;
	if(WaitForSingleObject(hThread, dwTimeOut) == WAIT_TIMEOUT)
	{
		if(m_hSendCGIRequest)	InternetCloseHandle(m_hSendCGIRequest);
		if(m_hSendCGIHttp)		InternetCloseHandle(m_hSendCGIHttp);
		if(m_hSendCGIInternetOpen)	InternetCloseHandle(m_hSendCGIInternetOpen);
		m_hSendCGIRequest = NULL;
		m_hSendCGIHttp = NULL;
		m_hSendCGIInternetOpen = NULL;
		CloseHandle(hThread);
		return FALSE;
	}
	CloseHandle(hThread);
	return TRUE;
}

BOOL CHttpInterface::KillMpegThread()
{
	DWORD dwExitCode;
	DWORD dwResult = WaitForSingleObject(m_pMpegThread->m_hThread, 1000);
	switch(dwResult){
		case WAIT_OBJECT_0:
			return TRUE;
		case WAIT_TIMEOUT:
			if(m_pMpegThread)	GetExitCodeThread(m_pMpegThread->m_hThread, &dwExitCode);
			if(m_pMpegThread)	TerminateThread(m_pMpegThread->m_hThread, dwExitCode);
			return TRUE;
	}
	return FALSE;
}

DWORD WINAPI SendCGICommandConnectThread(LPVOID lParam)
{
	CHttpInterface* pHttpInterface = (CHttpInterface*)lParam;

	CString   strConnectArg;
	CString   strHeaders =  _T("Content-Type: application/x-www-form-urlencoded");
	CString   strQueryString = "", strTemp;
	
	strConnectArg.Format("/cgi-bin/webapp.cgi?");
	
	try{
		pHttpInterface->m_hSendCGIInternetOpen = InternetOpen("MPEGCGITEST", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
		if(pHttpInterface->m_hSendCGIInternetOpen)
		{
			pHttpInterface->m_hSendCGIHttp = InternetConnect(pHttpInterface->m_hSendCGIInternetOpen, pHttpInterface->m_strDVRIPAddress, 
				pHttpInterface->m_nDVRPort, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 1);
		}
		else
		{
			InternetCloseHandle(pHttpInterface->m_hSendCGIInternetOpen);
			return FALSE;
		}

		if(pHttpInterface->m_hSendCGIHttp)
			pHttpInterface->m_hSendCGIRequest = HttpOpenRequest(pHttpInterface->m_hSendCGIHttp, "POST", (LPCTSTR)strConnectArg, "HTTP/1.0", NULL, NULL, INTERNET_FLAG_RELOAD | INTERNET_FLAG_DONT_CACHE, 1);		
		else
		{
			InternetCloseHandle(pHttpInterface->m_hSendCGIHttp);
			InternetCloseHandle(pHttpInterface->m_hSendCGIInternetOpen);
			return FALSE;
		}
		if(pHttpInterface->m_hSendCGIRequest)
		{
			HttpSendRequest(pHttpInterface->m_hSendCGIRequest, (LPCTSTR)strHeaders, strlen(strHeaders),
				(LPVOID)(LPCTSTR)pHttpInterface->m_strSendCgiCommandQueryString, 
				strlen(pHttpInterface->m_strSendCgiCommandQueryString));
		}
		else
		{
			InternetCloseHandle(pHttpInterface->m_hSendCGIRequest);
			InternetCloseHandle(pHttpInterface->m_hSendCGIHttp);
			InternetCloseHandle(pHttpInterface->m_hSendCGIInternetOpen);
			return FALSE;
		}

		InternetCloseHandle(pHttpInterface->m_hSendCGIRequest);
		InternetCloseHandle(pHttpInterface->m_hSendCGIHttp);
		InternetCloseHandle(pHttpInterface->m_hSendCGIInternetOpen);
		pHttpInterface->m_hSendCGIRequest = NULL;
		pHttpInterface->m_hSendCGIHttp = NULL;
		pHttpInterface->m_hSendCGIInternetOpen = NULL;
	}
	catch(...){

	}
	return TRUE;
}

void CHttpInterface::RedrawVideo(UINT nCH)
{
	CRect rect;
	m_pWnd->GetDlgItem(IDC_IMAGEVIEW)->GetClientRect(&rect);
	if(m_pDecoder == NULL)		return;
	
	m_pDecoder->setDisplaySize(nCH, rect.right - rect.left, rect.bottom - rect.top);
	m_pDecoder->RedrawVideo(nCH);
}

void CHttpInterface::DisplayTime(timeval* time)
{
	struct tm tmtime;
	time_t tt = (time_t)time->tv_sec;
	tmtime = *gmtime((const time_t*)&tt);
	
	CString strOSD;

	strOSD.Format("%04d-%02d-%02d %02d:%02d:%02d", tmtime.tm_year + 1900,
		tmtime.tm_mon + 1, tmtime.tm_mday, tmtime.tm_hour, tmtime.tm_min, tmtime.tm_sec);
	m_pWnd->GetDlgItem(IDC_OSD)->SetWindowText(strOSD);
}
