//*****************************************************************************
//
// serial_pic.cpp : Defines the entry point for the Win32 console application.
//
// Author:  Reed Bement 04/09/2004
//
//    Note:
//    ----
//
//    Application tests the simple serial data communications library by 
//    transfering data over the command line specified serial port. 
//
//*****************************************************************************

#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <process.h>

#include "serial.h"

// Structure that holds parsed command line parameters.

typedef struct 
{
   DWORD          dwComPort;
   DWORD          dwBaudRate;
   char           szInFile[MAX_PATH];
   char           szOutFile[MAX_PATH];

} CMD_PARAMS, *PCMD_PARAMS; 
 
// Structure that holds receive and transmit thread parameters.

typedef struct 
{
   FILE              *fp;
   PSERIAL_CONTEXT   psc;

}  THRD_PARAMS, *PTHRD_PARAMS;


//*****************************************************************************
//
// Global Variables
//
//*****************************************************************************

BOOL  g_bTransmitRunning;
BOOL  g_bReceiveRunning;
DWORD g_dwBytesSent;

//*****************************************************************************
//
// Usage - Prints commandline syntax at console.
//
// Paramaters - None.
//
// Returns - None.
//
//*****************************************************************************

void Usage(void)
{
    printf("\nUsage:\n");
    printf("serial_pic COMm[:] [-bBaudRate] -iInFile -oOutFile\n\n");
}

//*****************************************************************************
//
// ParseCmdLineParams -  Parse the command line parameters.
//
// Paramaters -
//    argc - See SDK.
//    argv - See SDK.
//    pcp  - Pointer to parsed command line parameters structure.
//
// Returns - TRUE on success.
//
//*****************************************************************************

BOOL ParseCmdLineParams(int argc, char* argv[], PCMD_PARAMS pcp)
{
   char *psz;
   bool bInputSpecDone = FALSE;

   // Process command line arguments
   if ((argc < 4) || (argc > 5)) {
      if (argc < 4) {
         printf("\nNot enough parameters\n");
      } else {
         printf("\nToo many parameters\n");
      }

      return FALSE;
   }

   // Loop through arguments, skip arg zero.
   for (int i = 1; i < argc; i++) {

      psz = argv[i];

      if (*psz == '-') {

         // Handle prefixed parameter.
         psz++;

         switch (*psz++) {
            
            case 'b':
            case 'B':
               if (sscanf(psz, "%d", &pcp->dwBaudRate) != 1) {
                  printf("\nError converting baud rate number: %s\n", psz);
                  return FALSE;
               }
               break;

            case 'i':
            case 'I':
               strncpy(pcp->szInFile, psz, MAX_PATH);  
               break;
   
            case 'o':
            case 'O':
               strncpy(pcp->szOutFile, psz, MAX_PATH);  
               break;
   
            default:
               psz--;
               printf("\nUnknown parameter: %c%c\n", *(argv[i]), *psz);
               return FALSE;
         }

      } else {

         // Handle unprefixed COM port specification.
         char szTmp[4];

         strncpy(szTmp, psz, 3); szTmp[3] = '\0'; psz+= 3; _strupr(szTmp);
         
         if (strcmp(szTmp, "COM")) {
            printf("\nUnknown parameter: %s\n", psz);
            return FALSE;
         }

         if (sscanf(psz, "%d", &pcp->dwComPort) != 1) {
            printf("\nError converting port number: %s\n", psz);
            return FALSE;
         }
      }
   }

   return TRUE;
}

//*****************************************************************************
//
// RecieveThread - Receive characters and write them to an open file.
//
// Paramaters
//    vp - FIle handle to receive file.
//
// Returns - Nothing.
//
//*****************************************************************************

void RecieveThread(void *vp)
{
   DWORD dwByteCount = 0;

   PTHRD_PARAMS ptp = (PTHRD_PARAMS) vp;

   while (g_bReceiveRunning) {

      BYTE b;

      if (RecvSerialByte(ptp->psc, &b)) {

         fputc(b, ptp->fp);

         dwByteCount++;

         if (g_dwBytesSent) {
            if (dwByteCount >= g_dwBytesSent) {
               printf("Received all sent bytes.\n");
               break;
            }
         }
      } else {
         printf("Error while receiving bytes. Count: %d\n", dwByteCount);
         printf("%s\n\n", ptp->psc->szError);
         break;
      }
   }
   
   g_bReceiveRunning = FALSE;
   printf("\nTotal bytes received: %d\n", dwByteCount);

   _endthread();
}

//*****************************************************************************
//
// TransmitThread - Send characters read from an open file.
//
// Paramaters
//    vp - FIle handle to send file.
//
// Returns - Nothing.
//
//*****************************************************************************

void TransmitThread(void *vp)
{
   DWORD dwByteCount = 0;

   PTHRD_PARAMS ptp = (PTHRD_PARAMS) vp;

   while (g_bTransmitRunning) {

      char ch = fgetc(ptp->fp);
      
      if (!feof(ptp->fp)) {
         
         if (SendSerialByte(ptp->psc, ch)) {
            dwByteCount++;
         } else {
            printf("Error while sending byte: 0x%X, count: %d\n", ch, dwByteCount);
            printf("%s\n\n", ptp->psc->szError);
            break;
         }
      }
      else {
         break;
      }
   }

   printf("\nTotal bytes transmitted: %d\n", dwByteCount);
   g_dwBytesSent = dwByteCount;
   g_bTransmitRunning = false;

   _endthread();
}

//*****************************************************************************
//
// Main - Entry point for serial_pic.exe application.
//
// Paramaters - See SDK.
//
// Returns - Zero on success, else error code.
//
//*****************************************************************************

int main(int argc, char* argv[])
{
   printf("Skeleton Win32 to PIC serial data communications test.\n");

   CMD_PARAMS cp;

   // Default to 9600 baud.
   cp.dwBaudRate = CBR_9600;


   if (ParseCmdLineParams(argc, argv, &cp)) {

      SERIAL_CONTEXT sc;

      if (InitSerial(&sc, cp.dwComPort, cp.dwBaudRate)) {
         printf("Opened COM%d at %d baud\n", cp.dwComPort, cp.dwBaudRate);

         FILE *fpInFile, *fpOutFile;

         // Open input file.
         fpInFile = fopen(cp.szInFile,"rb");
         if (fpInFile) {
            
            printf("Sending from: %s\n", cp.szInFile);

            // Create output file.
            fpOutFile = fopen(cp.szOutFile,"wb");
            if (fpOutFile) {
               
               printf("Receiving to: %s\n", cp.szOutFile);
               
               // Start recieve data thread.
               THRD_PARAMS rtp;
               
               rtp.fp  = fpOutFile;
               rtp.psc = &sc;

               g_bReceiveRunning = TRUE;
               _beginthread(RecieveThread, 0, &rtp);
      
               // Start send data thread.
               THRD_PARAMS ttp;
               
               ttp.fp  = fpInFile;
               ttp.psc = &sc;
               
               g_bTransmitRunning = TRUE;
               _beginthread(TransmitThread, 0, &ttp);
      
               // Loop until finished or interrupted.
               printf("Hit anykey to abort...\n");
               while (g_bTransmitRunning || g_bReceiveRunning)
               {
                  if (_kbhit()) {
                      _getch();
                      g_bTransmitRunning = g_bReceiveRunning = FALSE;
                      printf("\nUser abort.\n");
                      break;
                  }
               }
               
               fclose(fpOutFile);   
            }
            else {
               printf("Unable to create: %s\n", cp.szOutFile);
               printf("Last error: %d\n", GetLastError());
            }
            
            fclose(fpInFile);   
         }
         else {
            printf("Unable to open: %s\n", cp.szInFile);
            printf("Last error: %d\n", GetLastError());
         }

         CloseSerial(&sc);
      } else {
         printf("Error while initializing  COM%d at %d baud\n", cp.dwComPort, cp.dwBaudRate);
         printf("%s\n\n", sc.szError);
      }
   } else {
      Usage();
   }

   return 0;
}

