#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"
#ifdef WIN32
#include <windows.h>
#endif
#ifndef TRUE
#define FALSE 0
#define TRUE (!FALSE)
#endif
char dnews_conf[BFSZ];
int send_start(int next_state);
void suck_do(void);	
void cprintf(int c, char * arg_list, ...);
int suck_check(char *rec, int sz);
void waitfor(int c, int w, int n);
void waitdata(int c, int n);
void do_state(int c,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);
void xtime_add(int c,char *s, int nn);


enum	{S_USER,S_PASS,S_LIST, S_LIST_DATA, S_STAT, S_RETR
	  ,S_DEL, S_CLOSE, S_CLOSED
	  };

static char *mode_name[]={
	"S_USER","S_PASS","S_LIST", "S_LIST_DATA", "S_STAT", "S_RETR"
	  ,"S_DEL", "S_CLOSE", "S_CLOSED"
	  };

enum {XC_STATUS,XC_DATA,XC_IDLE};
static char *chan_str[]={"XC_STATUS","XC_ARTICLE","XC_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;
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 wait_wanted[MAX_CHAN];
static int wait_next[MAX_CHAN];
static int start_t[MAX_CHAN];	/* Start Time of Operation (open,group,xover,article) */
static int nchan;
static int t_group,t_open,t_xover,t_article;
static int t_ngroup,t_nxover,t_narticle;

int msec(void) /*Return millisec since midnight*/ 
{
#ifdef WIN32
	int t;
	struct _SYSTEMTIME st;
	GetSystemTime(&st);
	t = st.wHour; t *= 60;
	t += st.wMinute; t*= 60;
	t += st.wSecond; t*= 1000;
	t += st.wMilliseconds;
	return t;
#else
	struct timeval t;
	struct timezone tz;
	long tt;
	static long tstart;
	gettimeofday(&t,&tz);
	if (tstart==0) tstart = t.tv_sec;
	tt = ((t.tv_sec - tstart)*1000)+(t.tv_usec/1000);
	return (int) tt;
#endif
}
static int ntotal;
int main(int argc, char *argv[])
{
	static char fname[BFSZ];
	char bf[BFSZ];
	int i;

	dobig = FALSE;
	if (argc<3) {
		printf("Usage: poptest site nchans ntimes\n");
		return 0;
	}
	log_open("stress.log");
	strcpy(sitename,argv[1]);
	nlots = atoi(argv[2]);
	ntotal = atoi(argv[3]);

	nntp_startup();
	send_start(S_USER);
	nntp_wait();   /* DOESN'T RETURN */
	return 1;
}

static int tstart;
static int cto;
int send_start(int next_state)
{
	int c;
	tstart = lib_gmtime();
	if (strstr(sitename,":110")==NULL) strcat(sitename,":110");
	wmsg("Destination  site {%s} \n", sitename);
	nntp_alwaysblock();
	nchan++; 
	c = nntp_open(sitename); 
	if (c <0 ) {
		printf("Cannot Open channel to site \n");
		return FALSE;
	}
        nntp_setblock(c,TRUE); /* Set to blocking IO */
	chan_setstate(c,C_LIVE);
	waitfor(c,200,next_state);
	start_t[c] = msec();
	return TRUE;
}
void suck_do(void)	/* Background loop */
{
/*	if (!nntp_isopen(0)) {
		emsg("Thats odd, channel closed from %d \n",cto);
		stress_exit();
	}
*/
}

#define IFD if (FALSE) 
void cprintf(int c, 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(c,output,strlen(output));
	IFD emsg("send[%d]--> %s",c,output); 
}
static int cstate[MAX_CHAN];
static int mode;

static char p1[BFSZ];
static char p2[BFSZ];
static char p3[BFSZ];
static char p4[BFSZ];
static int nitems[MAX_CHAN];
static int arti[MAX_CHAN];
static int ngrp,g;
static Sary *grps;
static int nerr;
int live_read(int c, char *rec, int sz)
{
	static Sary *data;
	char bf[BFSZ];				
	char *s;

	if (data==NULL) data=s_alloc();
	switch (cstate[c]) {
  	  case XC_IDLE:
		  wmsg("IDLE: %s",rec);
		break;
	  case XC_STATUS:
		IFD wmsg("STATUS:[%d] %s",c,rec);
		if (rec[0] != '+') {
			emsg("Error %s",rec);
		}
		if (wait_wanted[c]==999) {
			s = strtok(rec," ");
			if (s!=NULL) s = strtok(NULL," ");
			nitems[c] = atoi(s);
			emsg("Chan %d has %d items\n",c,nitems[c]);
		}
		do_state(c,wait_next[c]);
		break; 
	  case XC_DATA:
	  	if (strcmp(rec,".\015\012")==0) {
			IFD wmsg("End of data found \n");
			do_state(c,wait_next[c]);
		}
		break;
	}
	dmsg("Suck return %d \n",sz);
   	return TRUE;
}
void waitfor(int c, int w, int n)
{
	wait_next[c] = n;
	wait_wanted[c] = w;
	cstate[c] = XC_STATUS;
}
void waitdata(int c, int n)
{
 	wait_next[c] = n;
	cstate[c] = XC_DATA;
}
#define NCROSS		3
/* #define ISIZE		200*/
#define ISIZE		3000
#define BIGISIZE	70000
#define NPATH 	20
static char xitem[BIGISIZE*2];
static int xlen;
static char make_mid[BFSZ];
static Sary *mbody;
static int start_time;
static int nsent;
void do_state(int c, int state)
{
	int i;
	static String *whole;
	mode = state;

again:
	IFD wmsg("Do state [%d] %s \n",c,mode_name[state]);
	switch (state) {
	  case S_USER:
 	  	cprintf(c,"user test%d\015\012",c);
 	  	waitfor(c,215,S_PASS);
		break;
	  case S_PASS:
 	  	cprintf(c,"pass test\015\012");
 	  	waitfor(c,215,S_LIST);
		break;
	  case S_LIST:
 	  	cprintf(c,"list\015\012");
 	  	waitfor(c,215,S_LIST_DATA);
		break;
 	  case S_LIST_DATA:
 	  	waitdata(c,S_STAT);
		break;
	  case S_STAT:
		if (nchan<nlots) send_start(S_USER);
		arti[c] = 1;
		cprintf(c,"stat\015\012");
		waitfor(c,999,S_RETR);
		break;
	  case S_RETR:
		ntotal--;
		if (arti[c]>nitems[c]) {
			cprintf(c,"rset\015\012");
			waitfor(c,200,S_CLOSE);
			break;
		}
		cprintf(c,"retr %d\015\012",arti[c]);
		waitdata(c,S_DEL);
		break;
	  case S_DEL:
 	  	cprintf(c,"del %d\015\012",arti[c]++);
		start_t[c] = msec();
 	  	waitfor(c,211,S_RETR);
		break;
 	  case S_CLOSE:
		xtime_add(c,"quit",1);
		if (ntotal<=0) stress_exit();
		cprintf(c,"quit\015\012");
 	  	waitfor(c,224,S_CLOSED);
 	  	break;
 	  case S_CLOSED:
		nntp_close(c);
		send_start(S_USER);
 	  	break;
	}
}

int stress_exit(void)
{
	stats_show();
	exit(1);
	return 0;
}




static char *stat_names[100];
static int nstat,stat_cur,stat_n[100],stat_time[100];
void xtime_add(int c,char *s, int nn)
{
 	int i;
	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_time[i] += msec() - start_t[c];
	stat_n[i] += nn;
}
void stats_show(void)
{
	char bf[BFSZ];
	int i;
	printf("     Name      Calls   Total   Avg\n");
	for (i=0; i<nstat; i++) {
		if (stat_n[i]>0) {
		  printf("%10s  %7d %5.2fs %7.4fs\015\012",stat_names[i]
			,stat_n[i]
			,(double)stat_time[i]/1000.0
			,(double) stat_time[i]/stat_n[i]/1000.0);
		}
	}
}
int suck_check(char *ss,int sz) {return TRUE;}
