// ------------------------------- //
// -------- Start of File -------- //
// ------------------------------- //
// ----------------------------------------------------------- // 
// C++ Source Code File Name: r_win32.cpp
// C++ Compiler Used: MSVC, BCC32, GCC, HPUX aCC, SOLARIS CC
// Produced By: DataReel Software Development Team
// File Creation Date: 09/20/1999
// Date Last Modified: 06/17/2016
// Copyright (c) 2001-2024 DataReel Software Development
// ----------------------------------------------------------- // 
// ------------- Program Description and Details ------------- // 
// ----------------------------------------------------------- // 
/*
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
 
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.
 
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  
USA

Code used by Win32 applications to read and write data to and
from a serial port using overlapped I/O.
*/
// ----------------------------------------------------------- // 
#include "gxdlcode.h"

void PrintError(LPCSTR str)
{
  LPVOID lpMessageBuffer;
  int error = GetLastError();
  FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
		FORMAT_MESSAGE_FROM_SYSTEM,
		NULL,
		error,
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
		(LPTSTR) &lpMessageBuffer,
		0,
		NULL);
  cout << str << ": " << error << " " << lpMessageBuffer << "\n";
  LocalFree(lpMessageBuffer);
}

DWORD CALLBACK ConsoleThread(HANDLE h)
// Starts a user input thread using the console.
// Takes console characters and sends them to the com port.
{
  OVERLAPPED ov;
  HANDLE hconn = GetStdHandle(STD_INPUT_HANDLE);
  BOOL quit = FALSE;
  
  ZeroMemory(&ov, sizeof(ov));
  ov.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  if(ov.hEvent == INVALID_HANDLE_VALUE) {
    PrintError("CreateEvent failed");
    SetCommMask(h, 0);
    return 0;
  }
  
  SetConsoleMode(hconn, 0);
  cout << "Waiting to receive..." << "\n";
  cout << "Press Esc to terminate." << "\n";
  do {
    char buf[10];
    DWORD bytes_read = 0;
    
    // Wait for user data
    WaitForSingleObject(hconn, INFINITE);
    
    // Read the console buffer
    if(!ReadConsole(hconn, buf, sizeof(buf), &bytes_read, NULL)) {
      PrintError("ReadConsole failed");
      quit  = TRUE;
    }
    if(bytes_read) {
      DWORD write;
      // Check for Esc key
      for(write=0; write < bytes_read; write++) {
	// Terminate when Esc key is pressed
	if(buf[write] == 0x1b) {
	  quit = TRUE;
	  break;
	}
      }
      // Send data to the serial device
      if(write) {
	if(!WriteFile(h, buf, write, &write, &ov)) {
	  if(GetLastError() == ERROR_IO_PENDING) {
	    if(!GetOverlappedResult(h, &ov, &write, TRUE)) {
	      PrintError("GetOverlappedResult failed");
	      quit = TRUE;
	    }
	  }
	} else {
	  PrintError("WriteFile failed");
	  quit = TRUE;
	}
      }
    }
    
  } while(!quit);
  
  // Signal the terminal thread to quit
  SetCommMask(h, 0);
  
  return 0;
}

void SerialRead(gxSerialComm *dev, char *dumpfile = 0)
{
  DiskFileB outfile;
  if(dumpfile) {
    cout << "Writing output to the " << dumpfile << " file" << "\n";
    outfile.df_Open(dumpfile);
    if(!outfile) {
      cout << "\n";
      cout << "Cannot open the: " << dumpfile << " capture file"
	   << "\n";
      cout << "Exiting..." << "\n";
      cout << "\n";
      return;
    }
  }

  HANDLE hconn = GetStdHandle(STD_OUTPUT_HANDLE);
  HANDLE h = dev->DeviceHandle();
  DWORD mask;
  DWORD id;
  OVERLAPPED ov;
    
  HANDLE hConsole = CreateThread(NULL, 0, ConsoleThread, h, 0, &id);
  if(hConsole == INVALID_HANDLE_VALUE) {
    PrintError("CreateThread failed");
    return;
  }
  CloseHandle(hConsole);
  
  ZeroMemory(&ov, sizeof(ov));

  // Create event for overlapped I/O
  ov.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  if(ov.hEvent == INVALID_HANDLE_VALUE)
    PrintError("CreateEvent failed");
  
  // Wait for received characters
  if(!SetCommMask(h, EV_RXCHAR))
    PrintError("SetCommMask failed");
  
  while(1) {
    // Get the event mask
    if(!WaitCommEvent(h, &mask, &ov)) {
      DWORD e = GetLastError();
      if(e == ERROR_IO_PENDING) {
	DWORD r;
	if(!GetOverlappedResult(h, &ov, &r, TRUE)) {
	  PrintError("GetOverlappedResult failed");
	  break;
	}
      } else {
	PrintError("WaitCommEvent failed");
	break;
      }
    }
    
    // If no event, then UI thread terminated with SetCommMask(h, 0)
    if(mask == 0) {
      break;
    }

    if(mask & EV_RXCHAR) {
      char buf[10];
      DWORD bytes_read;
      do {
	bytes_read = 0;
	if(!ReadFile(h, buf, sizeof(buf), &bytes_read, &ov)) {
	  if(GetLastError() == ERROR_IO_PENDING) {
	    if(!GetOverlappedResult(h, &ov, &bytes_read, TRUE)) {
	      PrintError("GetOverlappedResult failed");
	      break;
	    }
	  } else {
	    PrintError("ReadFile failed");
	    break;
	  }
	}
	if(bytes_read) {
	  if(dumpfile) { // Capture the data to a file
	    outfile.df_Write((char *)buf, bytes_read);
	    outfile.df_Flush();
	  }
	  else // Write the data to the console
	    WriteFile(hconn, buf, bytes_read, &bytes_read, NULL);
	}
      } while(bytes_read);
    }
    mask = 0;
  }
  outfile.df_Close();
  CloseHandle(ov.hEvent);  // Close the event
}
// ----------------------------------------------------------- // 
// ------------------------------- //
// --------- End of File --------- //
// ------------------------------- //