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

#include "libyuarel/yuarel.h"

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



#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;
}

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;

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

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

    feederLog(LOG_DEBUG, "rest: 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, "rest: Yuarel can not parse input\n");
	return 0;
    }
    
    c = strchr(url.query, ' ');
    if (c != NULL)
	*c = 0;

//    feederLog(LOG_DEBUG, "rest: scheme: %s\n", url.scheme);
//    feederLog(LOG_DEBUG, "rest: host: %s\n", url.host);pada?
//    feederLog(LOG_DEBUG, "rest: port: %d\n", url.port);
//    feederLog(LOG_DEBUG, "rest: path: %s\n", url.path);
    feederLog(LOG_DEBUG, "rest: query: %s\n", url.query);
//    feederLog(LOG_DEBUG, "rest: 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, "rest: path parts: %s, %s, %s\n", parts[0], parts[1], parts[2]);
    feederLog(LOG_DEBUG, "rest: Query string parameters:\n");
*/
    p = yuarel_parse_query(url.query, '&', params, 16);
    i = 0;
    ack = 0;
    while (i < p)
    {
	feederLog(LOG_DEBUG, "rest: %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 = atoll(params[i].val);
	    *id = strtoll(params[i].val, NULL, 16);
	}
	if (strcmp(params[i].key, "value") == 0)
	    result_array[0] = atof(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, "rest: creating new unit %ld\n", *id);

	unit = unitCreate(*id);
	if (units != NULL)
	{
	    unit->next = units;
	}
	units = unit;
    }
    else
        feederLog(LOG_DEBUG, "rest: 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 1;

    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, "rest: raw data out: \n%s\n", (char *)data);

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

    unit->seq = 0;

    return strlen((char *)data);
}
