/* 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 <stdlib.h>

#include "error.h"
#include "cm12a.h"
#include "proto.h"

DWORD WINAPI RingThreadProc( LPVOID lpParam );

/* We just fill the objects tab with X10_OBJECT_UNKNOWN to tell every status
   is unknown */
static void x10_init_status_objects(unsigned char obj_status,time_t t_stamp,
				    struct x10_status_t *status)
{
  register int house,number;

  for(house=0;house<16;house++)
    for(number=0;number<16;number++)
    {
      status->objects[house][number].status = obj_status;
      status->objects[house][number].timestamp = t_stamp;
    }
}


/* This is an API function. It's here to log every X10 event monitored on the AC bus, as
   well as API-triggered command. One should call it from the event hook function 
   passed to x10_open(), to get a status of the whole as accurate as possible. */
void x10_log_event(char house_code,int unit_code,int cmd,struct x10_status_t *status)
{
  int house,unit;

  /* ALL_UNIT_OFF command: self explains :-). NOTE that ALL_LIGHTS_ON and
     ALL_LIGHTS_OFF are not taken into account because we can't know which
     modules are affected and which are not (light or not light ?). So, although debatable,
     it's probably better to ignore it */
  if(cmd == X10_CMD_ALL_UNITS_OFF)
    {
      x10_init_status_objects(X10_OBJECT_OFF,time(NULL),status);
      return;
    }

  /* offsets for the object and timestamp for the event */
  house=house_code-'A';
  unit=unit_code-1;
  status->objects[house][unit].timestamp = time(NULL);

  switch(cmd)
    {
    case X10_CMD_ON:
      status->objects[house][unit].status = X10_OBJECT_ON;
      break;

    case X10_CMD_OFF:
      status->objects[house][unit].status = X10_OBJECT_OFF;
      break;

    case X10_CMD_DIM:
    case X10_CMD_BRIGHT:
      status->objects[house][unit].status = X10_OBJECT_DIM_BRIGHT;
      break;

    default:
      break;
    }
}


/* We init the API layer. We take the device name, the error function and the
   event handler and a default timeout for every read operation on the serila port */
struct x10_status_t *x10_open(char *device_name,
			      void (*error_function)(int,char *,struct x10_status_t *),
			      void (*function_hook) (unsigned char,int,int,int,struct x10_status_t *),
			      int timeout_chat, void* param)
{
  HANDLE fdinout;
  struct x10_status_t *status;
  DWORD dwErr;
  void (*adopted_error_function)(int,char *,struct x10_status_t *);

  /* We take the standard error function or the user provided one if it exists */
  if(error_function)
    adopted_error_function=error_function;
  else
    adopted_error_function=x10_default_error;

  /* open the port, check for errors */
  if((fdinout = x10_open_tty_port(device_name)) == INVALID_HANDLE_VALUE)
  {
    dwErr = GetLastError();
    adopted_error_function(dwErr,"in X10_open",NULL);
    return(NULL);
  }

  if(!(status=malloc(sizeof(struct x10_status_t))))
  {
    adopted_error_function(MALLOC_FAILED,"in X10_open",NULL);
    return(NULL);
  }

  /* We fill the status structure */
  status->fdinout=fdinout;
  status->timeout_chat=timeout_chat;
  status->error=adopted_error_function;
  status->function_hook=function_hook;

  /* and the last_* */
  status->last_house_code='Z';
  status->last_unit_code=0;
  status->last_func_code=16;
  status->param = param;

  /* and we init the objects */
  x10_init_status_objects(X10_OBJECT_UNKNOWN,0,status);
  
  x10_get_status_interface(status);
  return(status);
}


/* Just close the port and free memory */
void x10_close(struct x10_status_t *status)
{
  x10_close_tty_port(status->fdinout);
  status->fdinout = NULL;
  free(status);
}