// comm.cpp : Defines the entry point for the console application.
//

#define _CRT_SECURE_NO_DEPRECATE
#include <windows.h>
#include <stdio.h>
#include <process.h> 
#include <signal.h>
#include <assert.h>

// это - баг. файл блокировки должен находиться в конкретном месте, а не в текущем каталоге
#define LOCK_FILE_TRUENAME "8852.lck"

#define LOCK_FILE_NAME_SIZE 512


#ifdef WIN32
#define spawnl _spawnl
#define unlink _unlink
#endif 

int resetPort( HANDLE hCom, char evtChar ) {
	DCB dcb;

    if (!GetCommState(hCom, &dcb))  {   printf ("GetCommState failed with error %d.\n", GetLastError());   return (1);  }

   // Fill in DCB: 57,600 bps, 8 data bits, no parity, and 1 stop bit.
 
    dcb.BaudRate = CBR_2400;     // set the baud rate
    dcb.ByteSize = 8;             // data size, xmit, and rcv
    dcb.Parity = NOPARITY;        // no parity bit
    dcb.StopBits = ONESTOPBIT;    // one stop bit

	dcb.EvtChar = evtChar; 
	dcb.XonLim = dcb.XoffLim = 25; 

	if (!SetCommState(hCom, &dcb)) {  printf ("SetCommState failed with error %d.\n", GetLastError());  return (1); }
	return 0; 
}

char *pcCommPort;

int read_from_8852()
{
   HANDLE hCom;
   BOOL fSuccess;

   hCom = CreateFile( pcCommPort,
                    GENERIC_READ | GENERIC_WRITE,
                    0,    // must be opened with exclusive-access
                    NULL, // no security attributes
                    OPEN_EXISTING, // must use OPEN_EXISTING
                    0,    // not overlapped I/O
                    NULL  // hTemplate must be NULL for comm devices
                    );

   if (hCom == INVALID_HANDLE_VALUE) 
   {
       // Handle the error.
       printf ("CreateFile failed with error %d.\n", GetLastError());
       return (1);
   }
   printf("opened %s\n", pcCommPort); 

//   EscapeCommFunction( 0, 38 ) ничего не делаем, т.к. функция непонятно что значит. Она вернула 0x700465
// что это значит - тоже непонятно. 
//   fSuccess = EscapeCommFunction( hCom, CLRTIMERLOGIC, NULL, NULL);
//   if(!fSuccess) { printf("CLRTIMERLOGIC failed %d.\n", GetLastError()); return (2); }

   // Build on the current configuration, and skip setting the size
   // of the input and output buffers with SetupComm.
	if (resetPort(hCom,0)!=0) return(1); 
	if (!SetupComm(hCom,256,256)) { printf("SetupComm failed with error %d.\n", GetLastError()); return(1); }

	COMMTIMEOUTS cto;
	cto.ReadIntervalTimeout=-1;
	cto.ReadTotalTimeoutMultiplier=0;  
	cto.ReadTotalTimeoutConstant=0;  
	cto.WriteTotalTimeoutMultiplier=0;  
	cto.WriteTotalTimeoutConstant=0;
	if (!SetCommTimeouts(hCom,&cto)) { printf("SetCommTimeouts failed with error %d.\n", GetLastError()); return(1); }
	if (!PurgeComm(hCom,PURGE_RXABORT|PURGE_RXCLEAR))  { printf("PurgeComm failed with error %d.\n", GetLastError()); return(1); }
	if (!PurgeComm(hCom,PURGE_TXABORT|PURGE_TXCLEAR))  { printf("PurgeComm failed with error %d.\n", GetLastError()); return(1); }
	if (!SetCommMask(hCom,0)) { printf("first WaitCommEvent failed, %d.\n", GetLastError()); return(1); }
	if (resetPort(hCom,0xd)!=0) return(1); 
	if (!SetCommMask(hCom,EV_RXFLAG)) { printf("setCommMask, %d.\n", GetLastError()); return(1); }

	DWORD evtMask;
	while(1) {
		while(1) {
			if (!WaitCommEvent(hCom,&evtMask,NULL)) { printf("first WaitCommEvent failed, %d.\n", GetLastError()); return(1); }
//			printf("WaitCommEvent returned %d.\n",evtMask);
			if (evtMask & EV_RXFLAG) 
				break; 
		}

		DWORD errors;
		COMSTAT stat;
		if (!ClearCommError(hCom,&errors,&stat)) { printf("ClearCommError failed, %d.\n", GetLastError()); return(1); }

		char buf[513];
		char *bufPtr=buf;
		DWORD numberOfBytesRead;
		for (bufPtr=buf;bufPtr-buf<512;bufPtr++) {
		  fSuccess=ReadFile(hCom,bufPtr,1,&numberOfBytesRead,NULL);
		  if (!fSuccess)  { printf("ReadFile failed, %d.\n", GetLastError()); return(1); }
		  if(numberOfBytesRead==0) 
			  break;
		}
		*bufPtr=0;
		printf("%s",buf);
		if (!ClearCommError(hCom,&errors,&stat)) { printf("ClearCommError failed, %d.\n", GetLastError()); return(1); }
	}
    return (0);
}

int run_reader(int argc, char *argv[]) {
	char lockFileName[LOCK_FILE_NAME_SIZE];
	int tempPathLength=GetTempPath(LOCK_FILE_NAME_SIZE,lockFileName);
	if (!tempPathLength) {
		printf("unable to get lockfile dir");
		exit(10); 
	}
	if (sprintf_s(lockFileName+tempPathLength,LOCK_FILE_NAME_SIZE-tempPathLength,LOCK_FILE_TRUENAME)<int(strlen(LOCK_FILE_TRUENAME))) {
		printf("lock file name does not fit in buffer\n");
		exit(11); 
	}
	assert(sizeof(HANDLE)==sizeof(DWORD)); 
	if (argc==2) { 
		// to allow async input from port, create lock-file, start sub-process, wait for any character 
		// on input, then kill sub-process and exit. 
		FILE *lock_file;
		int myErrNo=fopen_s(&lock_file,lockFileName,"a");
		if (myErrNo) { printf("unable to create %s, error %d\n", lockFileName,myErrNo);  return(1);  } else
		{printf("created %s\n",lockFileName);}
		intptr_t child=spawnl( _P_NOWAIT, argv[0],argv[0],argv[1],"1",NULL);
		if (child==-1) {  printf("unable to spawn child, error %d\n", errno); return(2); }; 
		getc(stdin); 
		fclose(lock_file); 
		unlink(lockFileName);
		TerminateProcess(HANDLE(child),0);
		return(0);
	}
	else {
		printf("going to read from 8852\n");
		read_from_8852(); // never returns
  	    return(0);
	}
}

int main(int argc, char *argv[]) { // должен быть передан аргумент - имя порта, напр, COM1 или COM2
	assert(argc>1); 
    pcCommPort = argv[1];
	printf("started with argc=%d, com_port=%s\n",argc,pcCommPort);
	int res=run_reader(argc,argv); 
	putc('q',stdout);
	fflush(stdout); 
	return(res);
}
