/*
 * © 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.
 */

/*

B.Raoult
ECMWF Oct-93

*/

#include "mars.h"
#include <ctype.h>

#ifdef FORTRAN_UPPERCASE
#define bufrex_ BUFREX
#endif
#ifdef FORTRAN_NO_UNDERSCORE
#define bufrex_ bufrex
#endif


#ifdef FORTRAN_UPPERCASE
#define busel_ BUSEL
#endif
#ifdef FORTRAN_NO_UNDERSCORE
#define busel_ busel
#endif

#define MASK(n) ((unsigned char)(~(0xff<<(n))))

unsigned long getbits(unsigned char *p,int skip,int len)
{
	int             s = skip%8;
	int             n = 8-s;
	unsigned long   ret=0;

	p += (skip >> 3); /* skip the bytes */

	if(s) {
		ret = (MASK(n) & *p++);
		len -= n;
	}

	while(len >= 8)
	{
		ret = (ret<<8) + *p++;
		len -= 8;
	}

	ret = (ret << len) + (*p >> (8-len));

	return ret;
}


/* Use these macros only here...
Some BUFR messages have a wrong length (65535) in the key
on purpose. This indicates the length has to be read from
section 0 instead of from the key. Ask Milan for more
details */

#define WRONG_KEY_LENGTH 65535
#ifdef LITTLE_END
#define KEY_LENGTH(k)       ((unsigned int)getbits( (unsigned char*)k->length, 0, 16))
#else
#define KEY_LENGTH(k)       (k->length)
#endif

unsigned long key_length(const char *buffer, const packed_key *k)
{
	unsigned long length = KEY_LENGTH(k);
	if((length == WRONG_KEY_LENGTH) || ( length == 0))
	{
		length = (unsigned long)getbits((unsigned char *)(buffer+4),0,24);
	}
	return length;
}

void set_key_length(packed_key *key, unsigned long keylength)
{
	if(keylength >= WRONG_KEY_LENGTH)
		return;
	SET_KEY_LENGTH(*key,keylength);
}

boolean get_packed_key(char *buffer,packed_key *k)
{
	int version = buffer[7];
    marslog(LOG_DBUG, "getkey version %d\n", version);

	/* skip section 0 , check for BUFR version */
	if(version>1)
		buffer += 8;
	else
		buffer += 4;

	/* check if the key is present */

    marslog(LOG_DBUG, "SEC1_FLAGS %d\n", (int)SEC1_FLAGS(buffer, version));

	if(SEC1_FLAGS(buffer, version))
	{
		buffer += SEC1_LENGTH(buffer);  /* skip section 1 */
		buffer += 4;                    /* skip header of section 2 */
		/* now copy the packed key minus the size of two_byte_n_of_subset */
		memcpy((void*)k, (void*)buffer, sizeof(packed_key));
		return true;
	}

	return false;

}

boolean get_packed_section_1(char *buffer,packed_section_1* section)
{
	/* skip section 0 , check for BUFR version */
	if((unsigned char)buffer[7]>1)
		buffer += 8;
	else
		buffer += 4;

	memcpy((void*) section,buffer,sizeof(packed_section_1));

	return true;
}

int subset_count(char *buffer)
{
	int version = buffer[7];

	/* skip section 0 , check for BUFR version */
	if(version>1)
		buffer += 8;
	else
		buffer += 4;

	/* check if the key is present */

	if(SEC1_FLAGS(buffer, version))
	{
		char *p;
		unsigned long sec2len;

		buffer += SEC1_LENGTH(buffer);  /* skip section 1 */
		p = buffer;						/* p pointing to section 2 */
		/*--------------------------------------------------------*/
		/* There is a number of subsets field in the packed key   */
		/* which is either 8 bits or 16 bits long depending on	  */
		/* subtypes. It is also present at the beginning of 	  */
		/* section 3 where it is coded using 16 bits.             */
		/*--------------------------------------------------------*/
		sec2len = getbits((unsigned char*) p,0,24);  /* get len of section 2 */
		p += sec2len;							  /* p pointing to section 3 */
		/*------------------------------------------------------------*/
		/* get the number of subsets skipping the length of section 3 */
		/* ( 3 bytes) and a byte set to 0 							  */
		/*------------------------------------------------------------*/
		return getbits((unsigned char*) p,32,16);
	}

	return 0;
}

int bufr_sat_id(char *buffer,packed_key* k)
{
	unsigned char *p = (unsigned char*) k;
	int id;
	int offset = 192;

	int n = subset_count(buffer);
	if (n > 255 || (k->header.subtype >= 121 && k->header.subtype <= 130)
					|| k->header.subtype==31)
	{
		offset = 200;
	}

	id = p[offset/8];
	id = id << 8;
	id += p[(offset/8)+1];

	return id;
}

void set_bufr_sat_id(char* buffer,int id,packed_key* k)
{
	char *p = (char*) k;
	int offset = 192;

	int n = subset_count(buffer);
	if (n > 255 || (k->header.subtype >= 121 && k->header.subtype <= 130)
					|| k->header.subtype==31)
	{
		offset = 200;
	}

#ifdef LITTLE_END
	p[offset/8 + 1] = id >> 8;
	p[offset/8] = id;
#else
	p[offset/8] = id >> 8;
	p[offset/8 + 1] = id;
#endif
}

static packed_key* get_packed_key_addr(char *buffer)
{
	int version = buffer[7];

	/* skip section 0 , check for BUFR version */
	if(version>1)
		buffer += 8;
	else
		buffer += 4;

	/* check if the key is present */

	if(SEC1_FLAGS(buffer,version))
	{
		buffer += SEC1_LENGTH(buffer);  /* skip section 1 */
		buffer += 4;                    /* skip header of section 2 */
		return (packed_key*) buffer;
	}

	return NULL;
}

boolean replace_key(char* buffer,packed_key* key)
{
	packed_key* k = get_packed_key_addr(buffer);
	if (k)
	{
		memcpy(k,key,sizeof(packed_key));
		return true;
	}
	return false;
}

boolean patch_key_date(char* buffer,int y,int m,int d,int H, int M,
	int S)
{
	packed_key* k = get_packed_key_addr(buffer);
	if (k)
	{

		packed_key p;
		memcpy(&p,k,sizeof(packed_key));

		if (H == 24 && M == 0 && S == 0)
		{
			H = 23;
			M = 59;
			S = 59;
		}

#ifdef LITTLE_END
		marslog( LOG_WARN, "Set date & time not implemented on little-endian architecture");
		marslog( LOG_EXIT, "If needed, bufr.c needs modifications. Exiting...");
#else
		p.header.year   = y;
		p.header.month  = m;
		p.header.day    = d;
		p.header.hour   = H;
		p.header.minute = M;
		p.header.second = S;
#endif
		memcpy(k,&p,sizeof(packed_key));
		marslog(LOG_INFO,"New date %d-%d-%d %d:%d:%d (%d)",
			y,m,d,H,M,S,sizeof(packed_key));

		return true;
	}

	return false;
}

boolean patch_key_ident(char* buffer,int ident)
{
#if 0
	packed_key* k = get_packed_key_addr(buffer);
	if (k)
	{
		char s[10];
		char p[10];

		packed_key p;
		memcpy(&p,k,sizeof(packed_key));

		/* put the ident value followed by spaces in 9 chars.  */
		sprintf(p,"%d",ident);
		memset(s,' ',9);
		s[9] = 0;
		memcpy(s,p,strlen(p));

/*  NOT FINISHED */

		memcpy(k,&p,sizeof(packed_key));

		return true;
	}
#endif

	return false;
}

boolean patch_key_length(char* buffer,unsigned int new_value)
{
	packed_key* k = get_packed_key_addr(buffer);

	if(new_value == WRONG_KEY_LENGTH)
	{
		marslog(LOG_WARN,"Cannot set rdbkey length to %d",new_value);
		return false;
	}

	if (k)
	{
		packed_key p;
		memcpy(&p,k,sizeof(packed_key));
		SET_KEY_LENGTH( p, new_value );
		memcpy(k,&p,sizeof(packed_key));
		return true;
	}

	return false;
}

boolean shift_packed_key_ident(char* buffer)
{
	packed_key* k = get_packed_key_addr(buffer);
	if (k)
	{
		char *ident = KEY_IDENT(k);
		int i = 0;
		int j;
		while (i < 9 && ident[i] == ' ') /* ident len = 9 */
			i++;
		if (i < 9)
			memcpy(ident,&ident[i],9-i);
		for (j = 8; j > 9-i; j--)
			ident[j] = ' ';
		return true;
	}

	return false;
}

void set_bufr_mars_type(request *r, const packed_key* key)
{
	set_value(r,"TYPE","OB");

	if(KEY_TYPE(key) == BUFR_TYPE_TRACK)
	{
		set_value(r,"TYPE","TF");
	}
}

err bufr_to_request(request *r,char *buffer,long length)
{
	packed_key  ky;
	packed_key *k = &ky;
	char buf[10];
	int  i;
	char *p;

	if(!get_packed_key(buffer,k))
	{
		marslog(LOG_WARN,"BUFR message without key found");
		return -1;
	}

	if(mars.debug)
		print_packed_key(buffer,k);

	set_bufr_mars_type(r,k);

	set_value(r,"_BUFR_TYPE","%d",KEY_TYPE(k));
	set_value(r,"_BUFR_SUBTYPE","%d",KEY_SUBTYPE(k));
	set_value(r,"OBSTYPE","%d",KEY_SUBTYPE(k));

	set_value(r,"DATE","%04d%02d%02d",KEY_YEAR(k),KEY_MONTH(k),KEY_DAY(k));
	set_value(r,"TIME","%02d%02d",KEY_HOUR(k),KEY_MINUTE(k));


	if(IS_SATTELITE(k))
	{
		/* set_value(r,"_IS_SATTELITE","true"); */

		set_value(r,"_LATITUDE1","%.4f",UNLAT(KEY_LATITUDE1(k)));
		set_value(r,"_LATITUDE2","%.4f",UNLAT(KEY_LATITUDE2(k)));
		set_value(r,"_LONGITUDE1","%.4f",UNLON(KEY_LONGITUDE1(k)));
		set_value(r,"_LONGITUDE2","%.4f",UNLON(KEY_LONGITUDE2(k)));
		/* set_value(r,"IDENT","%d",KEY_IDSAT(k)); */
	}
	else
	{
		/* set_value(r,"_IS_SATTELITE","false"); */

		set_value(r,"_LATITUDE1","%.4f",UNLAT(KEY_LATITUDE(k)));
		set_value(r,"_LATITUDE2","%.4f",UNLAT(KEY_LATITUDE(k)));
		set_value(r,"_LONGITUDE1","%.4f",UNLON(KEY_LONGITUDE(k)));
		set_value(r,"_LONGITUDE2","%.4f",UNLON(KEY_LONGITUDE(k)));

		memcpy(buf,KEY_IDENT(k),9);
		i = 0;
		p = buf;
		buf[9] = 0;

		while(*p) {
			if(isalnum(*p)) buf[i++] = *p;
			p++;
		}
		buf[i] = 0;
		set_value(r,"IDENT",buf);
	}

	set_value(r,"_NOBS","%d",KEY_NOBS(k));
	set_value(r,"_RDBDAY","%d",KEY_RDBDAY(k));
	set_value(r,"_RDBHOUR","%d",KEY_RDBHOUR(k));
	set_value(r,"_RDBMINUTE","%d",KEY_RDBMINUTE(k));
	set_value(r,"_RDBSECOND","%d",KEY_RDBSECOND(k));

	set_value(r,"_RECDAY","%d",KEY_RECDAY(k));
	set_value(r,"_RECHOUR","%d",KEY_RECHOUR(k));
	set_value(r,"_RECMINUTE","%d",KEY_RECMINUTE(k));
	set_value(r,"_RECSECOND","%d",KEY_RECSECOND(k));

	set_value(r,"_CORR1","%d",KEY_CORR1(k));
	set_value(r,"_CORR2","%d",KEY_CORR2(k));
	set_value(r,"_CORR3","%d",KEY_CORR3(k));
	set_value(r,"_CORR4","%d",KEY_CORR4(k));

	set_value(r,"_PART1","%d",KEY_PART1(k));
	set_value(r,"_PART2","%d",KEY_PART2(k));
	set_value(r,"_PART3","%d",KEY_PART3(k));
	set_value(r,"_PART4","%d",KEY_PART4(k));

	set_value(r,"_QC","%d",KEY_QC(k));


	if(mars.debug)
		print_one_request(r);

	return 0;
}

boolean verify_bufr_key(const char* buffer, long length, const packed_key* key)
{
	boolean ok = true;
	unsigned long klength = key_length(buffer,key);

	if (klength != length)
	{
		marslog(LOG_EROR,"Wrong key length in bufr message %d != %d",klength
			,length);
		return false;
	}

	{
		char *p = (char*) buffer + length -4;
		ok = (p[0] == '7' && p[1] == '7' && p[2] == '7'
				&& p[3] == '7');
		if (!ok)
		{
			marslog(LOG_EROR,"7777 not found at offset+key length");
			return false;

		}
	}
	{
		char s[100];
		long date = KEY_YEAR(key) * 10000 +
			    KEY_MONTH(key) * 100 +
			    KEY_DAY(key);
		if (mars_julian_to_date(mars_date_to_julian(date),true) != date)
		{
			marslog(LOG_EROR,"date is weird %d",date);
			return false;
		}

		if ( KEY_HOUR(key) >= 24)
		{
			print_key_time(key,s);
			marslog(LOG_EROR,"time is weird %s",s);
			return false;
		}

		if ( KEY_MINUTE(key) >= 60)
		{
			print_key_time(key,s);
			marslog(LOG_EROR,"time is weird %s",s);
			return false;
		}

		if ( KEY_SECOND(key) >= 60)
		{
			print_key_time(key,s);
			marslog(LOG_EROR,"time is weird %s",s);
			return false;
		}
	}

	return ok;
}

static void zero_spaces(char* p)
{
	while (*p)
	{
		if (*p == ' ')
			*p = '0';
		p++;
	}
}

void print_key_date(const packed_key* keyptr,char* s)
{
	sprintf(s,"%2d/%2d/%4d",KEY_DAY(keyptr),KEY_MONTH(keyptr),KEY_YEAR(keyptr));
	zero_spaces(s);
}

void print_key_time(const packed_key* keyptr,char* s)
{
	sprintf(s,"%2d:%2d:%2d",KEY_HOUR(keyptr),
						  KEY_MINUTE(keyptr),
						  KEY_SECOND(keyptr));
	zero_spaces(s);
}

static void set_subset_count(char* buffer,packed_key* k)
{
	char* p = (char*) k;
	p[23] = subset_count(buffer);
}

int build_packed_key(char* buffer, fortint length, packed_key* k)
{
	fortint kerr = 0;

#if 0

#define KVALS 			80000

	fortint ksup[9];
	fortint ksec0[3];
	fortint ksec1[100];
	fortint ksec2[64];
	fortint ksec3[4];
	fortint ksec4[2];

	fortfloat values[KVALS];
	char cnames[20000*64];
	char cunits[20000*24];
	char cvals[KVALS*80];

	int i;
	char name[65];
	fortint kvals = KVALS;
	fortint kelem = KVALS / subset_count(buffer);
	fortint ktdexl;


	/* for busel */
	fortint ktdlst[2000];
	fortint ktdexp[2000];

	int	year	= -1;
	int month	= -1;
	int day		= -1;
	int hour	= -1;
	int	minute	= -1;
	int	second	= -1;
	int	block	= -1;
	int station	= -1;
	int	id		= -1;
	int	id_type	= 0;
	int lat		= -1;
	int	lon		= -1;

	memset((void*) k,0,sizeof(*k));

	bufrex_(&length,
	    buffer,
	    ksup,
	    ksec0, ksec1, ksec2, ksec3, ksec4,

	    &kelem,

	    cnames,
	    cunits,

	    &kvals,

	    values,
	    cvals,
	    &kerr,

	    64,
	    24,
	    80);
	if (kerr)
		return kerr;


	busel_(&kelem,ktdlst,&ktdexl,ktdexp,&kerr);
	if (kerr)
		return kerr;

	for (i = 0; i < 13; i++)
	{
		switch (ktdexp[i])
		{
			case 1006:
			case 1007:
			case 1005:
			case 1008:
			case 1011:
				id = i;
				id_type = ktdexp[i];
				break;
			case 1001:
				block = i;
				id_type++;
				break;
			case 1002:
				id_type++;
				station = i;
				break;
			case 4001:
				year = i;
				break;
			case 4002:
				month = i;
				break;
			case 4003:
				day = i;
				break;
			case 4004:
				hour = i;
				break;
			case 4005:
				minute = i;
				break;
			case 4006:
				second = i;
				break;
			case 5001:
				lat = i;
				break;
			case 6001:
				lon = i;
				break;
			case 5002:
				lat = i;
				break;
			case 6002:
				lon = i;
				break;
		}
	}

	if (year == -1 || month == -1 || day == -1 || hour == -1 || minute == -1 ||
		  lat == -1 || lon == -1)
		return -10;

	KEY_YEAR(k) 	= values[year];
	KEY_MONTH(k)	= values[month];
	KEY_DAY(k)		= values[day];
	KEY_HOUR(k)		= values[hour];
	KEY_MINUTE(k)	= values[minute];
	KEY_SECOND(k)	= second == -1 ? 0 : values[second];

	/* id */
	switch (id_type)
	{
		char ident[10];
		int i;
		int indx;
		int l;

		case 1007: /* SATELLITE IDENTIFIER INDEX */
			set_bufr_sat_id(buffer,values[id],k);
			break;

		case 1005: /* BUOY/PLATFORM IDENTIFIER INDEX */
			i = values[id];
			set_bufr_sat_id(buffer,i,k);
			sprintf(ident,"%5d",i);
			zero_spaces(ident);
			memcpy(KEY_IDENT(k),ident,5);
			break;

		case 1006: /* AIRCRAFT FLIGHT NUMBER INDEX */
		case 1008: /* AIRCRAFT REGISTRATION NUMBER INDEX */
		case 1011: /* SHIPS' CALL SIGN INDEX */
			indx = (int) values[id] / 1000; /* index in cvals where data is */
			l = (int) values[id] % 1000; 	/* length of data in cvals */
			for (i = 0; i < l; i++)
				KEY_IDENT(k)[i] = cvals[indx++];
			break;

		case 2:    /* WMO BLOCK AND STATION NUMBER */
			i = values[block]*1000+values[station];
			set_bufr_sat_id(buffer,i,k);
			sprintf(ident,"%5d",i);
			zero_spaces(ident);
			memcpy(KEY_IDENT(k),ident,5);
			break;

		default:
			kerr = -9;
			break;
	}
	if (kerr)
		return kerr;

	if (abs(values[lon] - 1.7E38) < 10.E-11 ||
		abs(values[lat] - 1.7E38) < 10.E-11)
		return -8;

#if 0
	printf("LAT: %d\n",(int) (values[lat]) * 100000.0+9000000.0));
	printf("LON: %d\n",(int) ((values[lon]+180) * 100000.0+18000000.0));
#endif

	k->length = length;
	set_subset_count(buffer,k);

#endif
	return kerr;
}


void print_packed_key(char* buffer,packed_key* k)
{
	printf("------------------------------------------------\n");
	printf("------------------------------------------------\n");
	printf("type                  %d\n",KEY_TYPE(k));
	printf("subtype               %d\n",KEY_SUBTYPE(k));
	printf("year                  %d\n",KEY_YEAR(k));
	printf("month                 %d\n",KEY_MONTH(k));
	printf("day                   %d\n",KEY_DAY(k));
	printf("hour                  %d\n",KEY_HOUR(k));
	printf("minute                %d\n",KEY_MINUTE(k));
	printf("second                %d\n",KEY_SECOND(k));
	printf("--------------------------------------------\n");
	printf("latitude1             %lu\n",KEY_LATITUDE1(k));
	printf("longitude1            %lu\n",KEY_LONGITUDE1(k));
	if (IS_SATTELITE(k))
	{
		printf("latitude2             %lu\n",KEY_LATITUDE2(k));
		printf("longitude2            %lu\n",KEY_LONGITUDE2(k));
	}
	printf("------------------------------------------------\n");
	printf("length                %lu\n",key_length(buffer,k));
	printf("number of subsets     %lu\n",KEY_NOBS(k));
	printf("------------------------------------------------\n");
	if (IS_SATTELITE(k))
		printf("ident                 %d\n", bufr_sat_id(buffer,k));
	else
	{
		char id[6];
		memcpy(id,KEY_IDENT(k),5);
		id[5] = 0;
		printf("ident                 %s\n", id);
	}
	printf("\n");
}
