#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_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
		,S_START
	  };

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", "S_START"
	  };

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: stress site nchans ngroups\n");
		return 0;
	}
	log_open("stress.log");
	strcpy(sitename,argv[1]);
	nlots = atoi(argv[2]);
	ntotal = atoi(argv[3]);

	nntp_startup();
	if (!send_start(S_LIST)) return 0;
	nntp_wait();   /* DOESN'T RETURN */
	return 1;
}

static int tstart;
static int cto;
int send_start(int next_state)
{
	int c;
	tstart = lib_gmtime();
	wmsg("Destination  site {%s} \n", sitename);
	nntp_alwaysblock();
	c = nchan++;
	if (c != nntp_open(sitename)) {
		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 dmsg("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 nart[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);
  		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!=wait_wanted[c]) {
			emsg("Wanted %d, got {%s}",wait_wanted[c],rec);
		}
		do_state(c,wait_next[c]);
		break; 
	  case XC_DATA:
	  	if (strcmp(rec,".\015\012")==0) {
			IFD wmsg("End of data found \n");
			if (mode==S_LIST_DATA) get_groups(data);
			if (mode==S_XOVER_DATA) get_items(data);
			do_state(c,wait_next[c]);
			s_clear(data);
		} else s_add(data,rec);
		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;
char *next_group(void)
{
 	static int ng;
	ng++;
	if (ng>=s_len(grps)) ng = 0;
	return s_line(grps,ng);
}
static int start_time;
static int nsent;
char *pick_group(int c)
{
	static int total;
	static int gg;
	int g = (c+gg) % s_len(grps);
	gg++;
	total++; 
	if (total>ntotal) stress_exit();
	if (gg>100) gg = 0;
	return s_line(grps,g);
}
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_LIST:
 	  	cprintf(c,"list\015\012");
 	  	waitfor(c,215,S_LIST_DATA);
		break;
 	  case S_LIST_DATA:
 	  	waitdata(c,S_START);
		break;
	  case S_START:
		xtime_add(c,"open",1);
		if (nchan<nlots) send_start(S_START);
		cprintf(c,"mode reader\015\012");
		waitfor(c,200,S_GROUPX);
		break;
	  case S_GROUPX:
 	  	cprintf(c,"group %s\015\012",pick_group(c));
		start_t[c] = msec();
 	  	waitfor(c,211,S_XOVER);
		break;
 	  case S_XOVER:
		xtime_add(c,"group",1);
		start_t[c] = msec();
		nart[c] = 0; 
 	  	cprintf(c,"xover %s-%d\015\012",p3,atoi(p3)+100);
 	  	waitfor(c,224,S_XOVER_DATA);
 	  	break;
 	  case S_XOVER_DATA:
 	  	waitdata(c,S_ARTICLE_NEXT);
		nart[c] = 0;
 	  	break;
	  case S_ARTICLE:
		cprintf(c,"article\015\012");
 	  	waitfor(c,220,S_ARTICLE_DATA);
 	  	break;
 	  case S_ARTICLE_DATA:
 	  	waitdata(c,S_ARTICLE_NEXT);
 	  	break;
 	  case S_ARTICLE_NEXT:
		xtime_add(c,"xover",1);
		start_t[c] = msec();
	  	if (nart[c]<10) {
			cprintf(c,"next\015\012");
	 	  	waitfor(c,223,S_ARTICLE);
			nart[c]++;
			return;
		}
		xtime_add(c,"article",10);
		start_t[c] = msec();
		state = S_GROUPX;
		goto again;
 	  case S_EXIT:
		emsg("Exit mode \n");
		stress_exit();
		break;
	}
}

int stress_exit(void)
{
	stats_show();
	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)
{
}



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;}















