//********************************************************************************************************************
//  File Name: NetComClient.h
//  Copyright 1998..2014 @ Neuralynx, Inc.
//********************************************************************************************************************
#pragma once
#include <vector>
#include <string>

//These forward declares allow us to only distribute this
//header file, instead of the entire header tree.  The
//necessary header files are included in the cpp file
class NetworkNodeClient;
namespace NlxDataTypes {
	struct SERec;
	struct STRec;
	struct TTRec;
	struct CRRec;
	struct EventRec;
	struct VideoRec;
}

namespace NlxNetCom {

	class NetComClient
	{
	public:
		__declspec(dllexport) NetComClient(void);
		__declspec(dllexport) virtual ~NetComClient(void);

		//client functions
		__declspec(dllexport) bool ConnectToServer(const wchar_t* const serverName, bool attemptRouterConnection = true);
		__declspec(dllexport) bool DisconnectFromServer();
		__declspec(dllexport) std::wstring GetClientVersionString() 
		{
			std::wstring clientVersionString(L"");
			wchar_t* pClientVersionString = NULL;

			GetClientVersionStringInternal(pClientVersionString);

			clientVersionString = pClientVersionString;

			FreeArray(pClientVersionString);

			return clientVersionString;
		}
		__declspec(dllexport) bool OpenStream(const wchar_t* const DASObjectName);
		__declspec(dllexport) bool CloseStream(const wchar_t* const DASObjectName);
		__declspec(dllexport) bool SendCommand(const wchar_t* const command, char*& reply, int& numBytesReturned);

		__declspec(dllexport) void SetCallbackFunctionMessage( void (*myFunctionPtr)(void* myClassPtr, int messageType, wchar_t* message), void* myClassPtr);
		__declspec(dllexport) void SetCallbackFunctionConnectionLost( void (*myFunctionPtr)(void* myClassPtr), void* myClassPtr);

		__declspec(dllexport) void SetCallbackFunctionSE( void (*myFunctionPtr)(void* myClassPtr, NlxDataTypes::SERec* records, int numRecords, const wchar_t* const objectName), void* myClassPtr);
		__declspec(dllexport) void SetCallbackFunctionST( void (*myFunctionPtr)(void* myClassPtr, NlxDataTypes::STRec* records, int numRecords, const wchar_t* const objectName), void* myClassPtr);
		__declspec(dllexport) void SetCallbackFunctionTT( void (*myFunctionPtr)(void* myClassPtr, NlxDataTypes::TTRec* records, int numRecords, const wchar_t* const objectName), void* myClassPtr);
		__declspec(dllexport) void SetCallbackFunctionCSC( void (*myFunctionPtr)(void* myClassPtr,NlxDataTypes::CRRec* records, int numRecords, const wchar_t objectName []), void* myClassPtr);
		__declspec(dllexport) void SetCallbackFunctionEV( void (*myFunctionPtr)(void* myClassPtr, NlxDataTypes::EventRec* records, int numRecords, const wchar_t* const objectName), void* myClassPtr);
		__declspec(dllexport) void SetCallbackFunctionVT( void (*myFunctionPtr)(void* myClassPtr, NlxDataTypes::VideoRec* records, int numRecords, const wchar_t* const objectName), void* myClassPtr);
		//setter functions
		__declspec(dllexport) bool SetApplicationName(const wchar_t* const myApplicationName);
		__declspec(dllexport) bool SetLogFileName(const wchar_t* const filename);

		//getter functions
		__declspec(dllexport) bool GetRingBufferSize(const wchar_t* const objectName, int& numRecords, int& recordSize);
		__declspec(dllexport) bool GetRingBufferData(const wchar_t* const objectName, char* dataBuffer, int numBytes, int& numBytesReturned);

		//getter status functions

		__declspec(dllexport) bool AreWeConnected();

		//C++ STL wrappers for C API functions
		//This saves having to worry about memory management and the DLL
		//boundary in an application linking against this library.
		//Since STL/CRT objects cannot pass the DLL boundary (the STL/CRT	
		//versions may differ), these functions must be implemented in
		//the header so that the application that links to this library
		//compiles them using its version of the STL/CRT.

		//*******************************************************************
		bool GetDASObjectsAndTypes(std::vector<std::wstring>& DASObjects, std::vector<std::wstring>& DASTypes)
		{
			DASObjects.clear();
			DASTypes.clear();

			wchar_t** pDASObjects = NULL;
			wchar_t** pDASTypes = NULL;
			int numObjectsReturned = -1;

			if(GetDASObjectsAndTypesInternal(pDASObjects, pDASTypes, numObjectsReturned)) {
				for(int i = 0; i < numObjectsReturned; ++i) {
					DASObjects.push_back(std::wstring(pDASObjects[i]));
					FreeArray((void*)(pDASObjects[i]));
					DASTypes.push_back(std::wstring(pDASTypes[i]));
					FreeArray((void*)(pDASTypes[i]));
				}

				FreeArray(pDASObjects);
				FreeArray(pDASTypes);
				return true;
			} else {
				return false;
			}
		}

		//*******************************************************************
		bool SendCommand(const wchar_t command [], std::wstring& reply)
		{
			wchar_t* pReply = NULL;
			reply = L"";

			if(SendCommandInternal(command, pReply) == true) {
				reply = pReply;
				FreeArray(pReply);
				return true;
			} else {
				return false;
			}
		}

		//*****************************************************************************
		bool SendCommand (const wchar_t* const command, int& commandSucceeded, std::vector<std::wstring>& replyValues)
		{
			wchar_t** pReply = NULL;
			int numReplyStringsReturned = 0;
			commandSucceeded = -1;
			replyValues.clear();

			if(SendCommandInternal(command, commandSucceeded, pReply, numReplyStringsReturned)) {
				for(int i = 0; i < numReplyStringsReturned; ++i) {
					replyValues.push_back(std::wstring(pReply[i]));
					FreeArray((void*)(pReply[i]));
				}

				FreeArray(pReply);
				return true;
			} else {
				return false;
			}

		}

		//*******************************************************************
		std::wstring GetServerPCName(void)
		{
			std::wstring pcName(L"");
			wchar_t* pPCName = NULL;

			GetServerPCNameInternal(pPCName);

			pcName = pPCName;

			FreeArray(pPCName);

			return pcName;
		}

		//*******************************************************************
		std::wstring GetServerIPAddress(void)
		{
			std::wstring ipAddress(L"");
			wchar_t* pIPAddress = NULL;

			GetServerIPAddressInternal(pIPAddress);

			ipAddress = pIPAddress;

			FreeArray(pIPAddress);

			return ipAddress;
		}

		//*******************************************************************
		std::wstring GetServerApplicationName(void)
		{
			std::wstring appName(L"");
			wchar_t* pAppName = NULL;

			GetServerApplicationNameInternal(pAppName);

			appName = pAppName;

			FreeArray(pAppName);

			return appName;
		}

	private:

		bool SetComputerNameOnServer();

		//C API general functions
		__declspec(dllexport) void GetClientVersionStringInternal(wchar_t*& clientVersionString);

		//C API DAS AE functions
		__declspec(dllexport) bool GetDASObjectsAndTypesInternal(wchar_t**& DASObjects, wchar_t**& DASTypes, int& numObjectsReturned);

		//C Command functions
		__declspec(dllexport) bool SendCommandInternal(const wchar_t command [], wchar_t*& reply);
		__declspec(dllexport) bool SendCommandInternal(const wchar_t* const command, int& commandSucceeded, wchar_t**& pReplyStrings, int& numReplyStringsReturned);

		//C API server information functions
		__declspec(dllexport) void GetServerApplicationNameInternal(wchar_t*& appName);
		__declspec(dllexport) void GetServerPCNameInternal(wchar_t*& pcName);
		__declspec(dllexport) void GetServerIPAddressInternal(wchar_t*& ipAddress);

		//C API helper functions
		__declspec(dllexport) void FreeArray(void* arrayPtr);


		NetworkNodeClient* mNetworkConnection; //implementation object
		std::wstring mPrevServerIPAddress;
	};

	//NetComClient factory methods so that we can allocate and deallocate
	//pointers to NetComClient objects on the DLL's heap. If a NetComClient*
	//is newed in some application, the memory for the NetComClient is in
	//the heap of that other application. However, anything newed by NetComClient
	//is in the heap of this DLL. So when ~NetComClient is called from the other
	//application, we get a heap corruption because we can't delete things in
	//this DLL's heap from the other application.
	//TODO: it might be a good idea to make the NetComClient class a factory with
	//private constructor.
	__declspec(dllexport) NetComClient* GetNewNetComClient(void);
	__declspec(dllexport) void DeleteNetComClient(NetComClient*);
}