/*
 * © Copyright 1996-2012 ECMWF.
 * 
 * This software is licensed under the terms of the Apache Licence Version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
 * In applying this licence, ECMWF does not waive the privileges and immunities 
 * granted to it by virtue of its status as an intergovernmental organisation nor
 * does it submit to any jurisdiction.
 */

#if (defined(__APPLE__) && defined(__MACH__))
#include <ctype.h>
#endif
#include "mars.h"
#include "ibmblk.h"

#ifdef  CHECK_ALIGN
#define COPY(a,b,c)   mycopy((char*)(a),(char*)(b),c)
#else
#define COPY(a,b,c)	  memcpy(a,b,c)
#endif

#define FILL_CHR(x,y) fill_chr(x, y, sizeof(x))
#define FILL_INT(x,y) fill_int (x, y, sizeof(x))
#define GET_CHR(x)    get_chr ((char*)&x,    sizeof(x))
#define GET_INT(x)    get_int  ((char*)&x,    sizeof(x))

static void mycopy(char *a,char *b,int c)
{
	while(c--) *a++ = *b++;
}


static char block[MAX_NET_BLOCK];

static void upper_case(request *r,char *name)
{
	const char *s = get_value(r,name,0);
	char *p,*q;

	if(!s) return;

	q = p = NEW_STRING(s);
	while(*p) 
	{ 
		if(islower(*p)) 
		*p = toupper(*p); 
		p++;
	}
	set_value(r,name,q);
	FREE(q);
}

static void fill_chr(char *p,const char *q,int n)
{
	char buf[256];
	sprintf(buf,"%*s",-n,q);
	COPY(p,buf,n);
}

static void fill_int(char *p,int m,int n)
{
	char buf[256];
	sprintf(buf,"%0*d",n,m);
	COPY(p,buf,n);
}

static char *get_chr(char *p,int n)
{
	static char buf[256];
	COPY(buf,p,n);
	buf[n] = 0;
	return buf;
}

static int get_int(char *p,int n)
{
	return atoi(get_chr(p,n));
}

static int add_1_value(value *v,int offset,int shift)
{
	_bvalue   bvalue;
	int       len = strlen(v->name + shift);
	int		  off = offset + OFFSET(_bvalue,value) + len;

	memset(&bvalue,'*',sizeof(bvalue));

	strncpy(bvalue.value, v->name + shift,len);

	FILL_INT(bvalue.vlen,  len);
	FILL_INT(bvalue.ptype, 
		*(v->name + shift) == '"' ? STR_TYPE :
		(is_number(v->name + shift)?NUM_TYPE:CHR_TYPE)
		);

	COPY(block+offset,&bvalue,OFFSET(_bvalue,value)+len);

	return off;
}

static int add_1_parameter(parameter *p,int offset)
{
	_bparam	  bparam;
	value     *v  = p->default_values?p->default_values:p->values;
	int       cnt = 0;
	int       len = strlen(p->name);
	int		  off = offset + OFFSET(_bparam,param) + len;

	memset(&bparam,'*',sizeof(bparam));

	strncpy(bparam.param, p->name,len);

	FILL_INT(bparam.plen,  len);

	while(v)
	{
		int shift = 0;
		if(strcmp(p->name,"DATE") == 0 && strlen(v->name) == 8) shift = 2;

		off = add_1_value(v,off,shift);
		cnt++;
		v = v->next;
	}
	

	FILL_INT(bparam.nvalues,cnt);
	COPY(block+offset,&bparam,OFFSET(_bparam,param)+len);
	return off;
}

static int add_1_request(request *r,int offset)
{
	_subhdr   subhdr;
	parameter *p  = r->params;
	int       cnt = 0;
	int		  off = offset + SUB_SIZE;


	memset(&subhdr,'*',sizeof(subhdr));

	FILL_INT(subhdr.shdrlen,OFFSET(_subhdr,nparms));
	FILL_CHR(subhdr.action,r->name);
	FILL_INT(subhdr.mode,1);
	FILL_INT(subhdr.ccode,0);
	FILL_INT(subhdr.escode,0);

	while(p)
	{
		if(*(p->name) != '_' )
		{
			off = add_1_parameter(p,off);
			cnt++;
		}
		p = p->next;
	}
	

	FILL_INT(subhdr.nparms,cnt);
	FILL_INT(subhdr.blklen,off-offset);
	COPY(block+offset,&subhdr,SUB_SIZE);
	return off;
}


char *buildblock(request *r,int actcls)
{
	_prmhdr prmhdr;
	int       cnt = 0;
	int		  off = PRM_SIZE;
	char buf[10]; 
	const char *s;
	long t;
	request *e = (request*)get_environ();

	memset(block,0,sizeof(block));

	memset(&prmhdr,'*',sizeof(prmhdr));

	FILL_INT(prmhdr.hdrlen,PRM_SIZE);

	s = getenv("ARCH");
	s = s?s:"unk";
	FILL_CHR(prmhdr.userid,s); 
	FILL_CHR(prmhdr.compid,s); 

	s = get_value(e,"user",0);
	FILL_CHR(prmhdr.sendid,s?s:"???");


	/* Expever should be uppercase */

	upper_case(r,"EXPVER");
	upper_case(r,"CFSPATH");
	upper_case(r,"PASSWORD");


	s = get_value(e,"account",0);
	FILL_CHR(prmhdr.account,s?s:"ECFSUN");

	gethostname(buf,sizeof(buf));
	FILL_CHR(prmhdr.recid,buf);

	if( (s = getenv("QSUB_REQNAME")) )
	{
		strncpy(buf,s,9);
		buf[9] = 0;
	}

	FILL_CHR(prmhdr.userjob,buf);
	FILL_INT(prmhdr.pswitch,mars.debug?8:0);
	FILL_INT(prmhdr.hdrvers,2);

	time(&t);
	strftime(buf,10,"%y%m%d",gmtime(&t));
	FILL_CHR(prmhdr.subdate,buf);
	FILL_INT(prmhdr.recdate,getpid()); 

	strftime(buf,10,"%H%M%S",gmtime(&t));
	FILL_CHR(prmhdr.subtime,buf);
	FILL_CHR(prmhdr.rectime,buf);
	FILL_INT(prmhdr.nihdr,  0);
	FILL_INT(prmhdr.actmod, 1);
	FILL_INT(prmhdr.suberr, 0);
	FILL_INT(prmhdr.status, 1);
	FILL_INT(prmhdr.actcls, actcls);

#ifdef ALL_REQS
	while(r)
	{
		off = add_1_request(r,off);
		cnt++;
		r = r->next;
	}
#else

	off = add_1_request(r,off);
	cnt++;

#endif


	FILL_INT(prmhdr.noshdr, cnt);
 	FILL_INT(prmhdr.msglen, off);
	COPY(block,&prmhdr,PRM_SIZE);

	if(mars.debug)
	{
		request *u = procces_reply(block);
		print_one_request(u);
		free_all_requests(u);
	}

	return block;
}

static request *procces_subhdr(int *offset)
{
	_subhdr subhdr;
	char    buf[256];
	int n;
	int i;
	int off   = *offset;
	request   *r1;
	parameter *p1,*p2;
	value     *v1,*v2;

	COPY(&subhdr,block+(*offset),sizeof(subhdr));	

	*offset += GET_INT(subhdr.shdrlen);

	strcpy(buf,GET_CHR(subhdr.action));
	i = strlen(buf);
	while(i>=0 && (buf[i] == 0 || buf[i] == ' ')) buf[i--] = 0;

	r1 = new_request(strcache(buf),NULL);

	n = GET_INT(subhdr.nparms);

	off += SUB_SIZE;

	for(i=0;i<n;i++)
	{
		int len,j,cnt;
		_bparam bparam;

		COPY(&bparam,block+off,sizeof(bparam));

		len = GET_INT(bparam.plen);
		cnt = GET_INT(bparam.nvalues);

		strncpy(buf,bparam.param,len); buf[len] = 0;

		off += len + OFFSET(_bparam,param);


		p2 = new_parameter(strcache(buf),NULL);
		if(r1->params == NULL)
			r1->params = p2;
		else 
			p1->next = p2;
		p1 = p2;


		for(j=0;j<cnt;j++)
		{
			_bvalue bvalue;
			COPY(&bvalue,block+off,sizeof(bvalue));

			len = GET_INT(bvalue.vlen);

			strncpy(buf,bvalue.value,len); buf[len] = 0;

			v2 = new_value(strcache(buf));
			if(p2->values == NULL)
				p2->values = v2;
			else
				v1->next = v2;
			v1 = v2;

			off += len + OFFSET(_bvalue,value);
		}

	}
	return r1;

}

int actcls(char *block)
{
	_prmhdr prmhdr;
	COPY(&prmhdr,block,sizeof(prmhdr));
	return GET_INT(prmhdr.actcls);
}

void geterrors(char *block,int req,int *e1,int *e2,int *e3)
{
	_prmhdr prmhdr;
	_subhdr subhdr;
	int n,i;
	int	off = PRM_SIZE;

	COPY(&prmhdr,block,sizeof(prmhdr));

	n   = GET_INT(prmhdr.noshdr);
	*e1 = GET_INT(prmhdr.suberr);

	for(i=0;i<n;i++)
	{
		COPY(&subhdr,block+off,sizeof(subhdr));	
		*e2 = GET_INT(subhdr.ccode);
		*e3 = GET_INT(subhdr.escode);
		off += GET_INT(subhdr.shdrlen);
		if(i == req) break;
	}


}

request *procces_reply(char *block)
{
	_prmhdr prmhdr;
	request *r1 = NULL;
	request *r2 = NULL;
	request *r3 = NULL;
	int     off = PRM_SIZE;

	int i;
	int n;

	COPY(&prmhdr,block,sizeof(prmhdr));

	n = GET_INT(prmhdr.noshdr);

	for(i=0;i<n;i++)
	{

		r3 = procces_subhdr(&off);

		if(r1==NULL) r1 = r3; else  r2->next = r3;
		r2 = r3;

	}
	return r1;

}

