/*
mkfifo.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/>.
*/
//MKF4ARDUINO to be defined for using this lib in arduino

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#ifndef MKF4ARDUINO
#include "mkfifo.h"
#include <unistd.h>
#endif


int _mkfprevious(int index) {
	int ret=index;
	ret--;
	if(ret<0) ret=MKF_MAXENTRIES -1;
	return ret;
	}

int _mkfnext(int index) {
	return (index+1)%MKF_MAXENTRIES;
	}

int mkfinit(mkf *mkf,char *filename) {
	mkf->magic=12345;
	mkf->head=0;
	mkf->tail=0;
	strncpy(mkf->filename,filename,MKF_SLEN);
	mkf->filename[MKF_SLEN-1]=(char)0;
#ifndef MKF4ARDUINO
	if(!sem_init(&mkf->sem,0,1)) return 0;
#endif
	return 1;
	}

#ifndef MKF4ARDUINO
int mkfload(mkf *mkf) {
	mkflock(mkf);
	FILE *f;
	char buf[MKF_BIGSTRING];
	// printf("fic=%s\n",mkf->filename);
	f=fopen(mkf->filename,"rt");
	if(!f) {mkfunlock(mkf); return 0;};
	while(fgets(buf,sizeof(buf),f)) {
		char *trimmer=&buf[strlen(buf)-1];
		if(*trimmer=='\n') *trimmer=(char)0;
		if(!_mkfwrite(mkf,buf)) {mkfunlock(mkf); return 0;};
		// printf("lecture et enregistrement %s OK \n",buf);
		};
	fclose(f);
	mkfunlock(mkf);
	return 1;
	}

int mkfsave(mkf *mkf) {
	mkflock(mkf);
	FILE *f;
	// printf("fic=%s\n",mkf->filename);
	f=fopen(mkf->filename,"wt");
	if(!f) {mkfunlock(mkf); return 0;};
	int slot=mkf->head;
	while(slot!=mkf->tail) {
		fputs(mkf->entry[slot],f);
		fputc((char)10,f);
		slot=_mkfnext(slot);
		};
	fclose(f);
	mkfunlock(mkf);
	return 1;
	}
#endif


int mkfcount(mkf *mkf) {
	mkflock(mkf);
	int ret=0;
	if(mkf->head <= mkf->tail) ret=mkf->tail - mkf->head;
	else ret= (MKF_MAXENTRIES - mkf->head) + mkf->tail +1;
	mkfunlock(mkf);
	return ret;
	}

int mkfsize(mkf *mkf) {
	mkflock(mkf);
	if(mkfempty(mkf)) {mkfunlock(mkf); return 0;};
	size_t ret=0;
	int slot=mkf->head;
	while(slot!=mkf->tail) {
		ret+=strlen(mkf->entry[slot]);
		slot=_mkfnext(slot);
		}; 
	mkfunlock(mkf);
	return ret;
	}

int mkfdebug(mkf *mkf) {
	fprintf(stderr,"magic=%d\nhead=%d\ntail=%d\n",mkf->magic,mkf->head,mkf->tail);
	}

int mkfdump(mkf *mkf, char*out,size_t n) {
	mkflock(mkf);
	*out=(char)0;
	int index=0;
	int slot=mkf->head;
	while(slot!=mkf->tail) {
		char *ptr=mkf->entry[slot];
		int len=strlen(ptr);
		//plus assez de place
		if(2+index+len>n) {mkfunlock(mkf); return 0;};
		// printf("\ntraitement slot %d",index);
		strcpy(&(out[index]),ptr);
		index+=len;
		out[index]=(char)10;
		out[++index]=(char)0;
		slot=_mkfnext(slot);
		};
	mkfunlock(mkf);
	return 1;
	}

int _mkfwrite(mkf *mkf,char *data) {
	int nextslot=_mkfnext(mkf->tail);
	// printf("\ninsertion dans slot=%d",mkf->tail);
	if(nextslot==mkf->head) return 0;
	char* ptr=(char*)malloc(strlen(data)+1);
	strcpy(ptr,data);
	mkf->entry[mkf->tail]=ptr;
	mkf->tail=nextslot;
	mkfunlock(mkf);
	return 1;
	}

int mkfwrite(mkf *mkf,char *data) {
	mkflock(mkf);
	int ret=_mkfwrite(mkf,data);
	mkfunlock(mkf);
	return ret;
	}

int mkfwritehead(mkf *mkf,char *data) {
	mkflock(mkf);
	int slot;
	slot=_mkfprevious(mkf->head);
	//plus de place
	if(slot==mkf->tail) {mkfunlock(mkf);return 0;};
	char* ptr=(char*)malloc(strlen(data)+1);
	strcpy(ptr,data);
	mkf->entry[slot]=ptr;
	mkf->head=slot;
	mkfunlock(mkf);
	return 1;
	}

int mkfempty(mkf *mkf) {
	return (mkf->head==mkf->tail);
	}

int mkfread(mkf *mkf,char *buf,size_t n) {
	mkflock(mkf);
	if(mkfempty(mkf)) {mkfunlock(mkf); return 0;};
	char *ptr=mkf->entry[mkf->head];
	strncpy(buf,ptr,n);
	buf[n]=(char)0;
	mkf->head++;
	mkf->head%=MKF_MAXENTRIES;
	free(ptr);
	mkfunlock(mkf);
	return 1;
	}

int mkfreadtail(mkf *mkf,char *buf,size_t n) {
	mkflock(mkf);
	if(mkfempty(mkf)) {mkfunlock(mkf); return 0;};
	mkf->tail=_mkfprevious(mkf->tail);
	char *ptr=mkf->entry[mkf->tail];
	strncpy(buf,ptr,n);
	buf[n]=(char)0;
	free(ptr);
	mkfunlock(mkf);
	return 1;
	}

char *mkfpeek(mkf *mkf) {
	if(mkfempty(mkf)) {return NULL;};
	return mkf->entry[mkf->head];
	}

char* mkfpeektail(mkf *mkf) {
	if(mkfempty(mkf)) {return NULL;};
	return mkf->entry[_mkfprevious(mkf->tail)];
	}

int mkfflush(mkf *mkf) {
	mkflock(mkf);
	int slot=mkf->head;
	while(slot!=mkf->tail) {
		free(mkf->entry[slot]);
		slot=_mkfnext(slot);
		};
	mkf->head=mkf->tail;
	mkfunlock(mkf);
	return 1;
	}

int mkfdelete(mkf *mkf) {
	mkfflush(mkf);
#ifndef MKF4ARDUINO
	unlink(mkf->filename);
	sem_destroy(&mkf->sem);
#endif
	return 1;
	}

int mkflock(mkf *mkf) {
#ifndef MKF4ARDUINO
	sem_wait(&mkf->sem);
#endif
	}

int mkfunlock(mkf *mkf) {
#ifndef MKF4ARDUINO
	sem_post(&mkf->sem);
#endif
	}

