#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <strings.h>
#include <sys/ioctl.h>
#include <signal.h>
#include <string.h>
#include <stdint.h>
#include <time.h>
#include "lib_sql.h"


#define DEVICE "/dev/ttyUSB0"


FILE* fichier;
int interrupt=0;

// Serial port initialisation
struct termios saveterm;

int init_serial(char *device,int speed){
	struct termios new;
	int fd=open(device,O_RDWR|O_NOCTTY); 
	if(fd<0){perror(device); exit(-1);}
	tcgetattr(fd,&saveterm); /* save current port settings */
	bzero(&new,sizeof(new));

	int fspeed;
	switch(speed){
	  case 1200: fspeed=B1200; break;
	  case 2400: fspeed=B2400; break;
	  case 4800: fspeed=B4800; break;
	  case 9600: fspeed=B9600; break;
	  case 19200: fspeed=B19200; break;
	  case 38400: fspeed=B38400; break;
	  case 57600: fspeed=B57600; break;
	  case 115200: fspeed=B115200; break;
	  default: fprintf(stderr,"Unknown serial speed %d.\n",speed); exit(-1);
	  }

	new.c_cflag=CLOCAL|CREAD|fspeed|CS8;
	new.c_iflag=0;
	new.c_oflag=0;
	new.c_lflag=0;  /* set input mode (non-canonical, no echo,...) */
	new.c_cc[VTIME]=0; /* inter-character timer unused */
	new.c_cc[VMIN]=1; /* blocking read until 1 char received */
	tcflush(fd, TCIFLUSH);
	tcsetattr(fd,TCSANOW,&new); //applications des nouveaux paramètres sur le port série.
	int status;
	ioctl(fd,TIOCMGET,&status); //récupération des signaux associés au port série
	status |=TIOCM_DTR; //empêcher le passage du DTR à 0
	ioctl(fd,TIOCMSET,&status); //appliquer les signaux modifiés
	return fd;
}

// Close serial port

void close_serial(int fd){
	tcsetattr(fd,TCSANOW,&saveterm);
	int status;
	ioctl(fd,TIOCMGET,&status);
	status |=TIOCM_DTR;//mettre le DTR à 1
	ioctl(fd,TIOCMSET,&status);
	close(fd);
}


//fonction de delay en ms, pour ne pas surcharger le port série.
void my_delay(int i)    /*Pause l'application pour i ms*/
{
    clock_t start,end;
    start=clock();
    while(((end=clock())-start)<=((i*CLOCKS_PER_SEC)/1000));
}

//cette fonction (bloquante) attend une trame API en provenance du Xbee branché sur le port série.
int detect_frame(int fd)
{ 
	int state;
	unsigned long long int adresse64=0; //adresse physique émettrice du paquet
	int adresse16=0;                    //adresse logique émettrice du paquet
	//ci dessous les champs pour le découpage de la trame API 
	int bytes_number,length;            
	char msb=0;
	char lsb=0;
	unsigned char c=0;
	unsigned char frame_type;
	unsigned char data_type;
	unsigned char adress_char[8];
	int verify_checksum=0;
	float temperature=0;
	//attente du délimiteur 0x7E (le caractère de début de trame)
	while(c!=0x7E) 
	{
		bytes_number=read(fd,&c,1);	
	}
	printf("    Frames detected\n");
		
	bytes_number=read(fd,&msb,1);
	//si le caractère lu pour le champ msb est 0x7d alors msb est échappé, donc c'est à la prochaine 
	//lecture que la valeur de ce champ est accessible (sous réserve de procéder à un OU exclusif avec cette valeur et 0x20 )
	//il en sera de même pour tout les champs de la trame.
	if(msb==0x7d){
		//le caractère suivant msb doit être échappé
	bytes_number=read(fd,&msb,1);
	msb=msb^0x20;
	}
	bytes_number=read(fd,&lsb,1);
	if(lsb==0x7d){
	bytes_number=read(fd,&lsb,1);
	lsb=lsb^0x20;
	}  
	//taille paquet	
	length=(msb<<8)+lsb;
	printf("%d %d ---> %d\n",msb,lsb,length);
	printf("    #  Longueur du packet %d\n",length);	
	  int i=0; //indice champ de la trame
	  int xored=0; //booléen pour l'échappement des caractères.
	while(i<length)  
	{   
		bytes_number=read(fd,&c,1);
		if(c==0x7D){  //le caractère suivant est échappé
				xored=1;
				continue; //on incrémente pas le i on passe direct au prochain caractère et on le décode
		}
		if(xored){
			//décodage
			c=c^0x20;
			xored=0;
		}
		//calcul du checksum: ajouter tout les champs à partir du champ "length"
		//les 8 premiers bits de poids faible de cette somme devront être égal à 0xFF.
		verify_checksum+=c;
		if(i==0){
			printf("    #  API identifier %X\n",(int)c);
			printf("    #  (0x90 Receive Frame -- 0x8B Transmit Status)\n");	
			frame_type=c;
		}
	
		if(frame_type==0x90){//si trame de réception
			state=0;
			//recupération adresse physique
			if((i>0)&&(i<9)){
				adresse64+=((unsigned long long int)(c))<<(64-(8*i));
				adress_char[i-1]=c;			
			}
			
			if(i==9){
				int j;
				for(j=0;j<8;j++){
					printf("0x%X ",adress_char[j]);				
				}
				printf("\n");				
				printf("    #  Source MAC address %llu .\n",adresse64);
				int x;
				unsigned char address_frame[8];	
				printf("Adresse décodée : ");				
				for(x=0;x<8;x++){
				address_frame[x]=((adresse64)>>(64-(8*(x+1))))&0x00000000000000FF;
				//frame[5+x]=address_frame[x];
				printf("%X",(int)address_frame[x]);				
				}	
				printf("  end\n");
				adresse16=c<<8;
			}
			//recupération adresse logique
			if(i==10){
				adresse16+=c;
				printf("    #  Source MY address %x.\n",adresse16);
			}
			//récupération des données
			if(i>11){
				if ((i==12)&&((int)c==0x02)){//si la trame reçue est une trame d'ajout capteur (premier bit de données égal à 0x02)
					int codeRetour;
					printf("Ajout d'un capteur !\n");
					char adresse_string[20];
					//ajout des fichiers dans le fichier de configuration (liste des périphériques à ajouter dans la base de donnée)
					fichier=fopen("/var/www/projet/include/new_modul.conf","a");
					sprintf(adresse_string,"%llu",adresse64);
					printf("Chaine %s\n",adresse_string);	
					codeRetour = fputs(adresse_string, fichier);
					fputs(";",fichier);//délimiteur
					fclose(fichier);					
					}
				
				
				printf("/n	 #  Données :  %x\n",(int)c);
				//si trame de mesure (3 octets dans le champ de données)
				if(i==13){
				temperature=c; //valeur entière de la température
				}			
				
				if(i==14)
				{temperature+=c/10;//valeur décimale de la température
				printf("	Température %f\n",temperature);
	
				//insertion dans la base de donnée de la mesure.
				char adresse_string[20];
				//conversion de l'adresse logique en chaine de caractère
				sprintf(adresse_string,"%llu",adresse64);
				insert_temp2DB(adresse_string,temperature);
				}
			}
			}

			//trame de statut de transmission
			if(frame_type==0x8b){
				if(i==1){
				printf("	#  Frame ID %x ....",c);
				}
				if(i==2){
					
					adresse16=c<<8;
				}
				if(i==3){
					adresse16+=c;
					printf("    #  Source MY address %x.\n",adresse16);
				}
				if((i==5)&&(c==0x00)){
					printf("   ...transmise avec succés\n");
					state=1;	
				} else{
				state=-2;//échec de la transmission
				}
				if(i>3){
					printf("\n Données :\n");
				}
				
				}
				if(frame_type==0x8A){
					printf("\n Content : %X\n",(int)c);
				}
				if(frame_type==0x8c){
					printf("\nContent :  %X\n",(int)c);
				}

	i++;
	}
	
  //lecture du checksum de la trame
  bytes_number=read(fd,&c,1);
  verify_checksum+=c;
  //test intégrité de la trame.
  if((verify_checksum&0x00FF)!=0x00FF){
	printf("Erreur dans le paquet\n");
	state=-1;  
	}
return state;
}
//Cette fonction permet d'envoyer un caractère dans le port série pointé par fd.
//Elle échappe le caractère si besoin.
void escape_and_send(unsigned char c,int fd){
	unsigned char d=c;
	unsigned char escape=0x7D;
	unsigned char escaped;		
	//test de la b=nécessité d'échapper le caractère
	if((d==0x7E)||(d==0x7D)||(d==0x11)||(d==0x13)){
	my_delay(20);	
	write(fd,&escape,1);
	escaped=d^0x20;
	my_delay(20);
	write(fd,&escaped,1);		
	}else
	{
	my_delay(20);	
	write(fd,&d,1);
	}
}


//Cette fonction permet d'envoyer un couple (mesure, consigne) à un chauffage OU une consigne manuelle (confort, arrêt) à un chauffage
void TXrequest_API(int fd,float mesure,float consigne,int manuel,uint64_t address)
{     
	unsigned char length;
	unsigned char address_frame[8];
	length=19://pour 5 octets de données.
	long long int checksum=0;
	//             Delimiter, MSB , LSB ,API types, ID frame                                                //manuel,mesure,consigne
	unsigned char frame[21]={0x00,length,0x10,0x02,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xFF,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
	
	//decoupage address
	int j;	
	for(j=0;j<8;j++){
		address_frame[j]=((address)>>(64-(8*(j+1))))&0x00000000000000FF;
		frame[4+j]=address_frame[j];	
		//printf("%X ",(int)frame[4+j]);
	}
	if(manuel!=0x00){
	frame[16]=manuel;
	length=15;
	}
	
	else{
		
		//manuel =0x01 confort 
		//manuel =0x02 arret
		frame[16]=manuel;
		frame[17]=(char)(mesure/1); // partie entière de la température
		frame[18]=(char)((mesure-(mesure/1))*10); // premier digit de la température 
		frame[19]=(char)(consigne/1); // partie entière de la consigne
		frame[20]=(char)((consigne-(consigne/1))*10); // premier digit de la consigne
		length=19;
	}
	//envoi du delimiter
	unsigned char envoi;
	envoi=0x7e;
	write(fd,&envoi,1);
	//envoi de la trame
        printf("envoi:\n");
	for(j=0;j<=(length+1);j++){
		envoi=frame[j];
		escape_and_send(envoi,fd);
		printf("%X ",(int)(frame[j]));
		if(j>1)
		{   //fabrication checksum
			checksum+=frame[j];
		}
	}

checksum=(0xFF-(checksum&0xFF));
envoi=(char)(checksum);	 
write(fd,&envoi,1);
printf("%X \n",(int)(envoi));
}

int main(){

/* Initialisation */
int fd=init_serial(DEVICE,9600);

Recup_Info_SQL("/var/www/projet/include/database.params.inc");

while(1){
switch(detect_frame(fd)){
case 1:  // bonne reception
	break;
case 0:  // paquet reçu
	break;
case -1: // problème de CRC dans le paquet reçu les informations, sont peut-être corrompues
	 // ne pas en prendre compte
	 break;
case -2: // paquet non envoyé 
	 
	 break;
default: // 
         // 
	break;
}

}
close_serial(fd);


return 0;
}
