// Zmodlib.cpp


#include "zmcore.h"
#include "Zmodlib.h"
#include "error.h"
#include "unused.h"

HANDLE hSerCom;
FILE *fq;
#define READ_TIMEOUT      500      // milliseconds



/*****************************************************************/

int InitCom(void)
{
	DCB dcb;
	BOOL fSuccess;
	char *pcCommPort = "COM1";

	COMMTIMEOUTS timeouts;
	timeouts.ReadIntervalTimeout = 20; 
	timeouts.ReadTotalTimeoutMultiplier = 10;
	timeouts.ReadTotalTimeoutConstant = 100;
	timeouts.WriteTotalTimeoutMultiplier = 0;
	timeouts.WriteTotalTimeoutConstant = 0;


	hSerCom = CreateFile( pcCommPort,
					GENERIC_READ | GENERIC_WRITE,
					0,    // must be opened with exclusive-access
					NULL, // no security attributes
					OPEN_EXISTING, // must use OPEN_EXISTING
					FILE_FLAG_OVERLAPPED,    // overlapped I/O
					NULL  // hTemplate must be NULL for comm devices
					);

	if (hSerCom == INVALID_HANDLE_VALUE) 
	{
		// Handle the error.
		printf ("CreateFile failed with error %d.\n", GetLastError());
		return (1);
	}

	// Build on the current configuration, and skip setting the size
	// of the input and output buffers with SetupComm.

	fSuccess = GetCommState(hSerCom, &dcb);

	if (!fSuccess) 
	{
		// Handle the error.
		printf ("GetCommState failed with error %d.\n", GetLastError());
		return (2);
	}

	// Fill in DCB: 115,200 bps, 8 data bits, no parity, and 1 stop bit.

	dcb.ByteSize = 8;             // data size, xmit, and rcv
	dcb.StopBits = ONESTOPBIT;    // one stop bit
	dcb.DCBlength = sizeof(dcb);      /* sizeof(DCB) */
	dcb.BaudRate = CBR_115200;     // set the baud rate
	dcb.Parity = NOPARITY;        // no parity bit
    dcb.fOutxCtsFlow = FALSE;		/* CTS handshaking on output */
    dcb.fOutxDsrFlow = FALSE;		/* DSR handshaking on output */
    dcb.fDtrControl = DTR_CONTROL_DISABLE;  /* DTR Flow control  */
    dcb.fDsrSensitivity = FALSE;	/* DSR Sensitivity */
    dcb.fTXContinueOnXoff = TRUE; /* Continue TX when Xoff sent */
    dcb.fOutX = FALSE;       /* Enable output X-ON/X-OFF */
    dcb.fInX = FALSE;        /* Enable input X-ON/X-OFF */
    dcb.fNull = FALSE;       /* Enable Null stripping */
    dcb.fRtsControl = RTS_CONTROL_DISABLE;  /* Rts Flow control */
    dcb.fAbortOnError = FALSE;	/* Abort all reads and writes on Error */
  
	fSuccess = SetCommState(hSerCom, &dcb);
	if (!fSuccess) 
	{
		// Handle the error.
		printf ("SetCommState failed with error %d.\n", GetLastError());
		return (3);
	}

	if (!SetCommTimeouts(hSerCom, &timeouts))
	// Error setting time-outs.


	printf ("Serial port %s successfully reconfigured.\n", pcCommPort);
	return 0;
}

/*********************************************************************************/

void CloseCom(void)
{
	CloseHandle(hSerCom);
}

/*********************************************************************************/
BOOL WriteSerPort(char *lpBuf, DWORD dwToWrite)
{
   OVERLAPPED osWrite = {0};
   DWORD dwWritten;
   BOOL fRes;

   // Create this writes OVERLAPPED structure hEvent.
   osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
   if (osWrite.hEvent == NULL)
      // Error creating overlapped event handle.
      return FALSE;

   // Issue write.
   if (!WriteFile(hSerCom, lpBuf, dwToWrite, &dwWritten, &osWrite)) {
      if (GetLastError() != ERROR_IO_PENDING) { 
         // WriteFile failed, but it isn't delayed. Report error and abort.
         fRes = FALSE;
      }
      else {
         // Write is pending.
         if (!GetOverlappedResult(hSerCom, &osWrite, &dwWritten, TRUE))
            fRes = FALSE;
         else
            // Write operation completed successfully.
            fRes = TRUE;
      }
   }
   else
      // WriteFile completed immediately.
      fRes = TRUE;

   CloseHandle(osWrite.hEvent);
   return fRes;
}

/**********************************************************************************/

BOOL ReadSerPort(char* lpBuf, DWORD dwToRead, DWORD *dwRead)
{
	DWORD dwRes;
	BOOL fWaitingOnRead = FALSE;
	OVERLAPPED osReader = {0};


	// Create the overlapped event. Must be closed before exiting
	// to avoid a handle leak.
	osReader.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

	if (osReader.hEvent == NULL)
	{
		// Error creating overlapped event; abort.
		printf("\nError Create Reader Event");
		return 1;
	}

	while(1)
	{
		if (!fWaitingOnRead)
		{
			// Issue read operation.
			if (!ReadFile(hSerCom, lpBuf, dwToRead, dwRead, &osReader))
			{
				if (GetLastError() != ERROR_IO_PENDING)     // read not delayed?
					printf("\nError in GetLastError.");
				else
					fWaitingOnRead = TRUE;
			}
			else
			{    
				// read completed immediately
				CloseHandle(osReader.hEvent);
				return 0;
			}
		}


		if (fWaitingOnRead)
		{
			dwRes = WaitForSingleObject(osReader.hEvent, READ_TIMEOUT);
			switch(dwRes)
			{
				// Read completed.
				case WAIT_OBJECT_0:
					if (!GetOverlappedResult(hSerCom, &osReader, dwRead, FALSE))
					{
						printf("\nError in GetOverlappedResult");
						return 1;
					}
					else
					{
						// Read completed successfully.
						CloseHandle(osReader.hEvent);
						return 0;
						break;
					}

				case WAIT_TIMEOUT:
					// Operation isn't complete yet, so loop back around.
					break;                       

				default:
					// Error in the WaitForSingleObject; abort.
					printf("\nError in WaitForSingleObject.");
					CloseHandle(osReader.hEvent);
					return 1;
					break;
			}
		}
	}

}
/********************************************************/

void sleep(clock_t seconds)
{   
	clock_t goal;
	goal = (seconds * CLOCKS_PER_SEC ) + clock();
	while( goal > clock() );
}

/**************************************************************/

void extModemClearInbound(ZMCORE *zmcore)
{
    char lpBuf[200];
	DWORD dwRead;

    do
	{
		(ReadSerPort(lpBuf, 1, &dwRead));
	}
	while (dwRead);
    return;
}

/**************************************************************/

void extModemGetBlock(ZMCORE *zmcore, 
                       void *buf, 
                      size_t max, 
                      size_t *actual)
{
    int cnt;
    DWORD dwToRead, dwRead;
    unused(zmcore);

	dwToRead = (DWORD)max;
	ReadSerPort((char*)buf, dwToRead, &dwRead);
    if (dwRead == 0)
    {
        /* wait for 2 seconds for response */
        for (cnt = 0; cnt < 200; cnt++)
        {
			/* Pause 10 milliseconds. */
			clock_t goal;
			goal = 10 + clock();
			while( goal > clock() );
		    ReadSerPort((char*)buf, dwToRead, &dwRead);
			if (dwRead != 0) break;
        }
    }
    if (dwRead == 0)
    {
        errorSet(ZMODEM_TIMEOUT);
    }
    *actual = dwRead;
    return;
}

/**************************************************************/

void extModemRegisterBad(ZMCORE *zmcore)
{
    unused(zmcore);
    return;
}

/**************************************************************/

void extModemRegisterGood(ZMCORE *zmcore)
{
    unused(zmcore);
//    unused(zmext);
    return;
}

/**************************************************************/

void extModemGetBlockImm(ZMCORE *zmcore, 
                         void *buf, 
                         size_t max, 
                         size_t *actual)
{
    DWORD dwToRead, dwRead;
    unused(zmcore);

	dwToRead = (DWORD)max;
	ReadSerPort((char*)buf, dwToRead, &dwRead);

    *actual = dwRead;
    if (dwRead == 0)
    {
        errorSet(ZMODEM_TIMEOUT);
    }
    return;
}

/**************************************************************/

void extModemWriteBlock(ZMCORE *zmcore, void *buf, size_t max)
{

    unused(zmcore);    
 	WriteSerPort((char*)buf, (DWORD)max);    
    return;
}
   
/**************************************************************/

void extFileSetInfo(ZMCORE *zmcore,
                    unsigned char *filename, 
                    unsigned char *fileinfo,
                    long *offset,
                    int *skip)
{
    unused(fileinfo);
    unused(zmcore);
    printf("opening file %s\n", filename);
    fq = fopen((char *)filename, "wb");
    if (fq == NULL)
    {
        printf("failed to open file %s\n", filename);
    }
    *offset = 0;
    *skip = 0;
    return;
}

/**************************************************************/

void extFileWriteData(ZMCORE *zmcore, void *buf, size_t bytes)
{
    unused(zmcore);
    fwrite(buf, bytes, 1, fq);
    return;
}

/**************************************************************/

void extFileFinish(ZMCORE *zmcore)
{
    unused(zmcore);
    printf("closing file\n");
    fclose(fq);
    return;
}

/**************************************************************/

int extFileGetFile(ZMCORE *zmcore, 
                   unsigned char *buf, 
                   long *filesize)
{
    int ret = 0;

    unused(zmcore);    
//    strcpy((char *)buf, zmext->fileList[zmext->fileUpto++]);
    printf("opening file %s\n", buf);
    fq = fopen((char *)buf, "rb");
    if (fq == NULL)
    {
        printf("failed to open file %s\n", buf);
    }
    else
    {
        fseek(fq, 0, SEEK_END);
        *filesize = ftell(fq);
        rewind(fq);
        ret = 1;
    }
    return (ret);
}

/**************************************************************/

void extFileSetPos(ZMCORE *zmcore, long offset)
{
    unused(zmcore);
    fseek(fq, offset, SEEK_SET);
    printf("seeking to offset %lu\n", offset);
    return;
}

/**************************************************************/

int extFileGetData(ZMCORE *zmcore,
                   void *buf, 
                   size_t max, 
                   size_t *bytes)
{
    unused(zmcore);
    *bytes = fread(buf, 1, max, fq);
    printf("read %d bytes\n", *bytes);
    return (*bytes != 0);
}

/*****************************************************************/
