#include <windows.h>
#include <string.h>
#include "IrLib.h"

HANDLE hDLLInst = 0;

static int FillDCB (int nBaud);
static int TestIR (DWORD spec);

// static char *szComPort[] = {"COM1", "COM1", "COM2", "COM3", "COM4",
//                            "COM5", "COM6", "COM7", "COM8", "COM9"};

static HANDLE hComm=NULL;          // the handle of the opened Comm Device.
static BYTE   cInBuf [256];        // Input buffer for receiving data.
static BYTE   szBuf [256];         // Record Buffer.
static BYTE   szIR [8];            // for IR command

static COMMTIMEOUTS  TimeOuts;
static DCB    dcb;
static DWORD  nChar;
static BOOL   bShowErrorMessage=TRUE;
static int    nDlyTime=200;        // delay time for DIP switch cradle, 2004.02.20

//-------------------------------------------------------------------------//
BOOL WINAPI DllMain (HANDLE hModule, DWORD dwFunc, LPVOID lpNot)
   {
   hDLLInst = hModule;

   switch (dwFunc)
       {
       case DLL_PROCESS_ATTACH:
       case DLL_PROCESS_DETACH:
       default:
            break;
       }
   return TRUE;
   }

//-------------------------------------------------------------------------//
DllExport HANDLE WINAPI OpenIrCom (int nPort, int nBaud)
   {
   char szComPort[16];

   wsprintf (szComPort, "\\\\.\\COM%d", nPort);  // 2006.08.16, to support COM port > 9
   if ((hComm = CreateFile (szComPort, GENERIC_READ | GENERIC_WRITE,
                 0, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE)
       {
       if (bShowErrorMessage)
           MessageBox (GetActiveWindow(), "Can not open the COM port!!",
                       "Error", MB_OK | MB_ICONEXCLAMATION);
       return NULL;
       }

   SetupComm (hComm, 256, 256);       // allocate transmit & receive buffer
   if (!FillDCB (nBaud))
       {
       CloseHandle (hComm);
       if (bShowErrorMessage)
           MessageBox (GetActiveWindow(), "Can not configure the IR device!!",
                       "Error", MB_OK | MB_ICONEXCLAMATION);
       return NULL;
       }

   if (SetCommState (hComm, &dcb) < 0)
       {
       CloseHandle (hComm);
       if (bShowErrorMessage)
           MessageBox (GetActiveWindow(), "Can not initialize the COM port!!",
                                          "Error", MB_OK);
       hComm = NULL;
       return NULL;
       }

   TimeOuts.ReadIntervalTimeout = MAXDWORD;
   TimeOuts.ReadTotalTimeoutMultiplier = 0;
   TimeOuts.ReadTotalTimeoutConstant = 0;
   TimeOuts.WriteTotalTimeoutMultiplier = 5;
   TimeOuts.WriteTotalTimeoutConstant = 50;
   SetCommTimeouts (hComm, &TimeOuts);           // check dwProvCapabilities

   return hComm;
   }

//-------------------------------------------------------------------------//
DllExport void WINAPI CloseIrCom (void)
   {   
   if (hComm != INVALID_HANDLE_VALUE && hComm != NULL)
       {
       CloseHandle (hComm);                      // close the COM Port.
       hComm = NULL;
       }
   }                                                                         

//-------------------------------------------------------------------------//
DllExport void WINAPI WriteIrCom (LPSTR lpStr)
   {
   DWORD   nChar, nCount;

   nCount = lstrlen (lpStr);
   WriteFile (hComm, lpStr, nCount, &nChar, NULL);
   }

//-------------------------------------------------------------------------//
DllExport LPSTR WINAPI ReadIrCom (void)
   {
   DWORD   i, nChar, nCount;
   BYTE    cRet;
   DWORD   dwTime0;
                        
   dwTime0 = GetTickCount ();
   nCount = 0;                                         // reset char counter
   cRet = 0;
      
   while (cRet != '\r')     // while not get the return char keep on reading
       {
       if (ReadFile (hComm, cInBuf, 1, &nChar, NULL))
           {
           for (i=0; i<nChar; i++)
               if (nCount < 255)
                   szBuf [nCount++] = cInBuf [i];
           cRet = cInBuf[i-1];
           }

       if (GetTickCount() - dwTime0 > 5000)            // check if time out
           {                            
           if (bShowErrorMessage)
               MessageBox (GetActiveWindow(), "Time out!", "Error", MB_OK);
           return NULL;
           }
       }
   szBuf [nCount] = 0;

   return szBuf;
   }

//-------------------------------------------------------------------------//
DllExport int WINAPI vbOpenIrCom (int nPort, int nBaud)
   {
   char szComPort[16];

   wsprintf (szComPort, "\\\\.\\COM%d", nPort);  // 2006.08.16, to support COM port > 9
   if ((hComm = CreateFile (szComPort, GENERIC_READ | GENERIC_WRITE,
                 0, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE)
       {
       if (bShowErrorMessage)
           MessageBox (GetActiveWindow(), "Can not open the COM port!!",
                       "Error", MB_OK | MB_ICONEXCLAMATION);
       return 0;
       }

   SetupComm (hComm, 256, 256);       // allocate transmit & receive buffer
   if (!FillDCB (nBaud))
       {
       CloseHandle (hComm);
       if (bShowErrorMessage)
           MessageBox (GetActiveWindow(), "Can not configure the IR device!!",
                      "Error", MB_OK | MB_ICONEXCLAMATION);
       return -1;
       }

   if (SetCommState (hComm, &dcb) < 0)
       {
       CloseHandle (hComm);
       if (bShowErrorMessage)
           MessageBox (GetActiveWindow(), "Can not initialize the COM port!!",
                                          "Error", MB_OK);
       hComm = NULL;
       return -1;
       }

   TimeOuts.ReadIntervalTimeout = MAXDWORD;
   TimeOuts.ReadTotalTimeoutMultiplier = 0;
   TimeOuts.ReadTotalTimeoutConstant = 0;
   TimeOuts.WriteTotalTimeoutMultiplier = 5;
   TimeOuts.WriteTotalTimeoutConstant = 50;
   SetCommTimeouts (hComm, &TimeOuts);          // check dwProvCapabilities

   return 1;
   }

//-------------------------------------------------------------------------//
DllExport int WINAPI vbReadIrCom (LPSTR lpBuf)
   {
   DWORD   i, nChar, nCount;
   BYTE    cRet;
   DWORD   dwTime0;
                        
   dwTime0 = GetTickCount ();
   nCount = 0;                                         // reset char counter
   cRet = 0;
      
   while (cRet != '\r')     // while not get the return char keep on reading
       {
       if (ReadFile (hComm, cInBuf, 1, &nChar, NULL))
           {
           for (i=0; i<nChar; i++)
               if (nCount < 255)
                   szBuf [nCount++] = cInBuf [i];
           cRet = cInBuf[i-1];
           }

       if (GetTickCount() - dwTime0 > 5000)            // check if time out
           {                            
           if (bShowErrorMessage)
               MessageBox (GetActiveWindow(), "Time out!", "Error", MB_OK);
           return -1;
           }
       }
   szBuf [nCount] = 0;

//   lstrcpy (lpBuf, szBuf); 
   memcpy (lpBuf, szBuf, nCount);                     // 2003.9.29

   return nCount;
   }

//-------------------------------------------------------------------------//
DllExport int WINAPI ShowErrorMessage (int nShow)
   {
   bShowErrorMessage = (nShow > 0)? TRUE : FALSE;
   return 1;
   }

//-------------------------------------------------------------------------//
#pragma optimize ("", off)

static int FillDCB (int nBaud)
    {
    GetCommTimeouts (hComm, &TimeOuts);
    TimeOuts.ReadIntervalTimeout = MAXDWORD;
    TimeOuts.ReadTotalTimeoutMultiplier = 0;
    TimeOuts.ReadTotalTimeoutConstant = 0;
    TimeOuts.WriteTotalTimeoutMultiplier = 5;
    TimeOuts.WriteTotalTimeoutConstant = 50;
    SetCommTimeouts (hComm, &TimeOuts);

    dcb.BaudRate = 38400;
    dcb.ByteSize = 8;
    dcb.Parity = NOPARITY;
    dcb.StopBits = ONESTOPBIT;
    dcb.fRtsControl = RTS_CONTROL_ENABLE;        // set RTS on (low!)
    dcb.fDtrControl = DTR_CONTROL_ENABLE;        // set DTR on (low!)
    SetCommState (hComm, &dcb);                  // get power from DTR
    nDlyTime = 200;                              // 2004.02.20, for DIP switch cradle
    Sleep (nDlyTime);
	
    if (TestIR (CBR_9600))                       // test current baud rate
        dcb.BaudRate = CBR_9600;
    else if (TestIR (CBR_38400))
        dcb.BaudRate = CBR_38400;
    else if (TestIR (CBR_115200))
        dcb.BaudRate = CBR_115200;
    else if (TestIR (CBR_57600))
        dcb.BaudRate = CBR_57600;
    else if (TestIR (CBR_19200))
        dcb.BaudRate = CBR_19200;
    else
        {
        return 0;
        }

    dcb.fRtsControl = RTS_CONTROL_DISABLE;       // set RTS off (high!)
    SetCommState (hComm, &dcb);	
    Sleep (nDlyTime);

    *szIR = 0x07;
    WriteFile (hComm, szIR, 1, &nChar, NULL);    // disable Echo
    Sleep (nDlyTime);

    if (nBaud == 115200)
        {
        dcb.BaudRate = CBR_115200;
        *szIR = 0x36;
        }
    else if (nBaud == 57600)
        {
        dcb.BaudRate = CBR_57600;
        *szIR = 0x35;
        }
    else if (nBaud == 38400)
        {
        dcb.BaudRate = CBR_38400;
        *szIR = 0x34;
        }
    else if (nBaud == 19200)
        {
        dcb.BaudRate = CBR_19200;
        *szIR = 0x33;
        }
    else                                         // use default value:9600 
        {
        dcb.BaudRate = CBR_9600;
        *szIR = 0x32;
        }

    WriteFile (hComm, szIR, 1, &nChar, NULL);    // set to new baud rate
    Sleep (nDlyTime);

    *szIR = 0x51;
    WriteFile (hComm, szIR, 1, &nChar, NULL);    // load new baud rate
    Sleep (nDlyTime);

    dcb.fRtsControl = RTS_CONTROL_ENABLE;        // set RTS on (low!)
    SetCommState (hComm, &dcb);
    Sleep (nDlyTime);
	
    dcb.Parity = NOPARITY;
    dcb.ByteSize = 8;
    dcb.fOutxCtsFlow = FALSE;                    // no flow control
    dcb.fOutX = FALSE;

    return 1;
    }

//-------------------------------------------------------------------------//
static int TestIR (DWORD spec)
    {
    static BYTE szIn [4];
    DWORD  nChar, dwTime;
    BOOL   nRtn;
	
    dcb.BaudRate = spec;
    dcb.fRtsControl = RTS_CONTROL_DISABLE;       // set RTS off (high !!)
    SetCommState (hComm, &dcb);
    Sleep (nDlyTime);

	*szIR = 0x0f;                                // set Control register 1
	WriteFile (hComm, szIR, 1, &nChar, NULL);
    Sleep (nDlyTime);

    dwTime = GetTickCount();
    nRtn = *szIn = 0;
    while (GetTickCount() - dwTime < 100)        // 100 msec
		{
		if (ReadFile (hComm, szIn, 1, &nChar, NULL)) // waiting for echo
			{
			if (*szIn == 0x0f)                   // receive the same char
                nRtn = 1;
			break;
			}
		}

	dcb.fRtsControl = RTS_CONTROL_ENABLE;        // set RTS on (LOW !!)
	SetCommState (hComm, &dcb);
    Sleep (nDlyTime);
    nDlyTime = 100;    // 2004.02.20, for DIP switch cradle, set to 100 except 9600bps
	return nRtn;
    }

#pragma optimize ("", on)
//-------------------------------------------------------------------------//
