#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "dnews.h"
#include "conf.h"
#include "lib.h"
#include "str.h"
#include "sary.h"
#include "emsg.h"
#include "nntp.h"
#include "chan.h"
#ifndef TRUE
#define FALSE 0
#define TRUE (!FALSE)
#endif
char dnews_conf[BFSZ];
int send_start(void);
void suck_do(void);	
void cprintf(char * arg_list, ...);
int suck_check(char *rec, int sz);
void waitfor(int w, int n, int f);
void waitdata(int n, int f);
void do_state(int state);
int stress_exit(void);
void get_groups(Sary *data);
void get_items(Sary *data);
void xtime_start(char *s);
void xtime_end(char *s);
void stats_show(void);


enum	{S_LIST, S_LIST_DATA, S_GROUPX, S_XOVER
	  ,S_XOVER_DATA, S_ARTICLE, S_ARTICLE_DATA
	  ,S_ARTICLE_NEXT, S_EXIT, S_IHAVE, S_IHAVE_SEND
	  };

static char *mode_name[]={
	"S_LIST", "S_LIST_DATA", "S_GROUPX", "S_XOVER"
	  ,"S_XOVER_DATA", "S_ARTICLE", "S_ARTICLE_DATA"
	  ,"S_ARTICLE_NEXT", "S_EXIT", "S_IHAVE", "S_IHAVE_SEND"
	  };

enum {C_STATUS,C_DATA,C_IDLE};
static char *chan_str[]={"C_STATUS","C_ARTICLE","C_IDLE"};

static int ihave_from,ihave_to,ihave;
char sitename[BFSZ];
static int dobig,ifile,postit,dodie,anygroup,fixmid,dolots,dolots2,nlots;
static int uneven,dohist,dohist2,longline;
int suck_stream_group(char *bf, int sz){return 0;}
int suck_stream_item(char *bf, int sz){return 0;}
int suck_stream_next(char *bf, int sz){return 0;}
static int doone=FALSE;
int main(int argc, char *argv[])
{
	static char fname[BFSZ];
	char bf[BFSZ];
	int i;

	dobig = FALSE;
	log_open("stress.log");
	for (i=1; i<argc; i++) {
		if      (strcmp(argv[i],"-ihave")==0) {
			ihave = TRUE;
  			ihave_from = atoi(argv[++i]);
 			ihave_to = ihave_from + atoi(argv[++i]);
		}
		else if (strcmp(argv[i],"-file")==0) { ihave = TRUE; ifile=TRUE;}
		else if (strcmp(argv[i],"-die")==0) { dodie = TRUE;}
		else if (strcmp(argv[i],"-post")==0) { postit = TRUE; ihave = TRUE; }
		else if (strcmp(argv[i],"-big")==0) dobig = TRUE;
		else if (strcmp(argv[i],"-any")==0) anygroup = TRUE;
		else if (strcmp(argv[i],"-lots")==0) {dolots = TRUE; nlots = atoi(argv[++i]);}
		else if (strcmp(argv[i],"-lots2")==0) dolots2 = TRUE;
		else if (strcmp(argv[i],"-fixmid")==0) fixmid = TRUE;
		else if (strcmp(argv[i],"-uneven")==0) uneven = TRUE;
		else if (strcmp(argv[i],"-long")==0) longline = TRUE;
		else if (strcmp(argv[i],"-hist")==0) dohist = TRUE;
		else if (strcmp(argv[i],"-hist2")==0) dohist2 = TRUE;
		else if (strcmp(argv[i],"-one")==0) doone = TRUE;
		else strcpy(sitename,argv[i]);	
	}
	if (strlen(sitename)==0) {
		emsg("Usage: stress site.name [-ihave 1000 10] -file -any -big -post \n");
		printf("site "); gets(sitename);
		ihave = TRUE;
	}
/*	if (ihave) {
		printf("From ? "); gets(bf); ihave_from = atoi(bf);
		printf("To   ? "); gets(bf); ihave_to = atoi(bf);
	}
*/
/*	sig_init(); */  /* Trap PIPE errors, rather than crash */
	wmsg("nntp startup\n");
	nntp_startup();
	if (dolots) {open_lots(); return 1;}
	if (dolots2) {open_lots2(); return 1;}
	wmsg("send_start\n");
	send_start();
	wmsg("nntp_wait\n");
	nntp_wait();   /* DOESN'T RETURN */
	return 1;
}

static int tstart;
static int cto;
int send_start(void)
{
	tstart = lib_gmtime();
	wmsg("Destination  site {%s} \n", sitename);
	wmsg("nntp open \n");
	nntp_alwaysblock();
	cto = nntp_open(sitename);
        nntp_setblock(cto,TRUE); /* Set to blocking IO */


	wmsg("channel %d \n",cto);
	if (cto<0) return FALSE;
	chan_setstate(cto,C_CHECK);
	if (anygroup) waitfor(200,S_LIST,S_EXIT);
	else waitfor(200,S_IHAVE,S_EXIT);
/* 	if (ihave) waitfor(200,S_IHAVE,S_EXIT); */
	return TRUE;
}
int open_lots(void)
{
	int i;
	int cto;
	char bf[2000];
	nntp_alwaysblock();
	for (i=0; i<nlots; i++) {	
		cto = nntp_open(sitename);
	        nntp_setblock(cto,TRUE); /* Set to blocking IO */
		emsg("Open %d %d \n",i,cto);	
		nntp_read(cto,bf,1000);
		emsg("> %s ",bf);		
	}
	printf("Press return to exit");
	gets(bf);
}
int open_lots2(void)
{
	int i;
	int cto;
	char bf[2000];
	for (i=0; i<100; i++) {	
		cto = nntp_open(sitename);
	}
	printf("Press return to exit");
	gets(bf);
}

void suck_do(void)	/* Background loop */
{
	if (!nntp_isopen(cto)) cto = -1;
	if ((cto<0)) {
		emsg("Thats odd, channel closed from %d \n",cto);
		stress_exit();
	}
}


void cprintf(char * arg_list, ...)
{
 	va_list arg_ptr;
 	char *format, output[1000];
 	va_start(arg_ptr, arg_list);
 	format = arg_list;
	vsprintf(output, format, arg_ptr);
	nntp_write_s(cto,output,strlen(output));
	wmsg("send--> %s",output); 
}
static cstate,mode,state_wanted,state_next,state_fail;

static char p1[BFSZ];
static char p2[BFSZ];
static char p3[BFSZ];
static char p4[BFSZ];
static int nart,arti,art[40000];
static int ngrp,g;
static Sary *grps;
static int nerr;
int suck_check(char *rec, int sz)
{
	static Sary *data;
	char bf[BFSZ];				
	char *s;
	dmsg("Suck called %d \n",sz);

	if (data==NULL) data=s_alloc();
	switch (cstate) {
  	  case C_IDLE:
	  	wmsg("%s",rec);
		break;
	  case C_STATUS:
		wmsg("STATUS: %s",rec);
  		nerr = atoi(rec);
		ncpy(bf,rec,BFSZ-1);
		s = strtok(bf," \n\r"); if (s!=NULL) strcpy(p1,s);
		s = strtok(NULL," \n\r"); if (s!=NULL) strcpy(p2,s);
		s = strtok(NULL," \n\r"); if (s!=NULL) strcpy(p3,s);
		s = strtok(NULL," \n\r"); if (s!=NULL) strcpy(p4,s);
		if (nerr==state_wanted) do_state(state_next);
		else {
			emsg("Wanted %d, got {%s}",state_wanted,bf);
			do_state(state_fail);
		}
		break; 
	  case C_DATA:
	  	if (strcmp(rec,".\015\012")==0) {
			wmsg("End of data found \n");
			if (mode==S_LIST_DATA) get_groups(data);
			if (mode==S_XOVER_DATA) get_items(data);
			do_state(state_next);
			s_clear(data);
		} else s_add(data,rec);
		break;
	}
	dmsg("Suck return %d \n",sz);
   	return TRUE;
}
void waitfor(int w, int n, int f)
{
	state_wanted = w;
	state_next = n;
	state_fail = f;
	cstate = C_STATUS;
}
void waitdata(int n, int f)
{
 	state_next = n;
	cstate = C_DATA;
}
#define NCROSS		3
/* #define ISIZE		200*/
#define ISIZE		3000
#define BIGISIZE	1500000
#define NPATH 	20
static char xitem[BIGISIZE*2];
static int xlen;
static char make_mid[BFSZ];
static Sary *mbody;
void s_readcrlf(Sary *bf, char *file);
void s_readcrlf(Sary *bf, char *file)
{
	FILE *f;
	char inbuff[20001];
	int i;

	f = fopen(file,"r");
	if (f==NULL) { emsg("Unable to open {%s} ",file); return;}

	s_clear(bf);
	for (;!feof(f);) {
	  if (fgets(inbuff,20000,f)!=NULL) {
		i = strlen(inbuff);
		if (inbuff[i-1]=='\n') inbuff[i-1] = 0;
		if (inbuff[i-1]=='\15') inbuff[i-1] = 0;
		if (inbuff[i-2]=='\15') inbuff[i-2] = 0;
		strcat(inbuff,"\015");
		s_add(bf,inbuff);			
	  }
	}
	fclose(f);
	strcpy(inbuff,"\015\012");
	s_add(bf,inbuff);			
}
char *next_group(void)
{
 	static int ng;
	ng++;
	if (ng>=s_len(grps)) ng = 0;
	return s_line(grps,ng);
}
int make_item(void)
{
 	static int mm;
	char bf[10000];
	char fname[100];
	String *sbody;
	int i;
	int isize;
	char *agrp;
	char *s;
	FILE *f;
	char *aagrp;
	mm++;
		
	xlen = 0;
	if (mm<ihave_from) mm=ihave_from;
	if (mm>ihave_to) {
		emsg("Completed %d %d \n",mm,ihave_to);
		return FALSE;
	}

	if (mbody!=NULL) s_free(mbody);
	mbody = s_alloc();

	if (ifile) {
	 	sprintf(fname,"store_%d.itm",mm);
		f = fopen(fname,"r");
		if (f==NULL) return FALSE;
		strcpy(xitem,"");
		for (;!feof(f);) {
		 	if (fgets(bf,BFSZ-10,f)==NULL) break;
			s = strchr(bf,'\n');
			if (s!=NULL) *s = 0;
		     	strcat(xitem,bf);
			strcat(xitem,"\015\012");
		}
		fclose(f);
		strcat(xitem,".\015\012");
		/* This cheat will probably work for now :-) */
		sprintf(make_mid,"<%d@test.netwin.co.nz>",mm);
		return TRUE;
	}
	if (anygroup) agrp = next_group(); else agrp = "";
	aagrp = agrp;
	if (fixmid) aagrp = "";
	sprintf(make_mid,"<%d@%stest.netwin.co.nz>",mm,aagrp);
	sprintf(xitem,"From: chrisp@test.site\015\012"); xlen+=strlen(xitem);
	sprintf(xitem+xlen,"Subject: Test message number %d\015\012",mm); xlen+=strlen(xitem+xlen);
	sprintf(xitem+xlen,"Message-ID: %s\015\012",make_mid); xlen+=strlen(xitem+xlen);
	sprintf(xitem+xlen,"Distribution: netwin\015\012"); xlen+=strlen(xitem+xlen);
	sprintf(xitem+xlen,"Date: 5 Dec 1996 21:22:23 GMT\015\012"); xlen+=strlen(xitem+xlen);
	if (doone) {
		sprintf(xitem+xlen,"Newsgroups: test.one\015\012"); xlen+=strlen(xitem+xlen);
	} else {
	if (anygroup)	{
		if (uneven) if ((mm & 1)>0) agrp = s_line(grps,mm % 100);
		sprintf(xitem+xlen,"Newsgroups: %s,%s\015\012",agrp,next_group()); xlen+=strlen(xitem+xlen);
	} else {
		sprintf(xitem+xlen,"Newsgroups: test.one,test.two,test.three\015\012"); xlen+=strlen(xitem+xlen);
	}}
	sprintf(xitem+xlen,"Path: grunt.marc.cri.nz!news.palm.cri.nz!iconz.co.nz!news.massey.ac.nz!waikato!news.uoregon.edu!news.kreonet.re.kr!usenet.kornet.nm.kr!news.sprintlink.net!tank.news.pipex.net!pipex!oleane\015\012");xlen+=strlen(xitem+xlen);
	sprintf(xitem+xlen,"\015\012");xlen+=strlen(xitem+xlen);
	sprintf(xitem+xlen,"Start: %s\015\012",make_mid); xlen+=strlen(xitem+xlen);
	isize = ISIZE;
	if (dobig) {
		/* if ((mm % 10)==0) {*/
			isize = BIGISIZE;
			isize = isize + ((mm * 123) % (BIGISIZE/5));
		/* } */
	}
	if (doone) isize = 170;
	for (i=0; i<isize/160;i++) {
		sprintf(xitem+xlen,"%d 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678\015\012",i);xlen+=strlen(xitem+xlen);
	}
	if (longline && ((mm%10)==0)) {
		memset(xitem+xlen,32,40000);
		xlen += 39000;
	}
	sprintf(xitem+xlen,"End: %s end of message! size %d\015\012",make_mid,isize); xlen+=strlen(xitem+xlen);
	sprintf(xitem+xlen,".\015\012");xlen+=strlen(xitem+xlen);
	printf("Message size %d \n",isize);
	return TRUE;
}
static int start_time;
static int nsent;
void slave_closing(int c)
{
}
void ncmd_timeout(int c)
{
}
void do_state(int state)
{
	int i;
	static String *whole;
	mode = state;

again:
	wmsg("Do state %s \n",mode_name[state]);
	switch (state) {
 	  case S_LIST:
 	  	cprintf("list\015\012");
		if (dodie) {for (i=10; i>5; i++) ;}
		xtime_start("LIST");
 	  	waitfor(215,S_LIST_DATA,S_EXIT);
		break;
 	  case S_LIST_DATA:
 	  	waitdata(S_GROUPX,S_EXIT);
		break;
 	  case S_GROUPX:
		if (ihave) goto do_ihave;
		xtime_end("LIST");
 	  	cprintf("group %s\015\012",s_line(grps,g++));
 	  	waitfor(211,S_XOVER,S_ARTICLE_NEXT);
		xtime_start("GROUP");
		break;
 	  case S_XOVER:
	  	nart = 0; arti = 0;
 	  	cprintf("xover %s-%s\015\012",p3,p4);
 	  	waitfor(224,S_XOVER_DATA,S_EXIT);
		xtime_end("GROUP");
		xtime_start("XOVER");
 	  	break;
 	  case S_XOVER_DATA:
 	  	waitdata(S_ARTICLE,S_EXIT);
 	  	break;
 	  case S_ARTICLE:
		xtime_end("XOVER");
		if (nart==0) {state=S_ARTICLE_NEXT; goto again;}
 	  	cprintf("article %d\015\012",art[arti++]);
 	  	waitfor(220,S_ARTICLE_DATA,S_EXIT);
 	  	break;
 	  case S_ARTICLE_DATA:
 	  	waitdata(S_ARTICLE_NEXT,S_EXIT);
 	  	break;
 	  case S_ARTICLE_NEXT:
	  	if (arti<nart) {state = S_ARTICLE; goto again;}
		if (g<s_len(grps))  {state = S_GROUPX; goto again;}
		stress_exit();
		break;
 	  case S_EXIT:
		emsg("Exit mode \n");
		stress_exit();
		break;
 	  case S_IHAVE:
do_ihave:
		if (start_time==0) start_time = time(NULL);
	  	if (!make_item()) stress_exit();	
		nsent++;
		xtime_end("IHAVE_DATA");
		if (postit) {
	 	  	cprintf("post\015\012",make_mid);
	 	  	waitfor(340,S_IHAVE_SEND,S_EXIT);
		} else {
	 	  	cprintf("ihave %s\015\012",make_mid);
			if (dohist) waitfor(435,S_IHAVE,S_EXIT);
			else if (dohist2) waitfor(335,S_IHAVE_SEND,S_IHAVE);
			else waitfor(335,S_IHAVE_SEND,S_EXIT);
		}
		xtime_start("IHAVE");
		break;
 	  case S_IHAVE_SEND:
		xtime_end("IHAVE");
		printf("xmit, posting message\n");
		if (whole==NULL) whole = str_new();
	/*	for (i=0; i<s_len(mbody); i++) {
		     printf("Sending {%s} %d\n",s_line(mbody,i),strlen(s_line(mbody,i)));
		}	
	  */
		chan_write_s(cto,xitem);
		{
			int lp;
			for (lp=0;nntp_isbusy(cto) && lp<10000;lp++) {
				nntp_write_now(cto);
			}
		}
		if (postit) {		
 	  		waitfor(240,S_IHAVE,S_IHAVE);
 	  	} else { 
 	  		if (dohist2) waitfor(235,S_IHAVE,S_IHAVE);
 	  		else waitfor(235,S_IHAVE,S_IHAVE);
		}
		xtime_start("IHAVE_DATA");
		break;
	}
}

int stress_exit(void)
{
	double speed;
	double  delay;
	delay = time(NULL)-start_time;
	if (delay==0) delay = 1;
	speed = ((double) nsent)/delay;
	emsg("Stress statistics speed %d items %g / sec\n",nsent,speed);
	
	stats_show();
	emsg("Stress statistics speed %d items %g / sec\n",nsent,speed);
	exit(1);
	return 0;
}

void get_groups(Sary *data)
{
 	int i;
	char bf[200];
	char *s;
	grps = s_alloc();
 	for (i=0; i<s_len(data); i++) {
		s = strtok(s_line(data,i)," ");
		if (s!=NULL) {
			strcpy(bf,s);
			s = strtok(NULL,"\n");
			if (s!=NULL) {
				if (strchr(s,'y')!=NULL) s_add(grps,bf);
			}
 		}
 	}		

}
void get_items(Sary *data)
{
 	int i;
	char *s;
	nart = 0;
 	for (i=0; i<s_len(data); i++) {
		s = strtok(s_line(data,i)," ");
		if (s==NULL) emsg("xover fatal: %s ",s_line(data,i));
		else art[nart++] = atoi(s);
 	}		
}



static char *stat_names[100];
static int nstat,stat_cur,stat_n[100];
static double stat_start,stat_estart,stat_time[100],stat_etime[100];
void xtime_start(char *s)
{
	int i;
	time_t  xx;
 	for (i=0; i<nstat; i++) if (strcmp(s,stat_names[i])==0) goto gotit;
	if (nstat>50) {emsg("stat problem %s \n",s); return;}
	stat_names[nstat++] = strdup(s);
	i = nstat-1;
gotit:
	stat_cur = i;
	stat_start = clock();
	stat_estart = time(&xx);
}
void xtime_end(char *s)
{
	time_t  xx;
	if (stat_names[stat_cur]==NULL) return;
	if (strcmp(stat_names[stat_cur],s)!=0) return;
	stat_etime[stat_cur]  += (time(&xx)-stat_estart);
 	stat_time[stat_cur]  += (clock()-stat_start);
 	stat_n[stat_cur]++;
}
void stats_show(void)
{
	char bf[BFSZ];
	int i;
	int cps=CLOCKS_PER_SEC;

	for (i=0; i<nstat; i++) {
		if (stat_n[i]>0) {
		  printf("%s  cpu=%g  calls=%d   cpuavg=%g elapsed=%g\015\012",stat_names[i]
				,stat_time[i]/cps,stat_n[i]
				,(stat_time[i]/cps)/stat_n[i]
				,(stat_etime[i])/stat_n[i]);
		  emsg("%s  cpu=%g  calls=%d   cpuavg=%g elapsed=%g\015\012",stat_names[i]
				,stat_time[i]/cps,stat_n[i]
				,(stat_time[i]/cps)/stat_n[i]
				,(stat_etime[i])/stat_n[i]);
		}
	}
}




void live_read(int chan, char *bf, int sz) {}











