#include <iostream>
#include <map>
#include <vector>

#include "stdint.h"
#include <atlbase.h>
#include "bCapServer/bcap_server.h"
#include "bCapBase.h"
#include "bCapController.h"
#include "bCapVariable.h"

using namespace std;

typedef map<int32_t, bCapBase*> map_server;
map_server map_ds;

static HRESULT find_open_address(int32_t *newVal)
{
	int32_t index;
	map_server::iterator it;

	for(index = 0; index < BCAP_OBJECT_MAX; index++){
		it = map_ds.find(index);
		if(it == map_ds.end()){
			*newVal = index;
			return S_OK;
		}
	}

	return E_MAX_OBJECT;
};

static HRESULT find_object(int32_t Handle, int Type, bCapBase **pObj)
{
	bCapBase *tmpObj = NULL;

	tmpObj = map_ds[Handle];
	if(tmpObj == NULL || tmpObj->get_Type() != Type){
		return E_HANDLE;
	}

	*pObj = tmpObj;
	return S_OK;
};

static HRESULT insert_object(bCapBase *pObj)
{
	HRESULT   hr = E_FAIL;
	int32_t   Handle;

	hr = find_open_address(&Handle);
	if(FAILED(hr)) return hr;

	map_ds.insert(map_server::value_type(Handle, pObj));

	return S_OK;
};

static HRESULT erase_object(int32_t Handle, int Type)
{
	HRESULT   hr;
	bCapBase  *pObj;

	hr = find_object(Handle, Type, &pObj);
	if(FAILED(hr)) return hr;

	map_ds.erase(Handle);
	delete pObj;

	return S_OK;
};

static HRESULT ServiceStart(VARIANT *vntArgs, int16_t Argc, VARIANT *vntRet){
	return S_OK;
};

static HRESULT ServiceStop(VARIANT *vntArgs, int16_t Argc, VARIANT *vntRet){
	return S_OK;
};

static HRESULT ControllerConnect(VARIANT *vntArgs, int16_t Argc, VARIANT *vntRet){
	HRESULT hr;
	VARIANT vntTmp;
	int32_t Handle;
	bCapController *pCtrl;

	VariantInit(&vntTmp);

	if(Argc < 1) return E_INVALIDARG;

	hr = find_open_address(&Handle);
	if(FAILED(hr)) goto exit_proc;

	hr = VariantChangeType(&vntTmp, vntArgs, 0, VT_BSTR);
	if(FAILED(hr)) goto exit_proc;

	pCtrl = new bCapController(vntTmp.bstrVal);
	insert_object(pCtrl);

	vntRet->vt = VT_I4;
	vntRet->lVal = Handle;

exit_proc:
	VariantClear(&vntTmp);
	return hr;
};

static HRESULT ControllerDisconnect(VARIANT *vntArgs, int16_t Argc, VARIANT *vntRet){
	HRESULT hr;
	VARIANT vntTmp;

	VariantInit(&vntTmp);

	if(Argc < 1) return E_INVALIDARG;

	hr = VariantChangeType(&vntTmp, vntArgs, 0, VT_I4);
	if(SUCCEEDED(hr)){
		hr = erase_object(vntTmp.lVal, TYPE_CTRL);
	}

	VariantClear(&vntTmp);

	return hr;
};

static HRESULT ControllerGetVariable(VARIANT *vntArgs, int16_t Argc, VARIANT *vntRet){
	HRESULT hr;
	VARIANT vntTmp, vntName, vntOpt;
	int32_t Handle;
	bCapController *pCtrl;
	bCapVariable *pVar;

	VariantInit(&vntTmp);
	VariantInit(&vntName);
	VariantInit(&vntOpt);

	if(Argc < 3) return E_INVALIDARG;

	hr = find_open_address(&Handle);
	if(FAILED(hr)) goto exit_proc;

	hr = VariantChangeType(&vntTmp, &vntArgs[0], 0, VT_I4);
	if(FAILED(hr)) goto exit_proc;

	hr = find_object(vntTmp.lVal, TYPE_CTRL, (bCapBase **)&pCtrl);
	if(FAILED(hr)) goto exit_proc;

	hr = VariantChangeType(&vntName, &vntArgs[1], 0, VT_BSTR);
	if(FAILED(hr)) goto exit_proc;

	hr = VariantChangeType(&vntOpt, &vntArgs[2], 0, VT_BSTR);
	if(FAILED(hr)) goto exit_proc;

	hr = pCtrl->AddVariable(vntName.bstrVal, vntOpt.bstrVal, &pVar);
	insert_object(pVar);

	vntRet->vt = VT_I4;
	vntRet->lVal = Handle;

exit_proc:
	VariantClear(&vntTmp);
	VariantClear(&vntName);
	VariantClear(&vntOpt);
	return hr;
};

static HRESULT ControllerExecute(VARIANT *vntArgs, int16_t Argc, VARIANT *vntRet){
	HRESULT hr;
	VARIANT vntTmp, vntName, vntOpt;
	bCapController *pCtrl;

	VariantInit(&vntTmp);
	VariantInit(&vntName);
	VariantInit(&vntOpt);

	if(Argc < 3) return E_INVALIDARG;

	hr = VariantChangeType(&vntTmp, &vntArgs[0], 0, VT_I4);
	if(FAILED(hr)) goto exit_proc;

	hr = find_object(vntTmp.lVal, TYPE_CTRL, (bCapBase **)&pCtrl);
	if(FAILED(hr)) goto exit_proc;

	hr = VariantChangeType(&vntName, &vntArgs[1], 0, VT_BSTR);
	if(FAILED(hr)) goto exit_proc;

	hr = VariantCopy(&vntOpt, &vntArgs[2]);
	if(FAILED(hr)) goto exit_proc;

	hr = pCtrl->Execute(vntName.bstrVal, vntOpt, vntRet);

exit_proc:
	VariantClear(&vntTmp);
	VariantClear(&vntName);
	VariantClear(&vntOpt);
	return hr;
};

static HRESULT VariableGetValue(VARIANT *vntArgs, int16_t Argc, VARIANT *vntRet){
	HRESULT hr;
	VARIANT vntTmp;
	bCapVariable *pVar;

	VariantInit(&vntTmp);

	if(Argc < 1) return E_INVALIDARG;

	hr = VariantChangeType(&vntTmp, vntArgs, 0, VT_I4);
	if(FAILED(hr)) goto exit_proc;

	hr = find_object(vntTmp.lVal, TYPE_CTRL_VAR, (bCapBase **)&pVar);
	if(FAILED(hr)) goto exit_proc;

	hr = pVar->get_Value(vntRet);

exit_proc:
	VariantClear(&vntTmp);
	return hr;
};

static HRESULT VariablePutValue(VARIANT *vntArgs, int16_t Argc, VARIANT *vntRet){
	HRESULT hr;
	VARIANT vntTmp;
	bCapVariable *pVar;

	VariantInit(&vntTmp);

	if(Argc < 2) return E_INVALIDARG;

	hr = VariantChangeType(&vntTmp, &vntArgs[0], 0, VT_I4);
	if(FAILED(hr)) goto exit_proc;

	hr = find_object(vntTmp.lVal, TYPE_CTRL_VAR, (bCapBase **)&pVar);
	if(FAILED(hr)) goto exit_proc;

	hr = pVar->put_Value(vntArgs[1]);

exit_proc:
	VariantClear(&vntTmp);
	return hr;
};

static HRESULT VariableRelease(VARIANT *vntArgs, int16_t Argc, VARIANT *vntRet){
	HRESULT hr;
	VARIANT vntTmp;

	VariantInit(&vntTmp);

	if(Argc < 1) return E_INVALIDARG;

	hr = VariantChangeType(&vntTmp, vntArgs, 0, VT_I4);
	if(SUCCEEDED(hr)){
		hr = erase_object(vntTmp.lVal, TYPE_CTRL_VAR);
	}

	VariantClear(&vntTmp);

	return hr;
};

static void SetCallFunctions()
{
	bCap_SetCallFunc(ID_SERVICE_START, &ServiceStart);
	bCap_SetCallFunc(ID_SERVICE_STOP, &ServiceStop);
	bCap_SetCallFunc(ID_CONTROLLER_CONNECT, &ControllerConnect);
	bCap_SetCallFunc(ID_CONTROLLER_DISCONNECT, &ControllerDisconnect);
	bCap_SetCallFunc(ID_CONTROLLER_GETVARIABLE, &ControllerGetVariable);
	bCap_SetCallFunc(ID_CONTROLLER_EXECUTE, &ControllerExecute);
	bCap_SetCallFunc(ID_VARIABLE_GETVALUE, &VariableGetValue);
	bCap_SetCallFunc(ID_VARIABLE_PUTVALUE, &VariablePutValue);
	bCap_SetCallFunc(ID_VARIABLE_RELEASE, &VariableRelease);
};

int main(void){
	int fd;
	char chTmp;
	HRESULT hr;

	/* Sets callback functions */
	SetCallFunctions();

	/* Starts receive thread */
	hr = bCap_Open_Server("tcp", 500, &fd);
	if(FAILED(hr)){
		cout << std::hex << hr << ": Failed to start receive thread." << endl;
		return -1;
	}

	cin >> chTmp;

	bCap_Close_Server(&fd);

	return 0;
}
