#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <syslog.h>
#include <stdint.h>
#include <math.h>

#include <confuse.h>

#include "libyuarel/yuarel.h"
//#include "libfixmath/libfixmath/fixmath.h"
#include "sensob/sensob.h"


//#include "../../status.h"
#include "../../feeder.h"



cfg_opt_t metaid_opts[] =
{
    CFG_INT((char *)"metaid", 0, CFGF_NONE),
    CFG_INT_LIST((char *)"sensorid", 0, CFGF_NONE),
    CFG_END()
};

cfg_opt_t device_opts[] =
{
    CFG_STR((char *)"unit_id", 0, CFGF_NONE),
    CFG_SEC((char *)"metaid", metaid_opts, CFGF_MULTI | CFGF_TITLE),
    CFG_END()
};

cfg_opt_t cfg_opts[] = 
{
    CFG_SEC((char *)"device", device_opts, CFGF_MULTI | CFGF_TITLE),
    CFG_END()
};




#define RECVD_HISTORY_SIZE 32//must be dividable by 8

typedef struct unit_t
{
    uint32_t id;
    uint8_t recvd[RECVD_HISTORY_SIZE];
    uint8_t pos, recvd_size;
    uint32_t seq;
    uint8_t ack;
    struct unit_t * next;
} unit_t;

static struct unit_t * units, * unit_now;

//static uint32_t seq;
static uint8_t ack;

static void (* feederLog)(int priority, const char * fmt, ...);

int setLog(void * func)
{
    feederLog = (void(*)(int, const char * , ...))func;
    return 0;
}


int init(void * param)
{
    (void)param;
    
//    seq = 0;
    units = unit_now = NULL;
    ack = 0;

    return 0;
}

struct unit_t * unitFind(struct unit_t * unit, uint32_t id)
{
    while (unit != NULL)
    {
	if (unit->id == id)
	    return unit;
	unit = unit->next;
    }
    return NULL;
}

struct unit_t * unitCreate(uint32_t id)
{
    struct unit_t * unit;
    unit = (struct unit_t *)malloc(sizeof(struct unit_t));
    unit->id = id;
    memset(unit->recvd, 0, RECVD_HISTORY_SIZE);
    unit->seq = 0;
    unit->pos = 0;
    unit->next = NULL;
    return unit;
}


void unitUpdateRecvd(struct unit_t * unit, uint8_t flag)
{
    unit->recvd[unit->pos++] = flag;
    if (unit->pos > RECVD_HISTORY_SIZE)
	unit->pos = 0;
}

void stringToArrayn(uint8_t * data, char * str, uint16_t len)
{
    uint16_t i;
    uint8_t j;
    char c;

    for (i = 0; i < len; i++)
    {
	data[i] = 0;
	for (j = 0; j < 2; j++)
        {
            c = str[i * 2 + j];
            if (c >= 'a')	//to high case
        	c -= 0x20;
            data[i] += (c - ((c < 'A')?'0':'7')) << ((j == 0)?4:0);
        }
    }
}

void arraynToString(char * str, uint8_t * data, uint16_t len)
{
    uint16_t i;
    uint8_t j;
    char c;

    for (i = 0; i < len; i++)
    {
        for (j = 0; j < 2; j++)
        {
            c = (data[i] >> ((j == 0)?4:0)) & 0x0F;
            str[i * 2 + j] = c + ((c < 0x0A)?'0':'7');
        }
    }
    str[len * 2] = '\0';
}

char * yarel_get_param(struct yuarel_param * params, int params_count, const char * param_name)
{
    int i;

    i = 0;
    while (i < params_count)
    {
	if (strcmp(params[i].key, param_name) == 0)
	    return params[i].val;
	i++;
    }
    return NULL;
}

unsigned int
process(void *lib_data, int socket, unsigned char *data,
        unsigned int length, unsigned long long int *id, time_t * tm,
        double *result_array, uint64_t * sensors, unsigned int *type)

{
    struct yuarel url;
    int p, i;
//    char * parts[3];
    struct yuarel_param params[16];
    char *c;
    struct unit_t * unit;
    uint32_t seq;
    uint8_t params_data_bin[12];
    uint8_t params_data_len;
    uint64_t pid;
    unsigned int res;

    (void)lib_data;
    (void)socket;
    (void)type;

    if ((data == NULL) || (length == 0))
	return 0;

    feederLog(LOG_DEBUG, "sigfox: raw data in:\n%s\n", (char *)data);

    seq = 0;
    *id = 0;

    data[length] = 0;
    if (yuarel_parse(&url, (char *)(data + 4)) != 0)//skip leading GET
    {
        feederLog(LOG_WARNING, "sigfox: Yuarel can not parse input\n");
	return 0;
    }
    
    c = strchr(url.query, ' ');
    if (c != NULL)
	*c = 0;

//    feederLog(LOG_DEBUG, "sigfox: scheme: %s\n", url.scheme);
//    feederLog(LOG_DEBUG, "sigfox: host: %s\n", url.host);pada?
//    feederLog(LOG_DEBUG, "sigfox: port: %d\n", url.port);
//    feederLog(LOG_DEBUG, "sigfox: path: %s\n", url.path);
    feederLog(LOG_DEBUG, "sigfox: query: %s\n", url.query);
//    feederLog(LOG_DEBUG, "sigfox: fragment: %s\n", url.fragment);pada?

/*
    if (3 != yuarel_split_path(url.path, parts, 3)) {
	fprintf(stderr, "Could not split path!\n");
	return 0;
    }

    feederLog(LOG_DEBUG, "sigfox: path parts: %s, %s, %s\n", parts[0], parts[1], parts[2]);
    feederLog(LOG_DEBUG, "sigfox: Query string parameters:\n");
*/
    p = yuarel_parse_query(url.query, '&', params, 16);
    
    params_data_len = strlen(yarel_get_param(params, p, "data")) / 2;
    stringToArrayn(params_data_bin, yarel_get_param(params, p, "data"), params_data_len);

    feederLog(LOG_DEBUG, "sigfox: binary data: ");
    for (i = 0; i < params_data_len; i++)
	feederLog(LOG_DEBUG, "%02X ", params_data_bin[i]);
    feederLog(LOG_DEBUG, "\n");
    	
    pid = strtoll(yarel_get_param(params, p, "unit_id"), NULL, 16);
    seq = atoi(yarel_get_param(params, p, "seq"));


    if (params_data_bin[0] == 0xFF)//this is sensob message
    {
	feederLog(LOG_DEBUG, "sigfox: sensob detected\n");
	feederLog(LOG_WARNING, "sigfox: sensob protocol not supported\n");
    }
    //    else if (json_data_data_bin[0] == 0xFE)//this is command todo: nebo rozlisit podle portu
    else if (params_data_bin[0] > 0xC0)//this is command todo: nebo rozlisit podle portu
    {
	feederLog(LOG_DEBUG, "sigfox: cmd detected\n");
	//cmdParse(params_data_bin, params_data_len, pid);
    }
    else if (params_data_bin[0] >= 0xA0) //this is sensob tiny message
    {
    	feederLog(LOG_DEBUG, "sigfox: sensob tiny detected\n");
    	
    	res = sensobtiny_parse(params_data_bin, params_data_len, strtol(yarel_get_param(params, p, "ts"), NULL, 10), tm, result_array, sensors, type);
    	*id = pid;
    }
    else //this is sensob lite message
    {
	feederLog(LOG_DEBUG, "sigfox: sensob lite detected\n");

	res = sensoblite_parse(params_data_bin, params_data_len, tm, result_array, sensors, type);
	*id = pid;
    }
/*    
    i = 0;
    ack = 0;
    while (i < p)
    {
	feederLog(LOG_DEBUG, "sigfox: %s: %s\n", params[i].key, params[i].val);
	
	if (strcmp(params[i].key, "date") == 0)
	    *tm = atoll(params[i].val);
	if (strcmp(params[i].key, "sensor_id") == 0)
	    sensors[0] = atoll(params[i].val);
	if (strcmp(params[i].key, "unit_id") == 0)
	{
	    *id = strtoll(params[i].val, NULL, 16);
	}
	if (strcmp(params[i].key, "value") == 0)
//	    result_array[0] = atof(params[i].val);
	    result_array[0] = fix16_to_float(atoll(params[i].val));
	if (strcmp(params[i].key, "seq") == 0)
	    seq = atoi(params[i].val);
	if (strcmp(params[i].key, "ack") == 0)
	    ack = (strcmp(params[i].val, "true") == 0)?1:0;

	i++;
    }*/
    *type = VALUES_TYPE_OBS;

    unit = unitFind(units, *id);
    if (unit == NULL)
    {
        feederLog(LOG_DEBUG, "sigfox: creating new unit %ld\n", *id);

	unit = unitCreate(*id);
	if (units != NULL)
	{
	    unit->next = units;
	}
	units = unit;
    }
    else
        feederLog(LOG_DEBUG, "sigfox: unit %ld found\n", unit->id);
    
    if (unit->seq == 0)//todo: je nula opravdu vyjimecna?
        unit->seq = seq;
    else
    {
	while (unit->seq++, unit->seq < seq)
	    unitUpdateRecvd(unit, 0);
    }
    unitUpdateRecvd(unit, 1);
    
    unit->ack = ack;
    
    unit_now = unit;
    
    if (*id != 0)
	return res;

    return 0;
}

int reply(void * lib_data, int socket, unsigned char *data)
{
//    uint8_t i, j;
    struct unit_t * unit;
    char * buf;
    

    (void)lib_data;
    (void)socket;
    
    unit = unit_now;
    
    if (unit == NULL)
    {
	//todo: odpovededet nejakym chybou
	return 0;
    }
    
    if (unit->seq == 0)
	return 0;

    time((time_t *)data);
    /*todo
    memset(data + 4, 0, 4);
    for (i = 0; i < RECVD_HISTORY_SIZE; i++)
    {
	
	j = unit->pos - (RECVD_HISTORY_SIZE + i);
	if (j > RECVD_HISTORY_SIZE)	//rotated (less then zero)
	    j += RECVD_HISTORY_SIZE;
	data[4 + i / 8] |= unit->recvd[j] << (i % 8);
    }
*/
    buf = (char *)data;
    time_t now = time(0);
    struct tm tm = *gmtime(&now);
    sprintf(buf, "HTTP/1.0 204 NO CONTENT\r\nServer: feeder\r\nContent-Length: %d\r\nDate: ", 0);
    strftime(buf + strlen(buf), 1000, "%a, %d %b %Y %H:%M:%S %Z", &tm);
    sprintf(buf + strlen(buf), "\r\n\r\n");

    feederLog(LOG_DEBUG, "sigfox: raw data out: \n%s\n", (char *)data);

    feederLog(LOG_DEBUG, "sigfox: replying\n");

    unit->seq = 0;

    return strlen((char *)data);
}
