/* @(#)record_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 <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.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 *postproc_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;
char *curr_dirname=NULL;
char *curr_filename=NULL;

/* 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);
	int len_dirname,len_filename;
	struct stat stbuf;
	int ret;

	len_dirname=9+strlen(target_directory)+1;
	curr_dirname=realloc(curr_dirname, len_dirname);

	sprintf(curr_dirname, "%s/%04d%02d%02d",target_directory, today->tm_year+1900, today->tm_mon+1, today->tm_mday);
	printf("today: %s\n", curr_dirname);
	len_filename= strlen(curr_dirname)+ 10+ strlen(format_type) +1;
	curr_filename=realloc(curr_filename, len_filename);
	
	sprintf(curr_filename, "%s/%02d.%02d.%02d.%s", curr_dirname, today->tm_hour,today->tm_min, today->tm_sec, format_type);
	printf("filename: %s\n", curr_filename);

	ret=lstat(curr_dirname,&stbuf);
	if(-1 == ret){
		ret=errno;
		if(ENOENT == errno){
			puts("devo creare la directory");
			mkdir(curr_dirname, 0777);
			ret=lstat(curr_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", curr_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 status;
	int ret = kill(child,SIGTERM);
	if (-1 == waitpid(child,&status, 0)){
		printf("error in waitpid(%d)\n", child);
	}else{
		printf ("ucciso processo child %d (return %d)\n", child, ret);
		if(postproc_command){
			pid_t postcmd;
			postcmd = fork();
			if (postcmd){
				waitpid(postcmd,NULL,WNOHANG);
			}else{
				execlp(postproc_command,postproc_command, curr_filename,NULL);
			}
		}
	}
	/* 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;
  struct stat stbuf;
  int ret;
  
  /* 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'},
			  {"postproc-command", required_argument, 0, 'p'},
			  {"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:p: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'p':
		  postproc_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 [OPTIONS]\n", argv[0]);
		  printf("Options:\n"
				 "\n"
"    -S, --serial-device DEV        Use serial device DEV. (default: '%s')   \n"
"    -c, --rec-command CMD          Command file to use to register. The absolute path of the filename\n"
"                                   will be passed to CMD. (default: '%s')\n"
"    -t, --format-type FMT          Format type of the registration. (default: '%s')\n"
"    -d, --target-directory DIR     Specify the target directory containing the registrations.\n"
"                                   Each registration will be put in the directory DIR/YYYYMMDD\n"
"                                   where YYYY is the actual 4-digit year, MM is the actual 2-digit month,\n"
"                                   and DD is the actual 2-digit day.\n"
"                                   The filename will be called HH.MM.SS.FMT where HH: current\n"
"                                   hour, MM = current minute, SS = current second, and FMT = choosen \n"
"                                   encoding type. (default: '%s')\n"
"    -n, --nodaemon                 Do not detach from shell. Default is to run in background.\n"
"    -p, --postproc-command CMD     Postprocessing command. After the end of the registration,\n"
"                                   the CMD command will be started in background and the filename\n"
"                                   of the registration will be passed as argument.\n"
"\n", DEFAULT_serial_device, DEFAULT_rec_command, DEFAULT_format_type, DEFAULT_target_directory);
/* 		  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); */
/* 		  printf("Default postproc command: None\n"); */
		  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);

  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)
  

 */
