#include <stdio.h>
#include <io.h>
#include <windows.h>
#include <stdarg.h>
#include <crtdbg.h>
#include <time.h>

#include "PRX.h"
#include "PRXInternal.h"
#include "PRXSonatestSerialMuxInternal.h"
#include "PRXStrings.h"
#include "RQProtected.h"

HANDLE comPortHandle;
unsigned long voltageClamp = 500;
unsigned long verNo1, verNo2, verNo3;

// This should probably all be moved to the HLPRX following IW integration.
NDTS_Boolean PRX_InitialiseSonatestSerialMuxInternal(int iniFileValue)
{
	PRX_State *pRX_State = &pRX_StateArray[0];
	// One PRX Card to store the state
	_ASSERTE(prxs_found == 1);
	if (prxs_found != 1)
		return NDTS_BFalse;

	if (!PRX_InitialiseSonatestSerialPort())
		return NDTS_BFalse;
	if (!PRX_SonatestSerialPortGetVersionNumber())
		return NDTS_BFalse;

	pRX_State->useSonatestMux = NDTS_BTrue;
	pRX_State->sonatestSerialMuxMode = iniFileValue;

	// Reset the sequencer and we should stay in sync from here on 
	if (verNo1 == 0 && verNo2 == 1 && verNo3 == 0)
	{
		// Older firmware doesnt have a /seq res command so need to reboot the mux each time
		PRX_SendCommandToSonatestSerialPort("/sys reboot");
	}
	else
	{
		PRX_SendCommandToSonatestSerialPort("/seq res");
	}

	// Set the mux to external trigger
//	if (!PRX_SendCommandToSonatestSerialPort("/pul trigger prf"))
	if (!PRX_SendCommandToSonatestSerialPort("/pul trigger ext"))
		return NDTS_BFalse;

	if (!PRX_SendCommandToSonatestSerialPort("/pul enable 1 2 3 4 5 6 7 8"))
		return NDTS_BFalse;

	return NDTS_BTrue;
}

// Daniel Feb 07 - for Jason Morgan's Sonatest serial Mux.  
NDTS_Boolean PRX_InitialiseSonatestSerialPort()
{
   DCB dcb;
   COMMTIMEOUTS timeouts;

//   comPortHandle = CreateFile("\\\\.\\CNCA0",GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL);
   comPortHandle = CreateFile("COM1",GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL);
//   comPortHandle = CreateFile("COM5",GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL);
   _ASSERTE((int)comPortHandle != -1);
   if ((int)comPortHandle == -1)
   {
		NDTS_LogLine(NDTS_LLCrit, "Sonatest MUX failed to open COM port");
		return NDTS_BFalse;
   }


   GetCommState(comPortHandle, &dcb);
   dcb.BaudRate = CBR_19200;     // set the baud rate
   dcb.ByteSize = 8;             // data size, xmit, and rcv
   dcb.Parity = NOPARITY;        // no parity bit
   dcb.StopBits = ONESTOPBIT;    // one stop bit
   SetCommState(comPortHandle, &dcb);
   
   // Read timeouts
   GetCommTimeouts(comPortHandle, &timeouts);
   timeouts.ReadIntervalTimeout = 1;
   timeouts.ReadTotalTimeoutConstant = 0;
   timeouts.ReadTotalTimeoutMultiplier = 1;
   SetCommTimeouts(comPortHandle, &timeouts);

   return NDTS_BTrue;
}

void PRX_SendStringToSonatestSerialPort(char *string)
{
   DWORD      dw; 
   WriteFile(comPortHandle , string, strlen(string), &dw, NULL); 
   WriteFile(comPortHandle , "\r", 1, &dw, NULL); 
   NDTS_LogLine(NDTS_LLDebug, "Serial to: %s", string);
}

#define SEND_STRING_BUFFER_SIZE 500
#define RECEIVE_STRING_BUFFER_SIZE 1000
NDTS_Boolean PRX_SendCommandToSonatestSerialPort(const char *msg, ...)
{
	int retries = 0;
	va_list arg;
	char string[SEND_STRING_BUFFER_SIZE + 3];
	va_start(arg,msg);
	_vsnprintf(string, SEND_STRING_BUFFER_SIZE, msg, arg);
	va_end(arg);

	PRX_SendStringToSonatestSerialPort(string);

	if (pRX_StateArray[0].sonatestSerialMuxMode != 2)
		return NDTS_BTrue;

	for (retries = 0; retries < 5; retries++)
	{
		int startTime = 0;
		char response[RECEIVE_STRING_BUFFER_SIZE + 2];  
		unsigned long responseBytes = 0;
		unsigned long bytesSoFar = 0;
		NDTS_Boolean badResponse = NDTS_BFalse;
		memset(response, 0, RECEIVE_STRING_BUFFER_SIZE);
   
		startTime = clock();
		do 
		{
			ReadFile(comPortHandle, &(response[bytesSoFar]), RECEIVE_STRING_BUFFER_SIZE - bytesSoFar, &responseBytes, NULL);
			bytesSoFar += responseBytes;
			// When rebooting, get more text than the buffer size.  Scroll the buffer half a buffer at a time.  This shouldn't lose anything vital
			if (bytesSoFar >= RECEIVE_STRING_BUFFER_SIZE)
			{
				int halfBuffer = RECEIVE_STRING_BUFFER_SIZE / 2;
				memcpy(&response[0], &response[halfBuffer], halfBuffer);
				memset(&response[halfBuffer], 0, halfBuffer-1);
				bytesSoFar = halfBuffer;
			}

			if (responseBytes != 0)
			{
				NDTS_LogLine(NDTS_LLDebug, "Time %d Serial from: %s", (clock() - startTime) / CLOCKS_PER_SEC , response);
				if (strstr(response, "Bad Value") || strstr(response, "no match:"))
				{
					NDTS_LogLine(NDTS_LLErr, "Bad response from sending %s.  Retries %d", string, retries);
					badResponse = NDTS_BTrue;
				}
				if (!badResponse && strstr(response, "PULSERMUX"))
				{
					// Success
					return NDTS_BTrue;
				}
			}
		}
		// The HT command takes the longest time - typically 6 seconds
		// Rebooting takes nearly 20 seconds.  Maybe should do something special for rebooting
		while ((clock() - startTime) / CLOCKS_PER_SEC < 25 && !badResponse);
		if (!badResponse)
		{
			// I think the COM port can go wrong when the Mux is rebooted - this helps it recover
			CloseHandle(comPortHandle);
			PRX_InitialiseSonatestSerialPort();
			NDTS_LogLine(NDTS_LLErr, "Timeout waiting for response from MUX to command %s.  Retries %d", string, retries);
		}
		// Send the string again because either not got the response or got an error response
		PRX_SendStringToSonatestSerialPort(string);
	}
	NDTS_LogLine(NDTS_LLErr, "Tried %d times to send command %s to MUX - giving up", retries, string);

	return NDTS_BFalse;
}

void PRX_SonatestMuxSetNumberOfSequenceSteps(unsigned long nSteps)
{
	PRX_SendCommandToSonatestSerialPort("/seq maxstep %d", nSteps);

	// Turn on / off the HV for newly enabled steps
	PRX_SonatestMuxSetHVEnable(pRX_StateArray[0].reg_shadow.globalRegs.highVoltageEnable);

}

void PRX_SonatestMuxSetHVEnable(unsigned long flag)
{
	char state[5]; // on or off
	PRX_State *pRX_State = &pRX_StateArray[0];
	unsigned long step;
	static unsigned long shadowValue = 999;

	// HT is very slow so only send if it really has changed
	if (shadowValue == flag)
		return;
	shadowValue = flag;

	// Assume card 0 is linked to Mux  - Sonatest MUX always linked to one PRX card.

	if (flag)
		sprintf(state, "on");
	else
		sprintf(state, "off");

	PRX_SendCommandToSonatestSerialPort("/pul ht %s", state);

	// Set the hv enable for each active step
	for (step = 1; step <= pRX_State->numberOfSequenceSteps; step++)
	{
		// Power not currently implemented PRX_SendCommandToSonatestSerialPort("Receiver power %d %s", step, state);
	}
}

void PRX_SonatestMuxSetHVLevel(unsigned long flag)
{
	unsigned long volts = (flag * 400 + 200) / 4095;

	static int fix = 0;
	if (fix)
	{
		PRX_SendCommandToSonatestSerialPort("/seq steps");
	}
	else
	{
		if (volts > voltageClamp)
		{
			NDTS_LogLine(NDTS_LLErr, "Sonatest MUX clamped voltage of %d to %d", volts, voltageClamp);
			volts = voltageClamp;
		}

		PRX_SendCommandToSonatestSerialPort("/pul ht set %d", volts);
	}
}

void PRX_SonatestMuxSetPulseEchoEnable(unsigned long flags)
{
	char state[10];
	unsigned long channel;
	static int test = 0;

	if (test)
	{
		PRX_SendCommandToSonatestSerialPort("/seq res");
		return;
	}

	for (channel = 1; channel <= 8; channel++)
	{
		if (flags >> (channel - 1) & 0x01)
			sprintf(state, "single");
		else
			sprintf(state, "double");

		PRX_SendCommandToSonatestSerialPort("/rec type %d %s", channel, state);
	}
}

void PRX_SonatestMuxSetReceiveChannel(unsigned long block, unsigned long channel)
{
	PRX_SendCommandToSonatestSerialPort("/seq step %d mixmask reset", block + 1);
	PRX_SendCommandToSonatestSerialPort("/seq step %d mixmask %d", block + 1, channel + 1);
}

#define TPE_COMMAND_BUF_SIZE 500
void PRX_SonatestMuxSetTransmitPulseEnable(unsigned long block, unsigned long flags)
{
	unsigned long channel;
	int bufPos = 0;
	char buf[TPE_COMMAND_BUF_SIZE + 3];

	PRX_SendCommandToSonatestSerialPort("/seq step %d trigmask reset", block + 1);

	// Build up a command like :- seq step 3 trigmask 1 6 7 to enable pulsers 1 6 and 7
	bufPos = _snprintf(buf, TPE_COMMAND_BUF_SIZE, "/seq step %d trigmask ", block + 1);
	for (channel = 1; channel <= 8; channel++)
	{
		if (flags >> (channel - 1) & 0x01)
			bufPos += _snprintf(&(buf[bufPos]), TPE_COMMAND_BUF_SIZE - bufPos, "%d ", channel);
	}
	PRX_SendCommandToSonatestSerialPort(buf);
}

void PRX_SonatestSerialMuxSetGainInternal(unsigned long oneBasedChannel, int gainIndB)
{
	PRX_SendCommandToSonatestSerialPort("receiver gain %d %d", oneBasedChannel, gainIndB);
}

void PRX_SonatestMuxSetPulseWidth(unsigned long block, unsigned long fiveNanoSecTicks)
{
	float usTicks = (float)fiveNanoSecTicks * 5.0f ; 
	PRX_SendCommandToSonatestSerialPort( "/pulser length %d %f", block + 1, usTicks);
}

void PRX_SonatestMuxSetInputImpedance(unsigned long block, unsigned long value)
{
	// value of 4 = 50 Ohms - set all other values to 200 Ohms
	int damping = (value == 4) ? 50 : 200;
// Damping not currently implemented on MUX	PRX_SendCommandToSonatestSerialPort("receiver damping %d %d", block + 1, damping);
	block;value;damping;

}
void PRX_SonatestSerialMuxSetFilterInternal(unsigned long oneBasedChannel, int filter)
{
	char option[20];

	if (filter) 
		sprintf(option, "enable");
	else
		sprintf(option, "disable");

	PRX_SendCommandToSonatestSerialPort("receiver filter %d %s", oneBasedChannel, option);
}

void PRX_SonatestSerialMuxSetVoltageClampInternal(unsigned long volts)
{
	voltageClamp = volts;
}

// Make sure the MUX is on line and get the three digit version number from the prompt
NDTS_Boolean PRX_SonatestSerialPortGetVersionNumber(const char *msg, ...)
{
	int retries = 0;
	va_list arg;
	char string[SEND_STRING_BUFFER_SIZE + 3];
	va_start(arg,msg);
	_vsnprintf(string, SEND_STRING_BUFFER_SIZE, msg, arg);
	va_end(arg);

	PRX_SendStringToSonatestSerialPort("  ");

	for (retries = 0; retries < 4; retries++)
	{
		int startTime = 0;
		char response[RECEIVE_STRING_BUFFER_SIZE + 2];  
		unsigned long responseBytes = 0;
		unsigned long bytesSoFar = 0;
		NDTS_Boolean badResponse = NDTS_BFalse;
		memset(response, 0, RECEIVE_STRING_BUFFER_SIZE);
   
		startTime = clock();
		do 
		{
			ReadFile(comPortHandle, &(response[bytesSoFar]), RECEIVE_STRING_BUFFER_SIZE - bytesSoFar, &responseBytes, NULL);
			bytesSoFar += responseBytes;
			// When rebooting, get more text than the buffer size.  Scroll the buffer half a buffer at a time.  This shouldn't lose anything vital
			if (bytesSoFar >= RECEIVE_STRING_BUFFER_SIZE)
			{
				int halfBuffer = RECEIVE_STRING_BUFFER_SIZE / 2;
				memcpy(&response[0], &response[halfBuffer], halfBuffer);
				memset(&response[halfBuffer], 0, halfBuffer-1);
				bytesSoFar = halfBuffer;
			}

			if (responseBytes != 0)
			{
				char *promptStart;
				NDTS_LogLine(NDTS_LLDebug, "Time %d Serial from: %s", (clock() - startTime) / CLOCKS_PER_SEC , response);
				promptStart = strstr(response, "PULSERMUX");
				if (promptStart)
				{
					// Found the prompt now get the version
					if (sscanf(promptStart, "PULSERMUX-%d.%d.%d", &verNo1, &verNo2, &verNo3) == 3)
					{
						return NDTS_BTrue;
					}
				}
			}
		}
		// Don't wait for too long here - if the mux isn't on line then inform the user and give up
		while ((clock() - startTime) / CLOCKS_PER_SEC < 2 && !badResponse);
		if (!badResponse)
		{
			// I think the COM port can go wrong when the Mux is rebooted - this helps it recover
			CloseHandle(comPortHandle);
			PRX_InitialiseSonatestSerialPort();
			NDTS_LogLine(NDTS_LLErr, "Timeout waiting for response from MUX to command %s.  Retries %d", string, retries);
		}
		// Send the string again because either not got the response or got an error response
		PRX_SendStringToSonatestSerialPort("  ");
	}

	// This isn't a nice thing to do but IW is not aware of the Sonatest Mux so it is probably the best thing to do
	// under the circumstances
	MessageBox(0, "Sonatest Serial Mux is not responding - check the Mux and restart Inspection Ware.  If you are not using a Sonatest serial mux - contact NDT Solutions for help", 0, MB_OK);

	return NDTS_BFalse;
}