/*
ardcom.c - Part of ardcom - Easy communication over USB with Arduino
Copyright 2013 - Laurent Menu-Kerforn
    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 3 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, see <http://www.gnu.org/licenses/>.
*/

//process/threads de communication avec arduino
/*
thread de lecture de arduino et remplissage fifo ffromard
thread ecriture de arduino et vidage ftoard
thread sauvegarde des fifo en local
thread socket
*/

// #include <select.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <termios.h>
#include <sys/select.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/time.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>

#include "ardcom.h"
#include "mkfifo.h"
// #define __MKOUT stdout
#include "mktools.c"

char ardtty[SLEN]=DEFAULTTTY;
char toardfifo[SLEN]=DEFAULTTOARDFIFO;
char fromardfifo[SLEN]=DEFAULTFROMARDFIFO;
char sockfile[SLEN]=DEFAULTSOCKET;
int fifosaveinterval=FIFOSAVEINTERVAL;
int fifopollinginterval=FIFOPOLLINGINTERVAL;
int verbose=0;

pthread_t t_readard,t_writeard,t_savefifo,t_sockaccess;

int fdard=0;
mkf ftoard,ffromard;
	
/* ==========================================
                 _   _   _        
 _ _ ___ __ _ __| | | |_| |_ _  _ 
| '_/ -_) _` / _` | |  _|  _| || |
|_| \___\__,_\__,_|  \__|\__|\_, |
                             |__/ 
*/
void* readard(void *arg) {
	if(verbose>1) printf("\nREADARD thread running");
	char message[MAXMESSAGESIZE+1];
	char rb;
	int cursor=0;
	ssize_t ret;
	while(ret=read(fdard,&rb,1)>0) {
		if(verbose>2) printf(" (c) ");
//		printf(" -%c (%d)- ",rb,(int)rb);
		message[cursor++]=rb;
		if(rb==(char)10) {
			//fin message
			message[cursor]=0;
			if(verbose) printf("\nFrom Arduino : >%s<",message);
			mkfwrite(&ffromard,message);
			cursor=0;
			}
		if(cursor>=MAXMESSAGESIZE) {
			errorf("BUFOVF","Buffer overflow");
			cursor=0;
			};
		};
	if(ret
	syserrorf("RDTTY","Error reading arduino.");
	pthread_exit(NULL);
	}

/* ==========================================
            _ _         _   _        
__ __ ___ _(_) |_ ___  | |_| |_ _  _ 
\ V  V / '_| |  _/ -_) |  _|  _| || |
 \_/\_/|_| |_|\__\___|  \__|\__|\_, |
                                |__/ 
*/
void* writeard(void *arg) {
	char message[MKF_BIGSTRING];
	size_t len,retlen;
	int stop=0;
	if(verbose>1) printf("\nWRITEARD thread running");
	useconds_t interval=fifopollinginterval*1000000;
	while(!stop) {
		if(verbose>2) printf("\nPolling ftoard fifo");
		if(mkfread(&ftoard,message,sizeof(message)-2)) {
			len=strlen(message);
			message[len++]=(char)10;
			message[len]=(char)0;
			retlen=write(fdard,message,len);
			if(verbose) printf("\nTo Arduino : >%s<",message);
			if(retlen!=len) {
				syserrorf("WRTTY","Error writing arduino");
				stop=1;
				};
			}
		else usleep(interval);
		};
	pthread_exit(NULL);
	}

/* ==========================================
                     __ _  __     
 ___ __ ___ _____   / _(_)/ _|___ 
(_-</ _` \ V / -_) |  _| |  _/ _ \
/__/\__,_|\_/\___| |_| |_|_| \___/
                                  
*/
void* savefifo(void *arg) {
	if(verbose>1) printf("\nSAVEFIFO thread running");
	useconds_t interval=fifosaveinterval*1000000;
	if(interval) {
		for(;;) {
			if(verbose>2) printf("\nSaving");
			mkfsave(&ffromard);
			mkfsave(&ftoard);
			usleep(interval);
			}
		};
	pthread_exit(NULL);
	}

/* ==========================================
             _       _   
 ___ ___  __| |_____| |_ 
(_-</ _ \/ _| / / -_)  _|
/__/\___/\__|_\_\___|\__|
                     
*/
void* sockaccess(void *arg) {
	if(verbose>1) printf("SOCK thread running");
	unsigned int fdserver, fdclient;
	struct sockaddr_un sockserver, sockclient;
	socklen_t soclen;
	char buf[MKF_BIGSTRING];

	fdserver=socket(AF_UNIX, SOCK_STREAM, 0);
	if(fdserver<0) sysexitf("NOSOCK","Can't create socket");

	memset(&sockserver, 0, sizeof(struct sockaddr_un));
	sockserver.sun_family = AF_UNIX;  /* local is declared before socket() ^ */
	strcpy(sockserver.sun_path,sockfile);
	unlink(sockserver.sun_path);
	if(bind(fdserver,(struct sockaddr *)&sockserver,sizeof(struct sockaddr_un))<0)
		sysexitf("NOSOCK","Can't bind socket");

	if(listen(fdserver,5)<0) 
		sysexitf("NOSOCK","Can't listen socket");

	for(;;) {
	soclen = sizeof(struct sockaddr_un);
	if((fdclient=accept(fdserver,(struct sockaddr *)&sockclient, &soclen))<0)
		sysexitf("NOSOCK","Can't accept connection");

	if(verbose>1) printf("\nConnection accepted");

	//quitter automat=99
	char automat='c';
	do {
		int stop=0,index=0;
		ssize_t len;
		if(verbose>2) printf("\n\nSTATE=%c\n\n",automat);
		switch(automat) {
		//Attente Commande
		case 'c':
			//recv(fdclient,buf,2,0);
			len=read(fdclient,buf,2);
			if(len>=0) {
				buf[len]=(char)0; 
				if(verbose>2) printf("\nReceived command=>%s<",buf);
				};
			if(len==0) {
				//fermeture socket coté client
				// warnf("RDSOCK","Closed socket");
				automat='q';
				break;
				};
			if(len<0) {
				//erreur sur accès client
				//printf("\nREAD ERROR len=%d\n",len);
				if(errno!=ECONNRESET) syserrorf("RDSOCK","(c) Socket read error");
				automat='q';
				break;
				};
			buf[2]=(char)0;
			if(!strcmp(buf,"RX")) automat='r';
			if(!strcmp(buf,"TX")) automat='t';
			break;
		case 'r':
			len=0;
			if(mkfread(&ffromard,buf,sizeof(buf)-2)) len=strlen(buf);
			buf[len]=(char)10;
			buf[len+1]=(char)0;
			//if(send(fdclient,buf,len+1,0)<0) {
			if(write(fdclient,buf,len+1)<0) {
				errorf("RDSOCK","Socket write failed");
				automat='q';
				}
			else automat='c';
			if(verbose) printf("\nRX %s",buf);
			break;
		case 't':
			index=0;
			do {
				// fprintf(stderr,"\nread char");
				// if((recv(fdclient,&buf[index],1,0))<=0) {
				if((read(fdclient,&buf[index],1))<=0) {
					errorf("RDSOCK","Can't collect message");
					automat='q';
					};
				if(buf[index]==(char)10) stop=1;
				index++;
				if(index>=sizeof(buf)) {
					errorf("RDSOCK","Message too long");
					automat='q';
					};
				}
			while (automat!='q' && !stop);
			if(automat=='q') break;
			buf[--index]=(char)0;
			mkfwrite(&ftoard,buf);
			if(verbose) printf("\nTX %s",buf);
			automat='c';
			break;
		default :
			automat='c';
		}
		
		}
	while(automat!='q');
	close(fdclient);

	} //fin boucle for(;;)

	pthread_exit(NULL);
	}


//====================================
/* ==========================================
            _      
 _ __  __ _(_)_ _  
| '  \/ _` | | ' \ 
|_|_|_\__,_|_|_||_|
                   
*/
main(int argc,char **argv) {
int index;
int c;
int restorefifo=0;
int quit=0; 
opterr = 0; //pas de raitement par defaut des options inconnues
char tag[SLEN];
 
while ((c=getopt(argc,argv,"VrSDhd:t:f:s:i:p:")) != -1)
	switch (c)
	{
	case 'i' :
		fifosaveinterval=atoi(optarg);
		if(fifosaveinterval<0) fifosaveinterval=FIFOSAVEINTERVAL;
		break;
	case 'p' :
		fifopollinginterval=atoi(optarg);
		if(fifopollinginterval<=0) fifopollinginterval=FIFOPOLLINGINTERVAL;
		break;
	case 'V':
		verbose++;
		break;
	case 'd':
		strcpy(ardtty,optarg);
		break;
	case 's':
		strcpy(sockfile,optarg);
		break;
	case 't':
		strcpy(toardfifo,optarg);
		break;
	case 'f':
		strcpy(fromardfifo,optarg);
		break;
	case 'r':
		restorefifo=1;
		break;
	case 'h':
		warnf("SYNT","Syntax : -h help -d <ard device> -r (restorefifo) -D (show Defaults) -S (show socket filename) -t <to ard fifo file> -f <from ard fifofile> -s <socket file> -V (verbose) -i <save interval (seconds)> -p <fifo polling interval (seconds)>");
		quit=1;
		break;
	case 'D' :
		printf("\nDEFAULTTTY=%s",DEFAULTTTY);
		printf("\nDEFAULTTOARDFIFO=%s",DEFAULTTOARDFIFO);
		printf("\nDEFAULTFROMARDFIFO=%s",DEFAULTFROMARDFIFO);
		printf("\nDEFAULTSOCKET=%s",DEFAULTSOCKET);
		printf("\nFIFOSAVEINTERVAL=%d",FIFOSAVEINTERVAL);
		break;
	case 'S' :
		printf("%s",DEFAULTSOCKET);
		quit=1;
		break;

	case '?':
		switch(optopt) {
		case 'd' :
			errorf("SYNT","-d missing device argument.");
			quit=1;
			break;
		default : 
			if(!quit) {
				sprintf(tag,"Incorrect option (%c), use -h for available options",optopt);
				errorf("SYNT",tag);
				};
			quit=1;
		};
		break;
	default:
		errorf("SYNT","Good shot : Unreachable error.");
		quit=1;		
	};
if(quit) {fprintf(stderr,"\n"); return 1;};

/*       for (index = optind; index < argc; index++)
         printf ("Non-option argument %s\n", argv[index]);
*/

fdard=open(ardtty,O_RDWR|O_NOCTTY);
if(fdard<=0) sysexitf("PBTTY","Can't open TTY");

usleep(ARDWAITBOOT);
tcflush(fdard,TCIOFLUSH); //vide le buffer

mkfinit(&ftoard,toardfifo);
mkfinit(&ffromard,fromardfifo);
if(restorefifo) {
	mkfload(&ffromard);
	mkfload(&ftoard);
	warnf("RESTORE","Restore saved fifos");
	};
if(verbose) printf("\nVerbosity level: %d",verbose);

if(pthread_create(&t_readard,NULL,readard,0)) sysexitf("THREADPB","Can't run thread");
if(pthread_create(&t_writeard,NULL,writeard,0)) sysexitf("THREADPB","Can't run thread");
if(pthread_create(&t_savefifo,NULL,savefifo,0)) sysexitf("THREADPB","Can't run thread");
if(pthread_create(&t_sockaccess,NULL,sockaccess,0)) sysexitf("THREADPB","Can't run thread");

pthread_exit(NULL);
return 0;	
}

