#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <sys/time.h>
#include <sys/select.h>

#define DEFAULT_UART_DEVICE			"/dev/ttyACM0"
#define DEFAULT_UART_SPEED			B9600
#define DEFAULT_UART_RESPONSE_TIMEOUT_MS	50

#define ESCAPED_DATA_MARKER  "serial_data"
#define HEX_ONLY_DATA_MARKER "hex_serial_data"
#define WAIT_RESPONSE_MARKER "wait_response"
#define UART_DEVICE_MARKER   "uart_device"
#define UART_SPEED_MARKER    "uart_speed"
#define UART_TIMEOUT_MARKER  "uart_timeout"

int is_hex_char(char c) {
    if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')  || (c >= 'a' && c <= 'f'))
	return 1;
    return 0;
}

int is_valid_escaped_hex(const char* s) {
    if (*s == '%' && (*(s+1) && is_hex_char(*(s+1))) && (*(s+2) && is_hex_char(*(s+2))))
	return 1;
    return 0;
}

int is_valid_hex(const char* s) {
    if (is_hex_char(*s) && is_hex_char(*(s+1)))
	return 1;
    return 0;
}

void unescape(const char* s, unsigned char **buf, int* len) {
    char* p;
    unsigned char* rp;
    int val;
    int l;

    for (p=(char*)s, l=0; *p; l++, p++) {
	if (is_valid_escaped_hex(p))
	    p += 2;
    }

    *len = 0;
    *buf = malloc(l);
    if (!*buf) return;

    for (p=(char*)s, rp=*buf; *p; p++, rp++, (*len)++) {
	if (*p == '+')
	    *rp = ' ';
	else if (is_valid_escaped_hex(p)) {
	    sscanf(p+1, "%02x", &val);
	    *rp = (unsigned char)(val & 0xff);
	    p += 2;
	} else
	    *rp = *p;
    }
}

void dual_unescape(const char* s, unsigned char **buf, int* len) {
    unsigned char *fbuf;
    int flen;
    char* s2;

    *buf = NULL;
    *len = 0;

    unescape(s, &fbuf, &flen);
    if (flen == 0) return;

    s2 = malloc(flen + 1);
    if (!s2) {
	free(fbuf);
	return;
    }

    memcpy(s2, fbuf, flen);
    s2[flen] = 0;
    free(fbuf);

    unescape(s2, buf, len);
    free(s2);
}

char* unescape_string(const char* s) {
    char* p;
    unsigned char* rp;
    char* buf;
    int val;
    int l;

    for (p=(char*)s, l=0; *p; l++, p++) {
	if (is_valid_escaped_hex(p))
	    p += 2;
    }

    buf = malloc(l + 1);
    if (!buf) return NULL;

    for (p=(char*)s, rp=buf; *p; p++, rp++) {
	if (*p == '+')
	    *rp = ' ';
	else if (is_valid_escaped_hex(p)) {
	    sscanf(p+1, "%02x", &val);
	    *rp = (unsigned char)(val & 0xff);
	    p += 2;
	} else
	    *rp = *p;
    }
    buf[l] = 0;
    return buf;
}

void str2bin_hex_only(const char* s, unsigned char **buf, int* len) {
    char* p;
    unsigned char* rp;
    int val;
    int l;

    for (p=(char*)s, l=0; *p; l++, p++)
	if (is_valid_hex(p)) p++;

    *len = 0;
    *buf = malloc(l);
    if (!*buf) return;

    for (p=(char*)s, rp=*buf; *p; p++) {
	if (is_valid_hex(p)) {
	    sscanf(p, "%02x", &val);
	    *rp = (unsigned char)(val & 0xff);
	    p++;
	    rp++;
	    (*len)++;
	}
    }
}

typedef struct {
    int speed;
    int code;
} uart_speed_code_pair_t;

uart_speed_code_pair_t uart_speed_codes[] = {
    {50, B50},
    {75, B75},
    {110, B110},
    {134, B134},
    {150, B150},
    {200, B200},
    {300, B300},
    {600, B600},
    {1200, B1200},
    {1800, B1800},
    {2400, B2400},
    {4800, B4800},
    {9600, B9600},
    {19200, B19200},
    {38400, B38400},
    {57600, B57600},
    {115200, B115200},
    {230400, B230400},
    {460800, B460800},
    {500000, B500000},
    {576000, B576000},
    {921600, B921600},
    {1000000, B1000000},
    {1152000, B1152000},
    {1500000, B1500000},
    {2000000, B2000000},
    {2500000, B2500000},
    {3000000, B3000000},
    {3500000, B3500000},
    {4000000, B4000000},
};

int find_uart_speed(int speed) {
    int i;
    for (i=0; i<sizeof(uart_speed_codes)/sizeof(uart_speed_code_pair_t); i++)
	if (uart_speed_codes[i].speed == speed)
	    return uart_speed_codes[i].code;
    return 0;
}

void post_string_to_serial_data(const char* post, unsigned char **buf, int* len, char** uart_device, int* uart_speed, int* uart_timeout, int* wait_response) {
    char* pair_from;
    char* pair_to;
    char* pair;
    char* key;
    char* value;
    int l;
    unsigned char *fbuf;
    int flen;

    *buf = NULL;
    *len = 0;
    *uart_device = NULL;
    *uart_speed = 0;
    *uart_timeout = -1;
    *wait_response = 0;

    pair_from = (char*)post;
    while (*pair_from) {
	pair_to = (char*)strchrnul(pair_from, '&');
	l = pair_to - pair_from;
	if (l < 2) {
	    if (!*pair_to) break;
	    pair_from = pair_to + 1;
	    continue;
	}
	pair = malloc(l + 1);
	if (!pair) return;
	memcpy(pair, pair_from, l);
	pair[l] = 0;
	key = pair;
	value = (char*)strchrnul(pair, '=');
	if (*value) {
	    *value = 0;
	    value++;
	}
	flen = 0;
	if ((!strcasecmp(key, ESCAPED_DATA_MARKER)) && (strlen(value) > 0)) {
	    dual_unescape(value, &fbuf, &flen);
	} else if ((!strcasecmp(key, HEX_ONLY_DATA_MARKER)) && (strlen(value) > 0)) {
	    str2bin_hex_only(value, &fbuf, &flen);
	} else if ((!strcasecmp(key, WAIT_RESPONSE_MARKER)) && (strlen(value) > 0)) {
	    *wait_response = atoi(value);
	} else if ((!strcasecmp(key, UART_DEVICE_MARKER)) && (strlen(value) > 0)) {
	    *uart_device = unescape_string(value);
	} else if ((!strcasecmp(key, UART_SPEED_MARKER)) && (strlen(value) > 0)) {
	    *uart_speed = find_uart_speed(atoi(value));
	} else if ((!strcasecmp(key, UART_TIMEOUT_MARKER)) && (strlen(value) > 0)) {
	    *uart_timeout = atoi(value);
	}
	if (flen > 0) {
	    if (!*buf) {
		*buf = fbuf;
		*len = flen;
	    } else {
		*buf = realloc(*buf, *len + flen);
		memcpy((*buf)+(*len), fbuf, flen);
		*len += flen;
		free(fbuf);
	    }
	}
	free(pair);
	if (!*pair_to) break;
	pair_from = pair_to + 1;
    }
}

void exit_status(int status) {
    printf("%d\n", status);
    exit(0);
}

int open_serial(const char* device, int speed) {
    struct termios options;
    int fd;

    fd = open(device, O_RDWR|O_NOCTTY);
    if (fd < 0) return -1;

    memset(&options, 0, sizeof(options));
    cfmakeraw(&options);
    cfsetspeed(&options, speed);
    options.c_cflag |= (CLOCAL|CREAD|CS8);

    if (tcsetattr(fd, TCSANOW, &options) < 0) {
	close(fd);
	return -1;
    }
    return fd;
}

char* sprinthex(unsigned char* buf, int len) {
    char* res;
    int i;

    res = malloc(len * 3 + 1);
    if (!res) return NULL;
    for (i=0; i<len; i++)
	sprintf(res+i*3, "%02x ", buf[i]);
    res[len*3-1] = 0;
    return res;
}

int main() {
    unsigned char *buf;
    int len;
    int i;
    char* cl_env;
    long content_length;
    char* content;
    int serial;
    fd_set readfds;
    struct timeval tv;
    int ready, cnt;
    unsigned char in_buf[8192];
    char* uart_device;
    int uart_speed;
    int uart_timeout;
    int wait_response;

    uart_device = DEFAULT_UART_DEVICE;
    uart_speed = DEFAULT_UART_SPEED;
    uart_timeout = DEFAULT_UART_RESPONSE_TIMEOUT_MS;
    wait_response = 0;

    cl_env = getenv("CONTENT_LENGTH");
    if (!cl_env) exit_status(411);

    content_length = atol(cl_env);
    if (content_length < 1) exit_status(400);

    content = malloc(content_length + 1);
    if (!content) exit_status(500);

    fread(content, content_length, 1, stdin);

    post_string_to_serial_data(content, &buf, &len, &uart_device, &uart_speed, &uart_timeout, &wait_response);
    if (len < 1) exit_status(400);

    if ((!uart_device) || (strlen(uart_device) < 1))
	uart_device = DEFAULT_UART_DEVICE;

    if (uart_speed < 1)
	uart_speed = DEFAULT_UART_SPEED;

    if (uart_timeout < 0)
	uart_timeout = DEFAULT_UART_RESPONSE_TIMEOUT_MS;

    serial = open_serial(uart_device, uart_speed);
    if (serial == -1) exit_status(500);

    write(serial, buf, len);

    if (wait_response) {
	FD_ZERO(&readfds);
	FD_SET(serial, &readfds);
	tv.tv_sec = 0;
	tv.tv_usec = uart_timeout * 1000;
	ready = select(serial + 1, &readfds, NULL, NULL, &tv);
	cnt = 0;
	if (ready == -1) {
	    exit_status(500);
	} else if (ready > 0) {
	    cnt = read(serial, (void*)in_buf, sizeof(in_buf));
	}
    } else {
	close(serial);
	exit_status(202);
    }

    close(serial);

    printf("Content-type: text/plain\n\n");
    printf("{\"sent\": \"%s\", \"received\": \"%s\"}", sprinthex(buf, len), (cnt>0)?sprinthex(in_buf, cnt):"");

    return 0;
}
