/* @(#)recorder_from_serial.c
 *
 *
 * 	Copyright (C) 2007 by Antonio Messina <antonio.messina@ictp.it> for the 
 * 	Abdus Salam International Center for Theoretical Phisics (ICTP). 
 *
 *
 *  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
 */

/* the main() function is abruptly copied from the
 * serial-programming-howto :) */

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>

#include <time.h>
#include <errno.h>

#include <unistd.h>
#define _GNU_SOURCE
#include <getopt.h>


#define DEFAULT_rec_command         "rec"
#define DEFAULT_format_type         "mp3"
#define DEFAULT_target_directory    "/tmp"
#define DEFAULT_serial_device       "/dev/ttyS0"
char *rec_command=NULL;
char *format_type=NULL;
char *target_directory=NULL;
char *serial_device=NULL;


  /* baudrate settings are defined in <asm/termbits.h>, which is
  included by <termios.h> */
#define BAUDRATE B9600
  /* change this definition for the correct port */

#define _POSIX_SOURCE 1 /* POSIX compliant source */

#define FALSE 0
#define TRUE 1

volatile int STOP=FALSE;
pid_t child;

/* TODO: una struttura per capire se qualcosa e' andato storto */
/* Per ora mi accontento di una stupida funzione signal() per settare
 * a 0 child nel caso il figlio muoia */
void dead_child(int n){
	if(SIGCHLD == n){
#ifdef DEBUG
		printf("Morto il figlio con pid %d\n", child);
#endif
		child = 0;
	}
}

void start_processing(){
/*   date = YYYYMMDD */
/*   file = YYYYMMDD/hhmm.avi */
/*   call mencoder -o file */
/*   (forka, e continua a leggere da seriale) */
	
	/* build directory */
	time_t today_t = time(NULL);
	struct tm *today = localtime(&today_t);
	
	char dirname[9+strlen(target_directory)+1];
	sprintf(dirname, "%s/%04d%02d%02d",target_directory, today->tm_year+1900, today->tm_mon+1, today->tm_mday);
	printf("today: %s\n", dirname);
	char filename[256];
	sprintf(filename, "%s/%02d.%02d.%02d.%s", dirname, today->tm_hour,today->tm_min, today->tm_sec, format_type);
	printf("filename: %s\n", filename);

	struct stat stbuf;
	int ret=lstat(dirname,&stbuf);
	if(-1 == ret){
		ret=errno;
		if(ENOENT == errno){
			puts("devo creare la directory");
			mkdir(dirname, 0777);
			ret=lstat(dirname,&stbuf);
			if(errno){perror("creazione directory ");}
		}else{
			printf("lstat returns errno %d\n", ret);
		}
	}

	if(S_ISDIR(stbuf.st_mode))
		puts("ok, e' directory");
	else
		puts("non e' directory");
	
	child = fork();
	/* DON'T FORGET TO AVOID ZOMBIES!!! */
	if(0 == child){
		/* Hi, I'm the child */
		/* exec the program */
		execlp(rec_command,rec_command, "-q", filename, NULL);
		puts("Something wrong");
	}else{
		/* Hi, I'm the parent */
		printf("child: %d\n", child);
		if (-1 == child){
			child = 0;
			/* qualcosa e' andato storto, fai qualcosa */
		}else{
			/* just return from the function. */
		}
	}
	
}

void end_processing(){
/* 	se hai un figlio, uccidilo */
	if(0 == child){ /* qualcosa di veramente strano e' successo. */ }
	int ret = kill(child,SIGTERM);
	printf ("ucciso processo child %d (return %d)\n", child, ret);
	/* se tutto e' andato bene imposta a 0 child */
	child = 0;
}

int main(int argc, char **argv)
{
  int fd, res;
  char buf[255];
  int want_daemonize=1;

  /* Set SIGCHILD handler */
  signal(SIGCHLD, dead_child);


  /* Parsing arguments */
  while(1){
	  static const struct option long_opts[] = 
		  {
			  {"serial-device", required_argument, 0, 'S'},
			  {"rec-command", required_argument, 0, 'c'},
			  {"format-type", required_argument, 0, 't'},
			  {"help", no_argument, 0, 'h'},
			  {"nodaemon", no_argument, 0, 'n'},
			  {"target-directory", required_argument, 0, 'd'},
			  {0,0,0,0}
		  };
	  int opt_index=0;
	  int c = getopt_long(argc,argv, "S:c:d:t:nh", long_opts, &opt_index);
	  if(-1 == c)
		  break;
	  switch(c){
	  case 'S':
		  serial_device=strdup(optarg);
		  break;
	  case'c':
		  rec_command=strdup(optarg);
		  break;
	  case't':
		  format_type=strdup(optarg);
		  break;
	  case'd':
		  target_directory=strdup(optarg);
		  break;
	  case 'n':
	    	  want_daemonize=0;
	    	  break;
	  default:
		  printf("Usage: %s [-S|--serial-device] [-c|--rec-command] [-d|--target-directory] [-n|--nodaemon]\n", argv[0]);
		  printf("Default serial device: %s\n", DEFAULT_serial_device);
		  printf("Default rec command: %s\n", DEFAULT_rec_command);
		  printf("Default encoding format: %s\n", DEFAULT_format_type);
		  printf("Default target_directory: %s\n", DEFAULT_target_directory);
		  return 0;
	  }
  }
  if(NULL == serial_device)
	  serial_device=strdup(DEFAULT_serial_device);
  if(NULL == rec_command)
	  rec_command=strdup(DEFAULT_rec_command);
  if(NULL == format_type)
	  format_type=strdup(DEFAULT_format_type);
  if(NULL == target_directory)
	  target_directory = strdup(DEFAULT_target_directory);

  struct stat stbuf;
  int ret=lstat(target_directory,&stbuf);  
  if(-1 == ret || !S_ISDIR(stbuf.st_mode)){
	  printf("Error: invalid target_directory %s: %s\n", target_directory,strerror(errno));	  
	  return EINVAL;
  }
  
/*
  Open modem device for reading and writing and not as controlling tty
  because we don't want to get killed if linenoise sends CTRL-C.
*/
#ifndef DEBUG
  struct termios oldtio,newtio;
 fd = open(serial_device, O_RDONLY | O_NOCTTY );
 if (fd <0) {perror(serial_device); return(-1); }
#ifndef DEBUG1
 tcgetattr(fd,&oldtio); /* save current serial port settings */
 bzero(&newtio, sizeof(newtio)); /* clear struct for new port settings */


/*
  BAUDRATE: Set bps rate. You could also use cfsetispeed and cfsetospeed.
  CRTSCTS : output hardware flow control (only used if the cable has
            all necessary lines. See sect. 7 of Serial-HOWTO)
  CS8     : 8n1 (8bit,no parity,1 stopbit)
  CLOCAL  : local connection, no modem contol
  CREAD   : enable receiving characters
*/
 newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;

/*
  IGNPAR  : ignore bytes with parity errors
  ICRNL   : map CR to NL (otherwise a CR input on the other computer
            will not terminate input)
  otherwise make device raw (no other input processing)
*/
/*  newtio.c_iflag = IGNPAR | ICRNL; */
 newtio.c_iflag = IGNBRK;

/*
 Raw output.
*/
 newtio.c_oflag = 0;

/*
  ICANON  : enable canonical input
  disable all echo functionality, and don't send signals to calling program
*/
/*  newtio.c_lflag = ICANON; */
 ;
/*
  initialize all control characters
  default values can be found in /usr/include/termios.h, and are given
  in the comments, but we don't need them here
*/
 newtio.c_cc[VINTR]    = 0;     /* Ctrl-c */
 newtio.c_cc[VQUIT]    = 0;     /* Ctrl-\ */
 newtio.c_cc[VERASE]   = 0;     /* del */
 newtio.c_cc[VKILL]    = 0;     /* @ */
 newtio.c_cc[VEOF]     = 4;     /* Ctrl-d */
 newtio.c_cc[VTIME]    = 5;     /* inter-character timer unused */
 newtio.c_cc[VMIN]     = 1;     /* blocking read until 1 character arrives */
 newtio.c_cc[VSWTC]    = 0;     /* '\0' */
 newtio.c_cc[VSTART]   = 0;     /* Ctrl-q */
 newtio.c_cc[VSTOP]    = 0;     /* Ctrl-s */
 newtio.c_cc[VSUSP]    = 0;     /* Ctrl-z */
 newtio.c_cc[VEOL]     = 0;     /* '\0' */
 newtio.c_cc[VREPRINT] = 0;     /* Ctrl-r */
 newtio.c_cc[VDISCARD] = 0;     /* Ctrl-u */
 newtio.c_cc[VWERASE]  = 0;     /* Ctrl-w */
 newtio.c_cc[VLNEXT]   = 0;     /* Ctrl-v */
 newtio.c_cc[VEOL2]    = 0;     /* '\0' */

/*
  now clean the modem line and activate the settings for the port
*/
 tcflush(fd, TCIFLUSH);
 tcsetattr(fd,TCSANOW,&newtio);
#endif //DEBUG1
/*
  terminal settings done, now handle input
  In this example, inputting a 'z' at the beginning of a line will
  exit the program.
*/
 /* DON'T FORGET TO DEMONIZE!!! */
 if(want_daemonize)
   daemon(0,0);
#else
 fd=0;
#endif
 puts("entering main loop");
 while (STOP==FALSE) {     /* loop until we have a terminating condition */
 /* read blocks program execution until a line terminating character is
    input, even if more than 255 chars are input. If the number
    of characters read is smaller than the number of chars available,
    subsequent reads will return the remaining chars. res will be set
    to the actual number of characters actually read */
    res = read(fd,buf,1);
    buf[res]=0;             /* set end of string, so we can printf */
    printf("read '%s'\n", buf);
	if('Q' == buf[0] && 0 == child)
		start_processing();
	if('R' == buf[0] && 0 != child)
		end_processing();
 }
 /* restore the old port settings */
#ifndef DEBUG
 tcsetattr(fd,TCSANOW,&oldtio);
#endif

 return 0;
}


/*
  Q == start
  R == end

  start:
  
  date = YYYYMMDD
  file = YYYYMMDD/hhmm.avi

  call mencoder -o file

  (forka, e continua a leggere da seriale)

  end:

  se hai un figlio mencoder, uccidilo (vedi se c'e' un modo pulito di chiuderlo)
  

 */
