/*
 * misc.c
 *
 *  Created on: 10-09-2013
 *      Author: Krzysztof Jakubczyk
 */

#include "misc.h"
#include <time.h>
#include "tdefs.h"
#include <string.h>
#include "config.h"

volatile u16 kob_bin_ench=0;
volatile u16 mwd32_mask=0;

u16 bus_out_data[MAX_OUT_CARDS];
u16 bus_out_data_test[MAX_OUT_CARDS];
u16 force_bus_out_data[MAX_OUT_CARDS];

u8 bus_bin_data[MAX_BIN_CARDS];
u8 force_bus_bin_data[MAX_BIN_CARDS];
u8 force_bus_bin_data_ench[MAX_BIN_CARDS];
u8 bus_bin_data_ench[MAX_BIN_CARDS];
u8 force_bus_bin_data_ench[MAX_BIN_CARDS];

u8 bus_an_samples_neg[MAX_AN_CARDS][4];
u16 bus_an_samples_buf[MAX_AN_CARDS][4][SAMPLES_PER_MS*MAIN_FREQ_PERIOD_MS*2];
u8 bus_an_cur_sample_num_3khz = 0;
u8 bus_an_cur_sample_num_xkhz = 0;

u16 tstamp_khz = 0;

u8 bus_an_cur_sample_num = 0;
u32 logman_notify = 0;
volatile u8 ext_sync=0;
volatile u32 timesync_method=0;
volatile u32 timesync_bits=0;
volatile struct in_cards *ic = (struct in_cards *)IN_CARDS_BASE_ADDR;
struct debug_info dbg;

const uint8_t _ytab[2][12] ={ { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } ,
						 	  { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } };


static int is_leap(unsigned y)
{
	y += 1900;
    return (y % 4) == 0 && ((y % 100) != 0 || (y % 400) == 0);
}


uint32_t timegm (const struct tm *tm)
{
	uint32_t res = 0;
	uint16_t i;

  	for (i = 70; i < tm->tm_year; ++i)
    	res += is_leap(i) ? 366 : 365;

  	for (i = 0; i < tm->tm_mon; ++i)
    	res += _ytab[is_leap(tm->tm_year)][i];

  	res += tm->tm_mday - 1;
  	res *= 24;
  	res += tm->tm_hour;
  	res *= 60;
  	res += tm->tm_min;
  	res *= 60;
  	res += tm->tm_sec;

  	return res;
}

void my_gmtime(const uint32_t *timer, struct tm *timep)
{
        uint32_t time = *timer;

        u32 dayclock, dayno;
        int16_t year = EPOCH_YR;

        dayclock = time % SECS_DAY;
        dayno = time / SECS_DAY;

        timep->tm_sec = dayclock % 60;
        timep->tm_min = (dayclock % 3600) / 60;
        timep->tm_hour = dayclock / 3600;
        timep->tm_wday = (dayno + 4) % 7;       /* day 0 was a thursday */
        while (dayno >= YEARSIZE(year)) {
                dayno -= YEARSIZE(year);
                year++;
        }
        timep->tm_year = year - YEAR0;
        timep->tm_yday = dayno;
        timep->tm_mon = 0;
        while (dayno >= _ytab[LEAPYEAR(year)][timep->tm_mon]) {
                dayno -= _ytab[LEAPYEAR(year)][timep->tm_mon];
                timep->tm_mon++;
        }
        timep->tm_mday = dayno + 1;

        timep->tm_isdst = 0;
}

/*
	isindst() - check if date is in daylight save time

	cest_cet_n - 1 when source tm is a summer time, 0 - winter
*/

uint8_t isindst(struct tm *tb, uint8_t cest_cet_n)
{
	uint8_t tmp;

	if (tb->tm_mon < 2 || tb->tm_mon > 9)
		return 0;

	if (tb->tm_mon > 2 && tb->tm_mon < 9)
		return 1;

	tmp=7 - tb->tm_wday + tb->tm_mday;

	if(tb->tm_mon==2) /* march */
	{
		if(tmp>31)
		{
			if(tb->tm_wday==0)
			{
				if ((!cest_cet_n && tb->tm_hour>=2)||(cest_cet_n && tb->tm_hour>=3)) /* it's the last sunday */
					return 1;
				else
					return 0;
			}
			else
				return 1;
		}
		else
			return 0;
	}

	if(tb->tm_mon==9) /* october */
	{
		if(tmp>31)
		{
			if(tb->tm_wday==0)
			{
				if ((!cest_cet_n && tb->tm_hour>=2)||(cest_cet_n && tb->tm_hour>=3)) /* it's the last sunday */
					return 0;
				else
					return 1;
			}
			else
				return 0;
		}
		else
			return 1;
	}

	return 0;
}


struct time_data irigb_process_frame()
{
	u8 i;
	u8 tmp_secs;
	u8 tmp_mins;
	u8 tmp_hour;
	u8 tmp_month;
	u8 tmp_mday;
	u16 tmp_year;
	u16 tmp_yearday;
	u16 tmp;
	u16 tmp2;
	uint32_t secs;
    struct tm tim;
    struct time_data irigb_time;

	if(ic->sync_reg & 0x4000) // IRIG-B sync
	{
		tmp = ic->irigb_p[0] & 0x01FF;
		tmp_secs = (((tmp & 0x1C0) >> 6)*10) + ((tmp>>1) & 0x00F);

		tmp = ic->irigb_p[1] & 0x01FF;
		tmp_mins = (((tmp & 0xE0) >> 5)*10) + (tmp & 0x00F);

		tmp = ic->irigb_p[2] & 0x01FF;
		tmp_hour = (((tmp & 0x60) >> 5)*10) + (tmp & 0x00F);

		tmp = ic->irigb_p[3] & 0x01FF;
		tmp_yearday = (((tmp & 0x01E0) >> 5)*10) + (tmp & 0x00F);
		tmp = ic->irigb_p[4] & 0x01FF;
		tmp_yearday+=((tmp & 0x03)*100);

		tmp = ic->irigb_p[5] & 0x01FF;
		tmp_year = (((tmp & 0x1E0) >> 5)*10) + (tmp & 0x00F);
		tmp_year+=2000;

		tmp2=0;

		if(LEAPYEAR(tmp_year))
		{
			for(i=0;i<12;i++)
			{
				tmp2+=_ytab[1][i];
				if(tmp_yearday<=tmp2)
					break;
			}
			tmp_mday=tmp_yearday-(tmp2-_ytab[1][i]);
		}
		else
		{
			for(i=0;i<12;i++)
			{
				tmp2+=_ytab[0][i];
				if(tmp_yearday<=tmp2)
					break;
			}
			tmp_mday=tmp_yearday-(tmp2-_ytab[0][i]);
		}

		tmp_month=i+1;
		tmp_year-=1900;

		tmp = (ic->irigb_p[6]) & 0x0000001F;
		irigb_time.fix_sat=tmp;
		irigb_time.day=tmp_mday;
		irigb_time.month=tmp_month-1;
		irigb_time.year=tmp_year;
		irigb_time.hour=tmp_hour;
		irigb_time.min=tmp_mins;
		irigb_time.sec=tmp_secs;

		tim.tm_hour = irigb_time.hour;
		tim.tm_min = irigb_time.min;
		tim.tm_sec = irigb_time.sec;

		tim.tm_mday = irigb_time.day;
		tim.tm_mon = irigb_time.month;
		tim.tm_year = irigb_time.year;

		secs=timegm(&tim);
		my_gmtime(&secs,&tim);

		if(isindst(&tim,0))
		{
			tim.tm_hour-=2; /* DST adjust is done by the GPS-Synchro IRIG-B modified protocol */
			tim.tm_isdst=1;
		}
		else
			tim.tm_hour-=1;

		irigb_time.secs=timegm(&tim)+1;

//		if(!validate_time(&irigb_time))
		{
/*			ctime.tv_sec=irigb_time.secs;

			time_flags&=~TIME_INVALID;

			if(tim.tm_isdst)
				time_flags|=TIME_SUMMER;
			else
				time_flags&=~TIME_SUMMER;*/
		}
	}
	else
		memset((u8 *)&irigb_time,0,sizeof(irigb_time));

	return irigb_time;
}

void reload_ic_cfg()
{
	memcpy((void*)ic->bin_ac_mask,(void*)dev_cfg.mwd_ac_mask,sizeof(ic->bin_ac_mask));
}

u8 mod256_cksum(uint8_t *buf, u32 len)
{
    uint8_t cksum;
    u32 i;

    cksum=0;

    for(i=0;i<len;i++)
		cksum+=*(buf++);

    return cksum;
}

int asn_parse(u8 **buf,int *bytes_left, u8 *tag)
{
	int len = 0;
	u8 oct;

	*tag=**buf;
	(*buf)++;
	(*bytes_left)--;


	if(**buf & 0x80)
	{
		oct = **buf&0x7f;
		if(oct>2 || oct==0) // too long for us
			return -1;

		(*buf)++;
		(*bytes_left)--;

		while(oct && *bytes_left)
		{
			len|=**buf;
			oct--;
			(*bytes_left)--;
			(*buf)++;
			if(oct)
				len<<=8;
		}

	}
	else
	{
		len = **buf;
		(*bytes_left)--;
		(*buf)++;
	}

	if (*bytes_left<len)
		return -1;

	return len;
}

void analog_inputs_init()
{
	int i;

	for(i=0;i<MAX_AN_CARDS;i++)
	{
		ic->an_offset[i].l1=32767;
		ic->an_offset[i].l2=32767;
		ic->an_offset[i].l3=32767;
		ic->an_offset[i].l4=32767;
	}

	bus_an_cur_sample_num_3khz = 0;
	bus_an_cur_sample_num = 0;
	memset(bus_an_samples_buf,0,sizeof(bus_an_samples_buf));
}
