/*
  
  irtext.c		- Jan Wagemakers -
  
  With this program you can send text to my PIC16F84 Infra-Red Receiver. 
  You can find more information at <http://www.janw.easynet.be/index.html>
  
  This program is based on :
  
      rc -  application for sending IR-codes via lirc
      Copyright (C) 1998 Christoph Bartelmus (lirc@bartelmus.de)
      
  --------------------------------------------------------------------------    
  
  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  
*/

#define DEVDIR                  "/dev"
#define DEV_LIRCD               "lircd"
#define LIRCD                   DEVDIR "/" DEV_LIRCD

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <getopt.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <errno.h>
#include <signal.h>
#include <limits.h>

#define PACKET_SIZE 256
/* three seconds */
#define TIMEOUT 3

int timeout = 0;
char *progname;

void
sigalrm (int sig)
{
  timeout = 1;
}

void
usage ()
{
  puts ("Usage : irtext [OPTION] TEXT ");
  puts ("");
  puts (" -cls           : clear screen");
  puts (" -line x        : goto line x");
  puts (" -time h m s    : set time hour min sec");
  puts (" -date d m c y  : set date day month century year");
  puts ("");
  puts ("Examples :");
  puts ("");
  puts (" irtext -date 17 12 20 03    : set date to 17/12/2003 ");
  puts (" irtext -cls -line 3 Test    : clear screen,");
  puts ("                               goto line 3,");
  puts ("                               print Test");
  puts ("");
  exit (EXIT_FAILURE);
}

const char *
read_string (int fd)
{
  static char buffer[PACKET_SIZE + 1] = "";
  char *end;
  static int ptr = 0;
  ssize_t ret;

  if (ptr > 0)
    {
      memmove (buffer, buffer + ptr, strlen (buffer + ptr) + 1);
      ptr = strlen (buffer);
      end = strchr (buffer, '\n');
    }
  else
    {
      end = NULL;
    }
  alarm (TIMEOUT);
  while (end == NULL)
    {
      if (PACKET_SIZE <= ptr)
	{
	  fprintf (stderr, "%s: bad packet\n", progname);
	  ptr = 0;
	  return (NULL);
	}
      ret = read (fd, buffer + ptr, PACKET_SIZE - ptr);

      if (ret <= 0 || timeout)
	{
	  if (timeout)
	    {
	      fprintf (stderr, "%s: timeout\n", progname);
	    }
	  else
	    {
	      alarm (0);
	    }
	  ptr = 0;
	  return (NULL);
	}
      buffer[ptr + ret] = 0;
      ptr = strlen (buffer);
      end = strchr (buffer, '\n');
    }
  alarm (0);
  timeout = 0;

  end[0] = 0;
  ptr = strlen (buffer) + 1;
#       ifdef DEBUG
  printf ("buffer: -%s-\n", buffer);
#       endif
  return (buffer);
}

enum packet_state
{
  P_BEGIN,
  P_MESSAGE,
  P_STATUS,
  P_DATA,
  P_N,
  P_DATA_N,
  P_END
};

int
send_char (int fd, const char *packet)
{

  /* DEBUG puts (packet); */

  if (send_packet (fd, packet) == -1)
    {
      exit (EXIT_FAILURE);
    }
}

int
send_packet (int fd, const char *packet)
{
  int done, todo;
  const char *string, *data;
  char *endptr;
  enum packet_state state;
  int status, n;
  unsigned long data_n = 0;

  todo = strlen (packet);
  data = packet;
  while (todo > 0)
    {
      done = write (fd, (void *) data, todo);
      if (done < 0)
	{
	  fprintf (stderr, "%s: could not send packet\n", progname);
	  perror (progname);
	  return (-1);
	}
      data += done;
      todo -= done;
    }

  /* get response */
  status = 0;
  state = P_BEGIN;
  n = 0;
  while (1)
    {
      string = read_string (fd);
      if (string == NULL)
	return (-1);
      switch (state)
	{
	case P_BEGIN:
	  if (strcasecmp (string, "BEGIN") != 0)
	    {
	      continue;
	    }
	  state = P_MESSAGE;
	  break;
	case P_MESSAGE:
	  if (strncasecmp (string, packet, strlen (string)) != 0 ||
	      strlen (string) + 1 != strlen (packet))
	    {
	      state = P_BEGIN;
	      continue;
	    }
	  state = P_STATUS;
	  break;
	case P_STATUS:
	  if (strcasecmp (string, "SUCCESS") == 0)
	    {
	      status = 0;
	    }
	  else if (strcasecmp (string, "END") == 0)
	    {
	      status = 0;
	      return (status);
	    }
	  else if (strcasecmp (string, "ERROR") == 0)
	    {
	      fprintf (stderr, "%s: command failed: %s", progname, packet);
	      status = -1;
	    }
	  else
	    {
	      goto bad_packet;
	    }
	  state = P_DATA;
	  break;
	case P_DATA:
	  if (strcasecmp (string, "END") == 0)
	    {
	      return (status);
	    }
	  else if (strcasecmp (string, "DATA") == 0)
	    {
	      state = P_N;
	      break;
	    }
	  goto bad_packet;
	case P_N:
	  errno = 0;
	  data_n = strtoul (string, &endptr, 0);
	  if (!*string || *endptr)
	    {
	      goto bad_packet;
	    }
	  if (data_n == 0)
	    {
	      state = P_END;
	    }
	  else
	    {
	      state = P_DATA_N;
	    }
	  break;
	case P_DATA_N:
	  fprintf (stderr, "%s: %s\n", progname, string);
	  n++;
	  if (n == data_n)
	    state = P_END;
	  break;
	case P_END:
	  if (strcasecmp (string, "END") == 0)
	    {
	      return (status);
	    }
	  goto bad_packet;
	  break;
	}
    }
bad_packet:
  fprintf (stderr, "%s: bad return packet\n", progname);
  return (-1);
}

main (int arg_aantal, char *arg_tekst[])
{

  int teller, cteller, ascii, speciaal;
  int fd, space;

  char unsigned *kar;
  char dummy[PACKET_SIZE + 1];

  struct sockaddr_un addr;
  struct sigaction act;

  if (arg_aantal < 2)
    {
      usage ();
    }

  act.sa_handler = sigalrm;
  sigemptyset (&act.sa_mask);
  act.sa_flags = 0;		/* we need EINTR */
  sigaction (SIGALRM, &act, NULL);

  addr.sun_family = AF_UNIX;
  strcpy (addr.sun_path, LIRCD);
  fd = socket (AF_UNIX, SOCK_STREAM, 0);
  if (fd == -1)
    {
      fprintf (stderr, "%s: could not open socket\n", progname);
      perror (progname);
      exit (EXIT_FAILURE);
    };
  if (connect (fd, (struct sockaddr *) &addr, sizeof (addr)) == -1)
    {
      fprintf (stderr, "%s: could not connect to socket\n", progname);
      perror (progname);
      exit (EXIT_FAILURE);
    };

  space = 0;
  for (teller = 1; teller < arg_aantal; teller = teller + 1)
    {

      speciaal = 0;

      if (space == 0)
	{
	  space = 1;
	}
      else
	{
	  sprintf (dummy, "send_once lcd ascii32\n");
	  send_char (fd, dummy);
	}
      if (strcmp (arg_tekst[teller], "-h") == 0)
	{
	  usage ();
	}
      if (strcmp (arg_tekst[teller], "-cls") == 0)
	{
	  speciaal = 255;
	  space = 0;

	  sprintf (dummy, "send_once lcd ascii0\n");
	  send_char (fd, dummy);
	  send_char (fd, dummy);

	}
      if (strcmp (arg_tekst[teller], "-line") == 0)
	{
	  speciaal = 1;
	  space = 0;

	  sprintf (dummy, "send_once lcd ascii0\n");
	  send_char (fd, dummy);

	}
      if (strcmp (arg_tekst[teller], "-time") == 0)
	{
	  speciaal = 3;
	  space = 0;

	  sprintf (dummy, "send_once lcd ascii0\n");
	  send_char (fd, dummy);
	  sprintf (dummy, "send_once lcd ascii5\n");
	  send_char (fd, dummy);

	}
      if (strcmp (arg_tekst[teller], "-date") == 0)
	{
	  speciaal = 4;
	  space = 0;

	  sprintf (dummy, "send_once lcd ascii0\n");
	  send_char (fd, dummy);
	  sprintf (dummy, "send_once lcd ascii6\n");
	  send_char (fd, dummy);
	}
      if (speciaal == 0)
	{
	  for (cteller = 0; cteller < strlen (arg_tekst[teller]);
	       cteller = cteller + 1)
	    {
	      kar = arg_tekst[teller] + cteller;

	      sprintf (dummy, "send_once lcd ascii%d\n", *kar);
	      send_char (fd, dummy);
	    }
	}
      else
	{
	  if (speciaal < 255)
	    {
	      for (cteller = 0; cteller < speciaal; cteller = cteller + 1)
		{
		  teller++;
		  if (teller >= arg_aantal)
		    {
		      usage ();
		    }
		  ascii = atoi (arg_tekst[teller]);
		  sprintf (dummy, "send_once lcd ascii%d\n", ascii);
		  send_char (fd, dummy);
		}
	    }
	}
    }
}