#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <time.h>
#include <sys/stat.h> 
#include <sys/types.h>
#include "dnewssrt.h"


#define MAX_MEM 120000000
char *get_path(char *s);
#define BFSZ 1000
static char workdir[BFSZ];
static char spooldir[BFSZ];
/* Bucket sorter for dnews to improve performance */
static Item *list;
static int nitem;
int bsort_do(char *bfile);
int build_index(char *bkt, int tot);
char *ncpy(char *dst, char *src, int len);
int sort_and_write(void);
void imsg_log(char *s);
int imsg(const char * arg_list, ...);
int create_fail(void);
#ifndef FALSE
#define FALSE 0
#define TRUE (!FALSE)
#endif
static char lasterr[BFSZ];
static int ncontig_old,ncontig_new;
static PHead h;
static int istart,iend,ibkt;
static char *msgbase;
static int max_mem = MAX_MEM;

int main(int argc, char *argv[]) 
{
	int r;
	if (argc<6) {
		printf("Usage: dnewssrt bucketfile bkt workpath start end\n");
		return 0;
	}
	strcpy(spooldir,get_path(argv[1]));
	h.ibkt = atoi(argv[2]);
	strcpy(workdir,get_path(argv[3]));
	h.istart = atoi(argv[4]);
	h.iend = atoi(argv[5]);
	if (argc>6) max_mem =  atoi(argv[6]);
	if (max_mem<=0) max_mem = MAX_MEM;
	imsg("Sort (%s), work (%s) bkt (%d) %d-%d\n",argv[1],workdir,h.ibkt,h.istart,h.iend);
	r = bsort_do(argv[1]);


	if (r) {
		if (nitem>0) imsg("Contig was %d (%2.2f%%) now %d (%2.2f%%)\n",ncontig_old,ncontig_old*100.0/nitem,ncontig_new,ncontig_new*100.0/nitem);
		imsg("Sorted %d items in bucket %s\n",nitem,argv[1]);
	}
	else {
		create_fail();
		imsg("Failed, read %d items in bucket %s \n",nitem,argv[1]);
		return 1;
	}
	return 0;
}
int file_len(FILE *f)
{
        struct stat bf;
        if (fstat(fileno(f),&bf)!=0) {imsg("FATAL Could not get file length\n"); return 0;}
        return bf.st_size;
}
char *ncpy(char *dst, char *src, int len)
{
        char *xdst = dst;
        int xlen = len;
                
        for (;(*src !=0) && (len>0); ) {*dst++  = *src++; len--;}
        *dst++ = 0;
        xdst[xlen] = 0;
        return xdst;
}

int bsort_do(char *bfile)
{
	char *bkt;
	int got;
	FILE *f;
	int len;
	f = fopen(bfile,"rb");	
	if (f==NULL) {
		imsg("Couldn't open (%s) (%s)\n",bfile,strerror(errno));
		return FALSE;
	}
	len = file_len(f);
	if (len<=0) {
		imsg("Bucket was empty?\n");
		return FALSE;
	}
	if (len>max_mem) {
		imsg("Bucket was too large to sort %d>%d increase BUCKET_SORT_MAX if you have enough RAM\n",len,max_mem);
		return FALSE;
	}
	msgbase = bkt = malloc(len+1);
	if (bkt==NULL) {
		imsg("Could not allocate (%d) bytes for %s\n",len,bfile);
		return FALSE;
	}
	if ((got = fread(bkt,1,len,f))!=len) {
		imsg("Only read %d/%d bytes\n",got,len);
		fclose(f);
		return FALSE;
	}
	fclose(f);
	bkt[len] = 0;
	/* Now scan bucket, find list of items and 
	   decide on best order of items */
	if (!build_index(bkt,len)) {
		imsg("Invalid character in bucket file\n");
		return FALSE;
	}
	sort_and_write();
	return TRUE;
}
/*
Lets sort on 'gid'
*/
int cmp(const void *aa, const void *bb);
int cmp(const void *aa, const void *bb)
{
        Item *a = (Item *) aa;
        Item *b = (Item *) bb;
        if (a->gid < b->gid) return -1;
        if (a->gid > b->gid) return 1;
        if (a->item < b->item) return -1;
        if (a->item > b->item) return 1;
        return 0;
}
static Item *table;
int sort_and_write(void)
{
	Item *m;
	int r;
	int nowat = 0;
	int i;
	int at=0;
	char ifile[BFSZ],pfile[BFSZ],pfile2[BFSZ];
	static char hbuf[32767];
	FILE *f;
	table = malloc(nitem*sizeof(Item));
	for (m = list,i=0; (i<nitem) && (m!=NULL); i++,m=m->next) {
		table[i] = *m;
	}
	if (nitem>3) qsort(table,nitem,sizeof(Item),cmp);	

	sprintf(ifile,"%s/%s",spooldir,DNEWSSRT_ITEM);
	imsg("Writing new item file %s\n",ifile);
	f = fopen(ifile,"wb");
	if (f==NULL) {
		imsg("Could not create %s %s\n",ifile, strerror(errno));
		return FALSE;
	}
	setvbuf( f, hbuf, _IOFBF, sizeof( hbuf ) -1); 
	for (i=0; i<nitem; i++) {
		if (i>0) if (table[i].gid==table[i-1].gid) ncontig_new++;
		m = &table[i];
		if (i+10>nitem) imsg("Writing item %d:%d\n",m->gid,m->item);
		r = fwrite(m->start+msgbase,1,m->len,f); 
		if (r<m->len) {
			imsg("Write failed %s\n",strerror(errno));
			return FALSE;
		}
		m->start = nowat;
		nowat += m->len;
	}
	if (fclose(f)!=0) {
		imsg("Write failed %s\n",strerror(errno));
		return FALSE;
	}
	/* Write patch index - binary  */
	sprintf(pfile,"%s/dnewssrt.tmp",workdir);
	f = fopen(pfile,"wb");
	if (f==NULL) {
		imsg("Could not create %s %s\n",pfile,strerror(errno));
		return FALSE;
	}
	if (nitem>0) sprintf(h.msg,"DnewsSrt bkt %d Was %d (%2.2f%%) now %d (%2.2f%%)\n",
		h.ibkt,
		ncontig_old,ncontig_old*100.0/nitem,ncontig_new,ncontig_new*100.0/nitem);

	h.version = 100;
	h.nitem = nitem;
	h.isok = TRUE;
	fwrite(&h,sizeof(PHead),1,f);
	if ((int) fwrite(table,sizeof(Item),nitem,f)!=nitem) {
		imsg("Error writing patch %s\n",strerror(errno));
		return FALSE;
	}
	if (fclose(f)!=0) {
		imsg("Error closing patch file %s\n",strerror(errno));	
	}
	sprintf(pfile2,"%s/%s",workdir,DNEWSSRT_PAT);
	remove(pfile2);
	if (rename(pfile,pfile2)!=0) {
		imsg("Rename (%s) (%s) failed (%s)\n",pfile,pfile2,strerror(errno));
	}
	return TRUE;
}
int create_fail(void)
{
	char pfile[BFSZ],pfile2[BFSZ];
	FILE *f;

	/* Write patch index - binary  */
	sprintf(pfile,"%s/dnewssrt.tmp",workdir);
	f = fopen(pfile,"wb");
	if (f==NULL) {
		imsg("Could not create %s %s\n",pfile,strerror(errno));
		return FALSE;
	}

	h.version = 100;
	h.nitem = nitem;
	h.isok = FALSE;
	strcpy(h.msg,lasterr);
	fwrite(&h,sizeof(PHead),1,f);
	fclose(f);
	sprintf(pfile2,"%s/%s",workdir,DNEWSSRT_PAT);
	remove(pfile2);
	if (rename(pfile,pfile2)!=0) {
		imsg("Rename (%s) (%s) failed (%s)\n",pfile,pfile2,strerror(errno));
	}
	return TRUE;
}
int myatoi(char *s)
{
	char bf[100];
	ncpy(bf,s,13);
	return atoi(bf);
}
int add_one(int gid, int item, char *start, int len);
int build_index(char *bkt, int tot)
{
	int gid;
	int item;
	int tlen;
	char *end = bkt+tot;
	char *s;	
	int lastgid= -2;
	for (;*bkt!=0;) {
		if (*bkt!='~') { imsg("Start char wrong (%.50s)  \n",bkt); return FALSE; }
		s = bkt+1;
		tlen = myatoi(s);
		for (;*s!=0;s++) if (*s==':') break;
		if (*s==0) { imsg("No colon after tlen (%.50s) \n",bkt); return FALSE; }
		s++;
		item = myatoi(s);
		for (;*s!=0;s++) if (*s==':') break;
		if (*s==0)  { imsg("No colon after item (%.50s) \n",bkt); return FALSE; }
		s++;
		gid = myatoi(s);
		if (gid==lastgid) ncontig_old++;
		lastgid = gid;
		for (;*s!=0;s++) if (*s==':') break;
		if (*s==0)  { imsg("No colon after gid (%.50s) \n",bkt); return FALSE; }
		tlen += s-bkt+2;

		if (tlen>20000000) { imsg("tlen too big (%.20s) \n",bkt); return FALSE; }
		if (tlen<0) { imsg("tlen too small (%.20s) \n",bkt); return FALSE; }
		if (gid>500000) { imsg("gid too big (%.20s) \n",bkt); return FALSE; }
		if (gid<1)  { imsg("gid too small (%.20s) \n",bkt); return FALSE; }
		if (!add_one(gid,item,bkt,tlen))  { imsg("add_one failed (%.20s) \n",bkt); return FALSE; }
		bkt += tlen;
		if (bkt>end)  { imsg("skipped past end %p %p \n",bkt,end); return FALSE; }
	}
	return TRUE;
}
int add_one(int gid, int item, char *start, int len)
{
	Item *m;
	m = malloc(sizeof(Item));
	if (m==NULL) return FALSE;
	m->gid = gid;
	m->item = item;
	m->start = start-msgbase;
	m->wasat = start-msgbase;
	m->len = len;
	m->next = list;
	list = m;
	nitem++;
	return TRUE;
}



int imsg(const char * arg_list, ...)
{
        va_list arg_ptr;
        char *format;
	char output[2000];
        va_start(arg_ptr, arg_list);
        format = (char *) arg_list;
        vsprintf(output, format, arg_ptr);
	imsg_log(output);
	return 0;
}
static char logfile[BFSZ];
static FILE *flog;
void imsg_log(char *s)
{
	time_t t;
        struct tm *tt; 
	char *ccc;
	static int bytes;
	t = time(NULL);
        tt = localtime(&t);
	ccc = ctime(&t);
	if (ccc==NULL) ccc = "badtimebadtimebadtime";
  
	
	if (flog==NULL) {
		if (strlen(logfile)==0) sprintf(logfile,"%s/dnewssrt.log",workdir);
		flog = fopen(logfile,"a");
		if (flog!=NULL) if (file_len(flog)>2000000) {
			fclose(flog);
			flog = fopen(logfile,"w");			
		}
	}
	if (flog==NULL) return;
	fprintf(flog,"%.19s %s",ccc,s);
	strcpy(lasterr,s);
}
char *get_path(char *s)
{
	static char bf[BFSZ];
	char *last=NULL;
	strcpy(bf,s);
	for (s=bf;*s!=0;s++) {
		if (*s=='/') last = s;
		if (*s=='\\') last = s;
	}
	if (last==NULL) return "";
	*last = 0;
	return bf;
}
