#include <../arch/arch.h>

#define	MAX_SERIES	255

char	  prefix[_MAX_PATH] = "out/";
char	  limits_dir[_MAX_PATH] = "limits/";

char	  basename[_MAX_PATH];
unsigned  num_ids;
char	  id[MAX_SERIES];
vhandle_t in;
vhandle_t out[MAX_SERIES];

u8_t init_time_id[8] =
	{ 0xe0, 0xa0, 0xa1, 0xae, 0xe2, 0xeb, 0 }; /* cp866 cyr "работы" */

unsigned start_time;
unsigned last_time;
vboolean something_read;
vec_t accumulated[MAX_SERIES];

u8_stack_t	accum[MAX_SERIES];

void init_accum(void)
{
	int i;
	for(i=0;i<MAX_SERIES;i++)
		u8sInit(&accum[i]);
}

void shut_accum(void)
{
	int i;
	for(i=0;i<MAX_SERIES;i++)
		u8sClear(&accum[i]);
}

void store(void)
{
	u32_t i;
	for(i=0;i<num_ids;i++)
		u8sPushBuffer(&accum[i],(u8_p)&accumulated[i],sizeof(vec_t));
}

vec_p accum_get(int s_num)
{
	return (vec_p)u8sGetStorage(&accum[s_num]);
}

void reset_values(void)
{
	unsigned i;
 	for(i=0;i<num_ids;i++)
 		accumulated[i] = 0;
 	something_read = false;
}

char *v_ftoa(vec_t v)
{
	static char v_ftoa_buf[256];
	char *ptr;
	sprintf(v_ftoa_buf,"%g",v);
	for(ptr = v_ftoa_buf;*ptr;ptr++)
		if(*ptr == '.')
			*ptr = ',';
	return v_ftoa_buf;
}
	
unsigned last_save_time = 0;

void save_values(unsigned for_time)
{
	unsigned i;
    for(i=0;i<num_ids;i++)
    	filePrintf(out[i],"%u\t%s\n",for_time-start_time,v_ftoa(accumulated[i]));
    store();
    last_save_time = for_time;
}

int index_of_id(char c)
{
	char *pos = strchr(id,toupper(c));
	if(!pos)
		return -1;
	return pos - id;
}

#define CHAR_VAL(c)	(unsigned)((u8_t)(c)-(u8_t)('0'))

vboolean parse_time(char *ts,unsigned *retval)
{
	unsigned h,m,s;
	if(strlen(ts)==8 && ts[2]==':' && ts[5]==':' &&
		isdigit(ts[0]) && isdigit(ts[1]) && 
		isdigit(ts[3]) && isdigit(ts[4]) &&
		isdigit(ts[6]) && isdigit(ts[7]))
		{
			h = CHAR_VAL(ts[0]) * 10 + CHAR_VAL(ts[1]);
			m = CHAR_VAL(ts[3]) * 10 + CHAR_VAL(ts[4]);
			s = CHAR_VAL(ts[6]) * 10 + CHAR_VAL(ts[7]);
			*retval = (h * 60 + m) * 60 + s;
			return true;
		}
	return false;
}

unsigned parse_number(char *raw)
{
	unsigned val = 0;
	char *ptr = raw;
	for(;*ptr && !isdigit(*ptr);ptr++);
	for(;*ptr && isdigit(*ptr);ptr++)
		val = val * 10 + CHAR_VAL(*ptr);
	return val;
}

void reparse(void)
{
	int ind;
	unsigned next_time, dt;
    char this_id;
    /*
     * Get next token
     */
	while(lexNext(in))
    {
    	/* Is it a time string ? */
    	if(parse_time(lexLast(in),&next_time))
        {
        	if(next_time < last_time)
        	{
        		/* broken field */
        		conlPrintf(LOG_ERROR,"REPARSE: broken field detected\n");
        		continue;
        	}
        	dt = next_time - last_time;
			/* Is a new time differ from prev time ? */
            if(!dt)
    	    	/* No, skip it */
                continue;
	        /* Yes, save and than reset all values */
            save_values(last_time++);
            reset_values();
    	    /* Is a difference greater than 1 second ? */
           	while(--dt)
        		/* Yes, fill gap with zeros */
                save_values(last_time++);
        }
        else
        {
        	/* No; threat it as a value id */
            if(strlen(lexLast(in)) < 3 || lexLast(in)[1] != '=')
            	/* wrong value format */
                continue;
            this_id = lexLast(in)[0];
            ind = index_of_id(this_id);
            if(ind < 0)
            	/* unknown id */
                continue;
			accumulated[ind] += parse_number(lexLast(in));
			something_read = true;
		}
    }
	dt = last_time - last_save_time;
	if(dt)
	{
		save_values(last_time++);
		reset_values();
		while(--dt)
			save_values(last_time++);
	}
}

/*
 * DSP routines
 */
void convolve(vec_t *in,u32_t in_len,vec_t *kernel,u32_t kernel_len,vec_t *out)
{
	u32_t out_len=in_len+kernel_len-1,i,j;
	memZero(out,out_len * sizeof(vec_t));
	for(i=0;i<in_len;i++)
		for(j=0;j<kernel_len;j++)
			out[i+j] += in[i] * kernel[j];
}

/*
 * FIR filter kernel
 */
vec_t fir11[] = {0.042f,-0.105f,-0.023f,0.140f,0.280f,0.333f,0.280f,0.140f,-0.023f,-0.105f,0.042f};

int	proportional_limit	= -1;
int elastic_limit		= -1;
int strength_limit	= -1;
int end_limit	= -1;
vboolean have_limits = false;

int classify(int sample_no)
{
	if((sample_no < 0) || (sample_no >= end_limit))
		return 0;
	if(sample_no < proportional_limit)
		return 1;
	if(sample_no < elastic_limit)
		return 2;
	if(sample_no < strength_limit)
		return 3;
	return 4;
}

vec_t F[5][MAX_SERIES];
vec_t D[5][MAX_SERIES];

void process_data(u32_t num_samples)
{
	u32_t i,j,k;
	vector_t *buf = vecAlloc(num_samples + 11);
	
	if(have_limits)
	{
    	/*
    	 * Calculate criterion 'F'
    	 */
    	for(i=0;i<num_ids;i++)
    	{
    		vec_p in = accum_get(i);
    		F[0][i] = F[1][i] = F[2][i] = F[3][i] = F[4][i] = 0;
    		for(j=0;j<num_samples;j++)
    		{
    			k = classify(j);
    			if(k > 0)
    			{
    				F[k][i] += in[j];
	    			/* discard all emission after destruction */
    				if(k < 4)
    					F[0][i] += in[j];
    			}
    		}
    		if(!IsZero(F[0][i]))
    		{
    			for(j=1;j<=4;j++)
    				F[j][i] /= F[0][i];
    		}
    	}
	}
	
	/*
	 * Smooth data
	 */
	
	for(i=0;i<num_ids;i++)
	{
		vec_p in = accum_get(i);
		convolve(in,num_samples,fir11,11,buf->elements);
		memCopy(in,buf->elements+5,num_samples * sizeof(vec_t));
		for(j=0;j<num_samples;j++)
			if(in[j] < 0)
				in[j] = 0;
	}
	
	memFree(buf);
	
}

vboolean try_read_limits = false;

int main(int argc,char *argv[])
{
	u32_t i;
	char outname[_MAX_PATH];

    logStartConsoleLog();
    logUpperLevel(LOG_TRACE);

	if(argc < 3)
	{
		puts("Usage: reparse <AF15 AE file> <ID1> [<ID2> ... <ID255>] [--prefix=<dir_name>]");
		return -1;
	}
	
	/*
	 * Open input file
	 */
	in = lexParseFile(argv[1],"");
	if(in == LIBV_INVALID_HANDLE)
	{
		printf("Unable to open '%s'\n",argv[1]);
		return -1;
	}
	// prefix[0]=0;
	/*
	 * pass N0 1: parse options
	 */
	for(i=1;i<(u32_t)argc;i++)
	{
		if(!memcmp(argv[i],"--prefix=",9))
		{
			strCopy(prefix,argv[i]+9,_MAX_PATH);
			if(strlen(prefix))
				strAppendSlash(prefix);
			continue;
		}
		if(!memcmp(argv[i],"--limits_dir=",13))
		{
			strCopy(limits_dir,argv[i]+13,_MAX_PATH);
			if(strlen(limits_dir))
				strAppendSlash(limits_dir);
			try_read_limits = true;
			continue;
		}
	}

	{
		char buf[_MAX_PATH];
		dos_name_to_unix_name(basename,argv[1]);
		fsSplitPath(basename,NULL,buf,NULL);
		fsMakePath(basename,prefix,buf,NULL);
	}
	
	/*
	 * pass N0 2: parse series IDs, open output files
	 */
	for(num_ids=0,i=2;i<(u32_t)argc && num_ids<MAX_SERIES;i++)
	{
		char ending[4] = "._";
		if(argv[i][0]=='-' && argv[i][1]=='-') /* option - skip it */
			continue;
		if(argv[i][1]) /* must be one char ! */
		{
			printf("Error: expected one-char ID, got '%s'\n",argv[i]);
			return -1;
		}
		id[num_ids] = (char)toupper(argv[i][0]);
		
		strCopy(outname,basename,_MAX_PATH);
		ending[1] = id[num_ids];
		strCat(outname,ending,_MAX_PATH-strlen(outname));
		
		out[num_ids] = 	fileOpenWrite(outname);
		num_ids++;
	}
	/*
	 * Search for start ID
	 */
	while(lexNext(in))
    {
		if(!strcmp(lexLast(in),(char*)init_time_id))
		{
			if(parse_time(lexNext(in),&start_time))
			{
				last_time = start_time;
				reset_values();
				init_accum();
				reparse();
			}
		}
    }
    lexDestroy(in);
	for(i=0;i<num_ids;i++)
		fileClose(out[i]);
	/*
	 * Read limits
	 */
	if(try_read_limits)
	{
		vhandle_t lms;
		char lname[_MAX_PATH],dn[_MAX_PATH],fn[_MAX_PATH];
		dos_name_to_unix_name(lname,argv[1]);
		fsSplitPath(lname,dn,fn,NULL);
		if(limits_dir[0])
			fsMakePath(lname,limits_dir,fn,".limits");
		else
			fsMakePath(lname,dn,fn,".limits");
		lms = lexParseFile(lname,"");
		if(lms != LIBV_INVALID_HANDLE)
		{
			while(lexNext(lms))
			{
				if(!strcmp(lexLast(lms),"proportional_limit"))
				{
					if(strToInt(lexNext(lms),(s32_t*)&proportional_limit))
						continue;
					else
						abort();
				}
				if(!strcmp(lexLast(lms),"elastic_limit"))
				{
					if(strToInt(lexNext(lms),(s32_t*)&elastic_limit))
						continue;
					else
						abort();
				}
				if(!strcmp(lexLast(lms),"strength_limit"))
				{
					if(strToInt(lexNext(lms),(s32_t*)&strength_limit))
						continue;
					else
						abort();
				}
				if(!strcmp(lexLast(lms),"end_limit"))
				{
					if(strToInt(lexNext(lms),(s32_t*)&end_limit))
						continue;
					else
						abort();
				}
			}
			if(proportional_limit > 0 && elastic_limit > 0 && strength_limit > 0 && end_limit > 0)
				have_limits = true;
		}
	}
	process_data(u8sCount(&accum[0]) / sizeof(vec_t));
	/*
	 * Save smooth curve
	 */
	for(i=0;i<num_ids;i++)
	{
		int j,k=u8sCount(&accum[0]) / sizeof(vec_t);
		vec_p ptr;
		char ending[] = "._.smooth";
		strCopy(outname,basename,_MAX_PATH);
		ending[1] = id[i];
		strCat(outname,ending,_MAX_PATH-strlen(outname));
		out[i] = fileOpenWrite(outname);
		assert(out[i]);
		ptr = accum_get(i);
		for(j=0;j<k;j++)
			filePrintf(out[i],"%s\n",v_ftoa(ptr[j]));
	   	fileClose(out[i]);
	}
	/*
	 * Save criterions
	 */
	if(have_limits)
	{
    	for(i=0;i<num_ids;i++)
    	{
    		vhandle_t criteria;
    		char ending[] = "._.F";
    		ending[1] = id[i];
    		strCopy(outname,basename,_MAX_PATH);
    		strCat(outname,ending,_MAX_PATH-strlen(outname));
    		criteria = fileOpenWrite(outname);
    		assert(criteria);
    
    		filePrintf(criteria,"%s\n",v_ftoa(F[0][i]));
    		filePrintf(criteria,"%s\t",v_ftoa(F[1][i]));
    		filePrintf(criteria,"%s\t",v_ftoa(F[2][i]));
    		filePrintf(criteria,"%s\t",v_ftoa(F[3][i]));
    		filePrintf(criteria,"%s\n",v_ftoa(F[4][i]));
    
    			/*
    			filePrintf(criteria,"F_%c=%s\t",c,v_ftoa(F[0][i]));
    			filePrintf(criteria,"F1_%c=%s\t",c,v_ftoa(F[1][i]));
    			filePrintf(criteria,"F2_%c=%s\t",c,v_ftoa(F[2][i]));
    			filePrintf(criteria,"F3_%c=%s\t",c,v_ftoa(F[3][i]));
    			filePrintf(criteria,"F4_%c=%s\n",c,v_ftoa(F[4][i]));
    			filePrintf(criteria,"D_%c=%s\t",c,v_ftoa(D[0][i]));
    			filePrintf(criteria,"D1_%c=%s\t",c,v_ftoa(D[1][i]));
    			filePrintf(criteria,"D2_%c=%s\t",c,v_ftoa(D[2][i]));
    			filePrintf(criteria,"D3_%c=%s\t",c,v_ftoa(D[3][i]));
    			filePrintf(criteria,"D4_%c=%s\n",c,v_ftoa(D[4][i]));
    			*/
    		
    	   	fileClose(criteria);
    	}
    }
	shut_accum();
	return 0;
}
