// nikonD3Client.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#pragma comment(lib, "Ws2_32.lib")

using namespace std;

#define DEFAULT_BUFFER_LENGTH	512


class Client {
public:
	Client(char* servername, char * serverport)
	{
		szServerName = servername;
		szServerPort = serverport;
		ConnectSocket = INVALID_SOCKET;
	}

	bool Start() {
		WSADATA wsaData;

		// Initialize Winsock
		int iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
		if(iResult != 0)
		{
			printf("WSAStartup failed: %d\n", iResult);
			return false;
		}

		struct addrinfo	*result = NULL,
			*ptr = NULL,
			hints;

		ZeroMemory(&hints, sizeof(hints));
		hints.ai_family = AF_UNSPEC;		
		hints.ai_socktype = SOCK_STREAM;	
		hints.ai_protocol = IPPROTO_TCP;

		// Resolve the server address and port
		iResult = getaddrinfo(szServerName, szServerPort, &hints, &result);
		if (iResult != 0)
		{
			printf("getaddrinfo failed: %d\n", iResult);
			WSACleanup();
			return false;
		}

		ptr = result;

		// Create a SOCKET for connecting to server
		ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);

		if (ConnectSocket == INVALID_SOCKET)
		{
			printf("Error at socket(): %d\n", WSAGetLastError());
			freeaddrinfo(result);
			WSACleanup();
			return false;
		}

		// Connect to server
		iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);

		if (iResult == SOCKET_ERROR)
		{
			closesocket(ConnectSocket);
			ConnectSocket = INVALID_SOCKET;
		}

		freeaddrinfo(result);

		if (ConnectSocket == INVALID_SOCKET)
		{
			printf("Unable to connect to server!\n");
			WSACleanup();
			return false;
		}

		return true;
	};

	// Free the resouces
	void Stop() {
		int iResult = shutdown(ConnectSocket, SD_SEND);

		if (iResult == SOCKET_ERROR)
		{
			printf("shutdown failed: %d\n", WSAGetLastError());
		}

		closesocket(ConnectSocket);
		WSACleanup();
	};

	// Send message to server
	bool Send(char* szMsg, int szLen)
	{

		int iResult = send(ConnectSocket, szMsg, szLen, 0);

		if (iResult == SOCKET_ERROR)
		{
			printf("send failed: %d\n", WSAGetLastError());
			Stop();
			return false;
		}

		return true;
	};

	// Receive message from server
	bool Recv(char * p_msg, int &len)
	{
		char recvbuf[DEFAULT_BUFFER_LENGTH];
		int iResult = recv(ConnectSocket, recvbuf, DEFAULT_BUFFER_LENGTH, 0);

		if (iResult > 0)
		{
			/*printf("RCV: ");
			for (int i=0; i<iResult; i++) {
				printf("%X ",recvbuf[i]);
			}
			printf("\n");*/

			//memset(p_msg, 0, DEFAULT_BUFFER_LENGTH);
			memcpy(p_msg, recvbuf, iResult);
			len = iResult; 

			return true;
		}

		return false;
	}


private:
	char* szServerName;
	char* szServerPort;
	SOCKET ConnectSocket;
};

class NikonClient {
public:
	NikonMessage  outmsg;
	bool		  bProgramRunning;

	NikonClient(void) {
		bProgramRunning = true;
		return;
	}
	void processNikonMessage(char* msgbuff) {
		const char * funcname = "processNikonMessage()";
		bool bMsgReady = false;
		bool bRet = false;
		long lValue;
		char sValue[NIKON_MESSAGE_STRING_LENGTH] = "\0";
		unsigned short replyMsg;
		//Potential replies or input
		//NikonMessageUARTType sUart;

		//Recast the message into a header and move the point into the message class
		NikonMessage nikonmsg;
		NikonMessageHeaderType * pHeader = (NikonMessageHeaderType *) (msgbuff);


		if (nikonmsg.SetHeader(pHeader)) {					
			printf("Got Message this long: %i\n",nikonmsg.MessageSize());
			replyMsg = nikonmsg.NikonCameraMessage();
			switch (nikonmsg.NikonCameraMessage()) {

			case NIKON_MESSAGE_NONE:		// Null message used to test communication
				if (nikonmsg.NikonCommand()==NIKON_COMMAND_GET || nikonmsg.NikonCommand()==NIKON_COMMAND_SET ){
					fprintf(stdout,"%s: received NIKON_MESSAGE_NONE\n",funcname);
					//	nmReply.SetMessage(NIKON_MESSAGE_NONE);
					bMsgReady = true;
				}
				break;

			case NIKON_MESSAGE_SYSTEM_RESET:	// Set to have server reload default config: this will stop acq
				////Do nothing on a get
				//if (nikonmsg.NikonCommand() == NIKON_COMMAND_SET || nikonmsg.NikonCommand() == NIKON_COMMAND_GET) {
				//	//TODO 
				////	nmReply.SetMessage(NIKON_MESSAGE_SYSTEM_RESET,NIKON_COMMAND_GET);
				//	bMsgReady = true;
				//}
				break;
			case NIKON_MESSAGE_CONNECT:			// Attempt to connect (1) or disconnect (0) the PTP connection to the dsc (NikonMessageLongType)
				if ((long) nikonmsg == 1 ) {
					fprintf(stdout,"%s: received NIKON_MESSAGE_CONNECT // Camera Connected.\n",funcname);
				}
				else if ((long) nikonmsg == 0 ) {
					fprintf(stdout,"%s: received NIKON_MESSAGE_CONNECT // Camera NOT Connected.\n",funcname);
				}

				break;
			case NIKON_MESSAGE_ACQUISITION:		
				if ((long) nikonmsg == 1 ) {
					fprintf(stdout,"%s: received NIKON_MESSAGE_ACQUISITION // Camera Logging.\n",funcname);
				}
				else if ((long) nikonmsg == 0 ) {
					fprintf(stdout,"%s: received NIKON_MESSAGE_ACQUISITION // Camera NOT Logging.\n",funcname);
				}

				break;

			case NIKON_MESSAGE_LASERPOWER:
				if ((long)nikonmsg == 1) {
					fprintf(stdout, "%s: received NIKON_MESSAGE_LASERPOWER // Lasers on.\n", funcname);
				}
				else if ((long)nikonmsg == 0) {
					fprintf(stdout, "%s: received NIKON_MESSAGE_LASERPOWER // Lasers off.\n", funcname);
				}

				break;


			case NIKON_MESSAGE_CONFIGFILE:		
				// The filename of the configuration for the server (NikonMessageStringType)
				// TODO implement file change
				/*if (nikonmsg.NikonCommand() == NIKON_COMMAND_SET || nikonmsg.NikonCommand() == NIKON_COMMAND_GET) {
				strcpy(sValue,(char*)sConfigFile);
				nmReply.SetMessage(NIKON_MESSAGE_CONFIGFILE,NIKON_COMMAND_GET,(unsigned char *) sValue, (unsigned long) NIKON_MESSAGE_STRING_LENGTH+1);
				bMsgReady = true;
				}*/
				break;
				/* TODO
				case NIKON_MESSAGE_TIME:			
				// Get the time of the clock on the server: set forces a sync with the camera
				if (nikonmsg.NikonCommand()==NIKON_COMMAND_GET){
				}
				if (nikonmsg.NikonCommand() == NIKON_COMMAND_SET || nikonmsg.NikonCommand() == NIKON_COMMAND_GET) {

				}
				*/
				break;

			case NIKON_MESSAGE_INTERVAL:		
				// The interval on which to trigger frames in milliseconds (NikonMessageLongType)
				lValue = (long) nikonmsg;
				fprintf(stdout,"%s: received NIKON_MESSAGE_INTERVAL // Period is %i ms.\n",funcname, lValue);

				break;

			case NIKON_MESSAGE_FILEPREFIX:		
				// Get/set the file prefix for image acquisition (NikonMessageStringType)

				//if (nikonmsg.NikonCommand()==NIKON_COMMAND_GET || nikonmsg.NikonCommand()==NIKON_COMMAND_SET){
				//	strcpy(sValue,sNikonServerData.cameraFilePrefix);
				//	nmReply.SetMessage(NIKON_MESSAGE_FILEPREFIX,NIKON_COMMAND_GET,(unsigned char *) sValue, (unsigned long) NIKON_MESSAGE_STRING_LENGTH+1);
				//	bMsgReady = true;
				//}

				break;
			case NIKON_MESSAGE_STATUS:			
				// Get NikonMessageStatusType of 
				if (nikonmsg.NikonCommand()==NIKON_COMMAND_GET){
					// recast into a status message

				}
				break;

			case NIKON_MESSAGE_IMAGECOUNT:		
				// Get the current frame number (NikonMessageLongType)
				lValue = (long) nikonmsg;
				fprintf(stdout,"%s: received NIKON_MESSAGE_IMAGECOUNT // Count is at %i.\n",funcname, lValue);
				break;
			case NIKON_MESSAGE_TRIGGERCOUNT:		
				// Get the current trigger number (NikonMessageLongType)
				lValue = (long) nikonmsg;
				fprintf(stdout,"%s: received NIKON_MESSAGE_TRIGGERCOUNT // Count is at %i.\n",funcname, lValue);
				break;
				// TRIGGER SERIAL PORT OPTIONS
			case NIKON_MESSAGE_TRIGGERCONFIG:
				//if (nikonmsg.NikonCommand()==NIKON_COMMAND_GET || nikonmsg.NikonCommand()==NIKON_COMMAND_SET){
				//	// Populate response
				//	strcpy((char *) sUart.name,(char *) sNikonServerData.triggerPortName);
				//	sUart.baudRate = sNikonServerData.triggerBaudRate;
				//	sUart.bits = sNikonServerData.triggerBits;
				//	sUart.parity = (unsigned char) sNikonServerData.triggerParity[0];
				//	sUart.stop = sNikonServerData.triggerStop;
				//	//Stuff Message
				//	nmReply.SetMessage(NIKON_MESSAGE_TRIGGERCONFIG,NIKON_COMMAND_GET,(unsigned char *)&sUart, (unsigned long) sizeof(NikonMessageUARTType));
				//	bMsgReady = true;
				//}

				break;
			default: 

				break;
			}

		}
		else {
			fprintf(stdout, "%s: ERROR, Malformed message received.\n",funcname);
		}

	};
	bool handleInput(string msg) {
		bool bMsgSet		= false;
		long lValue;

		if (msg.compare(0,1,"n") == 0) {
			printf("Sending Message NONE\n");
			outmsg.SetMessage(NIKON_MESSAGE_NONE);
			bMsgSet = true;
		}
		else if (msg.compare(0,1,"c") == 0)
		{	
			printf("Sending Message CONNECT\n");

			if (msg.length() ==1) {
				outmsg.SetMessage(NIKON_MESSAGE_CONNECT);
				bMsgSet = true;
			}
			else if (msg.length() == 2) {
				switch (msg[1]) {
				case '1': //connect camera
					lValue = 1;
					outmsg.SetMessage(NIKON_MESSAGE_CONNECT,NIKON_COMMAND_SET,(unsigned char *) &lValue, (unsigned long) sizeof(lValue));
					bMsgSet = true;
					break;
				case '0': //disconnect camera
					lValue = 0;
					outmsg.SetMessage(NIKON_MESSAGE_CONNECT,NIKON_COMMAND_SET,(unsigned char *) &lValue, (unsigned long) sizeof(lValue));
					bMsgSet = true;
					break;
				default:
					printf("invalid input\n");
					break;
				}
			}
		}
		else if (msg.compare(0,1,"a") == 0)
		{	
			printf("Sending Message ACQUISITION\n");

			if (msg.length() ==1) {
				outmsg.SetMessage(NIKON_MESSAGE_ACQUISITION);
				bMsgSet = true;
			}
			else if (msg.length() == 2) {
				switch (msg[1]) {
				case '1': //start acq
					lValue = 1;
					outmsg.SetMessage(NIKON_MESSAGE_ACQUISITION,NIKON_COMMAND_SET,(unsigned char *) &lValue, (unsigned long) sizeof(lValue));
					bMsgSet = true;
					break;
				case '0': //stop acq
					lValue = 0;
					outmsg.SetMessage(NIKON_MESSAGE_ACQUISITION,NIKON_COMMAND_SET,(unsigned char *) &lValue, (unsigned long) sizeof(lValue));
					bMsgSet = true;
					break;
				default:
					printf("invalid input\n");
					break;
				}
			}
		}
		else if (msg.compare(0, 1, "l") == 0)
		{
			printf("Sending Message LASERPOWER\n");

			if (msg.length() == 1) {
				outmsg.SetMessage(NIKON_MESSAGE_LASERPOWER);
				bMsgSet = true;
			}
			else if (msg.length() == 2) {
				switch (msg[1]) {
				case '1': //laser on
					lValue = 1;
					outmsg.SetMessage(NIKON_MESSAGE_LASERPOWER, NIKON_COMMAND_SET, (unsigned char *)&lValue, (unsigned long) sizeof(lValue));
					bMsgSet = true;
					break;
				case '0': //laser off
					lValue = 0;
					outmsg.SetMessage(NIKON_MESSAGE_LASERPOWER, NIKON_COMMAND_SET, (unsigned char *)&lValue, (unsigned long) sizeof(lValue));
					bMsgSet = true;
					break;
				default:
					printf("invalid input\n");
					break;
				}
			}
		}
		else if (msg.compare(0, 1, "i") == 0)
		{	
			printf("Sending Message INTERVAL\n");

			if (msg.length() ==1) {
				outmsg.SetMessage(NIKON_MESSAGE_INTERVAL);
				bMsgSet = true;
			}
			else if (msg.length() >= 2) {
				lValue = (long)atoi(msg.c_str() +1);
				if (lValue >= 200){
					outmsg.SetMessage(NIKON_MESSAGE_INTERVAL,NIKON_COMMAND_SET,(unsigned char *) &lValue, (unsigned long) sizeof(lValue));
					bMsgSet = true;
				}
				else printf("invalid interval given, must be 1000 or greater.");
			}
		}
		else if (msg.compare(0,2,"fc") == 0)
		{	
			printf("Sending Message IMAGECOUNT\n");
			outmsg.SetMessage(NIKON_MESSAGE_IMAGECOUNT);
			bMsgSet = true;
		}
		else if (msg.compare(0,2,"tc") == 0)
		{	
			printf("Sending Message TRIGGERCOUNT\n");
			outmsg.SetMessage(NIKON_MESSAGE_TRIGGERCOUNT);
			bMsgSet = true;
		}
		else if (msg.compare(0,1,"x") == 0)
		{
			bProgramRunning = false;
		}
		else {
			printf("Unknown Command.\n");
			print_commands();
		}

		return bMsgSet;
	};
	void print_usage() {
		fprintf (stdout, 
			"nikonD3Client.exe [serverip] [serverport]					\n"
			"-------------------------------------------				\n"
			"This client code does single message interaction with		\n"
			"the tcp server running autonomous acquisition code for a	\n"
			"Nikon D3 camera.											\n"
			"(C) 2013 MBARI												\n");					

	};

	void print_commands() {
		fprintf(stdout,
			"COMMANDS\n"
			"--------------------------------------	\n"
			"n		- Send NONE message to test comms	\n"
			"c[0,1] - Tell System to Connect		\n"
			"a[0,1] - start acquisition				\n"
			"l[0,1] - turn off/on lasers            \n"		
			"i[X]   - set interval to X milliseconds\n"
			"tc     - retrieve trigger count		\n"
			"fc     - retrieve frame count			\n"
			"s - Get all status						\n"
			"x - Exit program						\n"
			"--------------------------------------	\n"
			);
	};
private:

};


int _tmain(int argc, _TCHAR* argv[])
{
	string msg;
	char rcvmsg[DEFAULT_BUFFER_LENGTH];
	int  rcvlen;
	char servername[32] = "127.0.0.1";
	char serverport[8]  = "2020";

	NikonClient nclient;
	bool bMsgSet = false;




	if (argc == 3) {
		strcpy_s(servername, argv[1]);
		strcpy_s(serverport, argv[2]);
	}
	else {
		fprintf(stdout,"Not enough input arguments (%i)\n",argc);
		nclient.print_usage();
	}

	Client client(servername,serverport);



	if (!client.Start()) {
		fprintf(stdout, "main(): ERROR Cannot connect to server at %s:%s\n",servername,serverport);
		getchar();
		return 1;
	}

	while(true)
	{
		bMsgSet = false;
		printf(":> ");

		std::getline(std::cin, msg);

		bMsgSet = nclient.handleInput(msg);

		if (bMsgSet) {
			if (!client.Send((char *) nclient.outmsg.Message(),nclient.outmsg.MessageSize())) {
				printf("failed to send\n");
				continue;
			}

			printf("waiting for reply...\n");
			if (client.Recv(rcvmsg,rcvlen) && rcvlen >0) {
				nclient.processNikonMessage(rcvmsg);
			}
			else {
				printf ("failed to receive.\n");
				continue;
			}
		}

		if (!nclient.bProgramRunning) 
			break;

	}

	client.Stop();

	printf("Client Connection Closed, Press Any Key to Exit...\n");

	return 0;
}





