/* Todo list:
in db.c if max_open > 50 decrease it to 50
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>  
#include <errno.h>  
#ifndef isunix
#include <process.h>  
#include <io.h>
#define pipe_create(a,b,c) _pipe(a,b,c)
#else
#define pipe_create(a,b,c) pipe(a,b)
#ifndef _O_BINARY
#define _O_BINARY 0
#endif
#endif    
#include "pipelib.h"
#include "emsg.h"
 
#ifndef FALSE
#define TRUE (!FALSE)
#define FALSE 0
#endif

/*--------------------------------------------------------------*/
/* Pipe library used by db.c library */
/*--------------------------------------------------------------*/
static int pih[100];
static char *pi_name[100];
static int pi_isnew[100];
static int pi_gid[100];
static int pi_num[100];
static int plast[10];
static int nwrite;

#define pi_hash(x) ((x) % nwrite)
static int pi_working;

static int pread[10],pwrite[10];
char *pi_status(void)
{
 	static char bf[BFSZ];
	sprintf(bf,"Pipes %d %d",nwrite,pi_working);
	return bf;
}
int pi_init(int n) /*open n writing processes */
{
	int i;
	char cmd[CMD_SIZE];
	pi_working = TRUE;
	if (n==0) {pi_working = FALSE; return TRUE;}
	if (n>5) n = 5;
	for (i=0; i<n; i++) {
		if (!pcreate(&pread[i],&pwrite[i])) {
		 	pread[i] = -1;
			pwrite[i] = -1;
			pi_working = FALSE;
		} else {	
		 	emsg("Checking version of writing process\n");
			if (pipe_read(pread[i],cmd,CMD_SIZE) < CMD_SIZE) {
			 	pi_working = FALSE; return FALSE;
			}
			if (strcmp(cmd,"Writer 1.0")!=0) {
			 	emsg("Wrong version of writer process %s\n",cmd);
				pi_working = FALSE;
				return FALSE;
			}
		}

	} 	
	nwrite = n;
	return TRUE;
}
#define PIPE_BIG 60000
char *file_config(char *s);
char *pipe_prog(void)
{
#ifdef isunix
	return file_config("dnews");
#else
	return file_config("pchild.exe");
#endif
}
int pcreate(int *r, int *w)
{
	int ph,p;
	char cread[10],cwrite[10];
	int pread[2],pwrite[2];
	if (pipe_create(pread,3000,_O_BINARY)!=0) { perror("Could not create pipes"); return FALSE; }
	if (pipe_create(pwrite,PIPE_BIG,_O_BINARY)!=0) { perror("Could not create pipes"); return FALSE; }
	sprintf(cread,"%d",pwrite[0]);
	sprintf(cwrite,"%d",pread[1]);
	emsg("Creating process %s %s\n",cread,cwrite);
#ifdef isunix
	p = fork();
	if (p<0) {filemsg("Fork() failed\n"); return FALSE;}
	if (p==0) {
		pipe_child(atoi(cread),atoi(cwrite)); 	
		/* Should disable dnews exit handler before doing this */ 
	 	exit(0);
	}
#else
	ph = _spawnl(_P_NOWAIT ,pipe_prog(),pipe_prog(),cread,cwrite,NULL);
	if (ph<=0) {perror("spawnl()"); return FALSE;}
#endif
	*w = pwrite[1];
	*r = pread[0];
	return TRUE;
}
void pi_end(void)
{
	char cmd[CMD_SIZE];
	int i;
	for (i=0; i<nwrite;i++) {
		strcpy(cmd,"exit");
		if (pipe_write(pwrite[i],cmd,CMD_SIZE)<CMD_SIZE) {
			filemsg("pi_end failed"); 
		}
	} 	
}
int pi_nexth(void)
{
        int i;
	static int done_init;
	if (!done_init) {
	 	for (i=0; i<100; i++) pih[i] = -1;
		done_init = TRUE;
	}
        for (i=0; i<100; i++) {
		if (pi_name[i]==NULL) pi_name[i] = malloc(BFSZ);
                if (pih[i]<0) return i;
        }
	if (i>90) {
	        emsg("No free handles for pi file routines, open  \n");
	        return 0;
	} 	 	
}
int pi_open(const char *fname, int isnew, int gid, int num)
{
	int f = pi_nexth();
	strncpy(pi_name[f],fname,200);
	pih[f] = f;
	pi_isnew[f] = isnew;
	pi_gid[f] = gid;
	pi_num[f] = num;
	return f;
}
int pi_close(h)
{
 	pih[h] = -1;
	return 0;
}

int pi_write(int h, char *bf, int len, int flen)
{
	int f;
	char cmd[CMD_SIZE];
    	if (pih[h]== -1) { emsg("pi_write: File not open {%d} \n",h); return 0;}
	if (len==0) return 0;
	f = pwrite[pi_hash(pi_gid[h])];
	if (f<0) {emsg("Pi_write failed %d",f); return 0;}
	plast[pi_hash(pi_gid[h])] = pi_gid[h];
	sprintf(cmd,"write %d %d %d %d %d %s",pi_isnew[h],len,pi_gid[h],pi_num[h],flen,pi_name[h]);
	if (pipe_write(f,cmd,CMD_SIZE)<CMD_SIZE) return 0;
	return pipe_write(f,bf,len);
}
void pi_sync_all(void)
{
	int i;
	if (!pi_working) return;
	for (i=0; i<nwrite; i++) pi_sync(plast[i]);
}
int lib_gmtime(void);
int stats_clock(void);
void stats_add(char *s, int t, int t2);
int pi_sync(int gid)
{
	char bf[BFSZ];
	char cmd[BFSZ];
	int c = pi_hash(gid);
	char *s;
	int start_time;
	int start_clock;


	if (plast[c]==gid) {
		start_time = lib_gmtime();
		start_clock = stats_clock();

		emsg("Syncing with reader process %d\n",c);
		sprintf(cmd,"sync");
		if (pipe_write(pwrite[c],cmd,CMD_SIZE)<CMD_SIZE) {
			filemsg("pi_sync failed"); return FALSE;
		}
again:		pipe_read(pread[c],bf,CMD_SIZE);
		s = strtok(bf," "); if (s==NULL) goto fail;
		strcpy(cmd,s);
		s = strtok(NULL,"\n"); if (s==NULL) goto fail;

		plast[c] = -1;
		if (strcmp(cmd,"ok")==0) {
			stats_add("psync",start_time,start_clock);
			return TRUE;
		}
		if (strcmp(cmd,"error")==0) {
		 	emsg("Write error [%d] %s\n",s);
			goto again;
		}
		if (strcmp(cmd,"fixlength")==0) {
			db_fixlength(s);
			goto again;
		}
		goto fail;
	}
	return TRUE;
fail:
      	emsg("Pipe returned bad data\n");
	return 0;
}



/*--------------------------------------------------------------*/
/* Library for db.c to use directly with or without pipes */
/*--------------------------------------------------------------*/
enum {H_FILE,H_PIPE};
static int myh[300];
static int myh_type[300];
static int mynopen;
void myfreeh(int h)
{
 	myh[h] = -1;
	mynopen--;
}
int mynexth(void)
{
        int i;
	static int done_init;
	if (!done_init) {
	 	for (i=0; i<100; i++) myh[i] = -1;
		done_init = TRUE;
	}
	mynopen++;
        for (i=0; i<200; i++) {
                if (myh[i]<0) return i;
        }
        emsg("No free handles for my file routines, %d open  \n",mynopen);
        return 0;
}
int myread(int handle, void *buffer, unsigned int count)
{
dmsg("my: myread(%d)\n",handle);
	if (myh_type[handle]==H_PIPE) return -1;
	return read(myh[handle],buffer,count);
}
int mywrite(int handle, char *buffer, unsigned int count, int flen)
{
dmsg("my: mywrite(%d)\n",handle);
	if (myh_type[handle]==H_PIPE) return pi_write(myh[handle],buffer,count,flen);
	return write(myh[handle],buffer,count);
}
long mylseek(int handle, long offset, int origin)
{
	if (myh_type[handle]==H_PIPE) return -1;
	return lseek(myh[handle],offset,origin);
}
int myopen(const char *fname, int oflag, int pmode, int gid, int fg)
{
        int f = mynexth();
	int isnew=FALSE;
	int iswrite=FALSE;
	if (f<0) return -1;

dmsg("my: myopen(%s)\n",fname);
	myh_type[f] = H_FILE;
	if (!pi_working) goto skip_pi;
    	if (oflag & O_WRONLY) {isnew = TRUE; iswrite = TRUE;}
        if (oflag & O_APPEND) {isnew = FALSE;  iswrite = TRUE;}
	if (iswrite) {
		myh[f] = pi_open(fname,isnew,gid,fg);
		myh_type[f] = H_PIPE;
	} else {
		mysync(gid,fg);
skip_pi:
        	myh[f] = open(fname,oflag,pmode);
        }
        if (myh[f]== -1) {
        	myfreeh(f);
		return -1;
        }
dmsg("my: myopen(%d)\n",f);
        return f;
}
int myclose(int f)
{
        int r;
dmsg("my: myclose(%d)\n",f);
        if (myh[f]<0) {emsg("myclose handle null %d\n",f); return 0;}
	if (myh_type[f]==H_PIPE) {
	 	r = pi_close(myh[f]);
	} else {
		r = close(myh[f]);
	}
	myfreeh(f);
 	return r;
}
int file_length(int h);
int my_file_length(int f, int fl)
{	
	if (myh_type[f]==H_PIPE) return fl;
	return file_length(myh[f]);
}
int mysync(int gid, int fg)
{
	return pi_sync(gid);
}
/*- end of db.c library -----------------------------------------*/
