/* Copyright 1999 by Herve Regad-Pellagru, E-mail: regad@micronet.fr

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.
 
    This program 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */

#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>

#include "cm12a.h"
#include "proto.h"
#include "error.h"

#define ASCII_XON       0x11
#define ASCII_XOFF      0x13

/* x10_open_tty_port() opens the serial port in an CM11A-friendly
   way (8 bits, 4800 bps, no parity) */
HANDLE x10_open_tty_port(char *filename)
{
  DCB dcb;
	COMMTIMEOUTS CommTimeOuts;
  HANDLE fd;

  if((fd = CreateFile(filename, GENERIC_READ | GENERIC_WRITE,
                  0,                    // exclusive access
                  NULL,                 // no security attrs
                  OPEN_EXISTING,
                  FILE_ATTRIBUTE_NORMAL, 
                  NULL)) == INVALID_HANDLE_VALUE)
    return(INVALID_HANDLE_VALUE);


		// get any early notifications
		SetCommMask(fd, EV_RXCHAR|EV_RING);

		// setup device buffers
		SetupComm(fd, 4096, 4096);

		// purge any information in the buffer
		PurgeComm(fd, PURGE_TXABORT | PURGE_RXABORT |
									  PURGE_TXCLEAR | PURGE_RXCLEAR);

		CommTimeOuts.ReadIntervalTimeout = 300;//1000 ;
		CommTimeOuts.ReadTotalTimeoutMultiplier = 300;//1000 ;
		CommTimeOuts.ReadTotalTimeoutConstant = 300;//1000 ;
		CommTimeOuts.WriteTotalTimeoutMultiplier = 300;//1000;
		CommTimeOuts.WriteTotalTimeoutConstant = 300;//1000 ;
		SetCommTimeouts(fd, &CommTimeOuts);


		// set baud rate and comms  parameters
		memset(&dcb,0,sizeof(DCB));
		dcb.DCBlength = sizeof(DCB);
		dcb.BaudRate = CBR_4800;
		dcb.ByteSize = 8; // bits per byte
		dcb.Parity = 0;   // 0-4=no,odd,even,mark,space
		dcb.StopBits = 0; // 0,1,2 = 1, 1.5, 2

		// setup hardware flow control
		dcb.fDtrControl = DTR_CONTROL_ENABLE;
		dcb.fRtsControl = RTS_CONTROL_ENABLE;

		// setup software flow control
		dcb.fInX = dcb.fOutX = TRUE;;
		dcb.XonChar = ASCII_XON;
		dcb.XoffChar = ASCII_XOFF;
		dcb.XonLim = 100;
		dcb.XoffLim = 100;

		// other various settings
		dcb.fBinary = TRUE;
		dcb.fParity = TRUE;

		SetCommState(fd, &dcb);

		EscapeCommFunction(fd, SETDTR);

    //purge com port
    //PurgeComm(fd, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);

  return(fd);
}


/* Just close the port */
int x10_close_tty_port(HANDLE fd)
{
  return(CloseHandle(fd));
}


/* Well, to read from the CM11A is a bit tricky as the damn thing can poll the PC
   and won't do anything until the poll is satisfied. Also, to avoid blocking, we
   use select with a timeout to actually get a chance of doing something else
   if nothing happens on the AC bus. Also, x10_read() has a flag called check_poll
   which tells whether we should consider polling codes as exception or not. */
int x10_read(struct x10_status_t *status,unsigned char *buffer,int leng,int useconds,
	     int check_poll)
{
  //struct timeval time_wait;
  BOOL ret;
  DWORD real_len, return_read;
  int return_complete;
  int total_read;

  /* fill time_wait struct for select(2), and set the FD_SET structure */
  //time_wait.tv_sec = useconds/1000000;
  //time_wait.tv_usec = useconds % 1000000;

  /*FD_ZERO(&(status->serial_set));
  FD_SET(status->fdinout,&(status->serial_set));

  real_len = select(status->fdinout+1,&(status->serial_set),NULL,NULL,&time_wait);
  */
  
  //ReadFile(status->fdinout, buffer, leng, &real_len, NULL);
  
  //P_DEBUG1("x10_read: select returns %d\n",real_len);

  /*switch(real_len)
    {*/
      /* select has failed -> error */
    /*case -1:
      P_DEBUG1("x10_read: %s FAILED\n","select");
      return(SELECT_FAILED);

    /*case 0:*/
      /* nothing to read after timeout */
      /*P_DEBUG1("x10_read: TIMEOUT in %s\n","read");
      return(TIMEOUT_READ);*/

    /*default:*/
      /* something to read */
      /*break;*/
    /*}*/

  /* Read only one byte, as it may be a poll and we have to treat it before
     coming back to our business */
  //return_read=read(status->fdinout,buffer,1);
  ret = ReadFile(status->fdinout, buffer, 1, &return_read, NULL);
  if (ret == FALSE)
  {
    P_DEBUG1("x10_read: %s FAILED\n","read");
    return(READ_FAILED);
  }

  if (return_read<=0)
  {
    //No data to read
    return(READ_FAILED);
  }
  P_DEBUG2("x10_read: reading %d bytes out of %d bytes\n",return_read,leng);


  if(check_poll)
    switch(buffer[0])
      {
	/* Is it a poll ? */
      case X10_INTERFACE_POLL:
	/* yes, we get the data, evaluate and decode it, and trigger whatever
	   event handler has been specified */
	P_DEBUG1("x10_read: receiving %x as INTERFACE_POLL\n",buffer[0]);
	return_complete=x10_complete_events(status);
	if(return_complete < 0)
	  return(return_complete);

	if(!return_complete)
	  return(x10_read(status,buffer,leng,useconds,check_poll));

	return(CHAT_INTERRUPTED);
	break;

      /* Did we get a power fail ? */
      case X10_POWER_FAIL:
	/* We set the date and time */
	P_DEBUG1("x10_read: receiving %x as POWER_FAIL\n",buffer[0]);
	return_complete=x10_set_status_interface(status);
	return(return_complete ? return_complete : CHAT_INTERRUPTED);
	break;
      }

  /* This wasn't a poll: we read the remaining bytes. Indeed, there's no poll
     to take care of, here, just the waited bytes */
  for(total_read=1;total_read<leng;total_read++)
    if(ret=ReadFile(status->fdinout, buffer+total_read, 1, &real_len, NULL) == FALSE)
      return(READ_FAILED);

  P_DEBUG2("x10_read: reading remaining %d bytes out of %d bytes\n",leng-1,leng);

  return(total_read);
}


/* X10_write is quite trivial */
int x10_write(struct x10_status_t *status,unsigned char *buffer,int leng)
{
  DWORD dwBytesWritten;
	WriteFile(status->fdinout, (LPCVOID)buffer, (DWORD)leng, &dwBytesWritten, 0);
  //Sleep(60); /* sleep for one second, else we may miss the next hail */
	return dwBytesWritten;
}