// Copyright 2015 by Thorsten von Eicken, see LICENSE.txt

#include <esp8266.h>
#include "uart.h"
#include "cgi.h"
#include "uart.h"
#include "serbridge.h"
#include "config.h"
#include "console.h"

// Microcontroller console capturing the last 1024 characters received on the uart so
// they can be shown on a web page

// Buffer to hold concole contents.
// Invariants:
// - console_rd==console_wr <=> buffer empty
// - *console_rd == next char to read
// - *console_wr == next char to write
// - 0 <= console_xx < BUF_MAX
// - (console_wr+1)%BUF_MAX) == console_rd <=> buffer full
#define BUF_MAX (1024)
static char console_buf[BUF_MAX];
static int console_wr, console_rd;
static int console_pos; // offset since reset of buffer

static void ICACHE_FLASH_ATTR
console_write(char c) {
	console_buf[console_wr] = c;
	console_wr = (console_wr+1) % BUF_MAX;
	if (console_wr == console_rd) {
		// full, we write anyway and loose the oldest char
		console_rd = (console_rd+1) % BUF_MAX; // full, eat first char
		console_pos++;
	}
}

#if 0
// return previous character in console, 0 if at start
static char ICACHE_FLASH_ATTR
console_prev(void) {
	if (console_wr == console_rd) return 0;
	return console_buf[(console_wr-1+BUF_MAX)%BUF_MAX];
}
#endif

void ICACHE_FLASH_ATTR
console_write_char(char c) {
	//if (c == '\n' && console_prev() != '\r') console_write('\r'); // does more harm than good
	console_write(c);
}

int ICACHE_FLASH_ATTR
ajaxConsoleReset(HttpdConnData *connData) {
	if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up.
	jsonHeader(connData, 200);
	console_rd = console_wr = console_pos = 0;
	serbridgeReset();
	return HTTPD_CGI_DONE;
}

int ICACHE_FLASH_ATTR
ajaxConsoleBaud(HttpdConnData *connData) {
	if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up.
	char buff[512];
	int len, status = 400;
	len = httpdFindArg(connData->getArgs, "rate", buff, sizeof(buff));
	if (len > 0) {
		int rate = atoi(buff);
		if (rate >= 9600 && rate <= 1000000) {
			uart0_baud(rate);
			flashConfig.baud_rate = rate;
			status = configSave() ? 200 : 400;
		}
	} else if (connData->requestType == HTTPD_METHOD_GET) {
		status = 200;
	}

	jsonHeader(connData, status);
	os_sprintf(buff, "{\"rate\": %ld}", flashConfig.baud_rate);
	httpdSend(connData, buff, -1);
	return HTTPD_CGI_DONE;
}

int ICACHE_FLASH_ATTR
ajaxConsole(HttpdConnData *connData) {
	if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up.
	char buff[2048];
	int len; // length of text in buff
	int console_len = (console_wr+BUF_MAX-console_rd) % BUF_MAX; // num chars in console_buf
	int start = 0; // offset onto console_wr to start sending out chars

	jsonHeader(connData, 200);

	// figure out where to start in buffer based on URI param
	len = httpdFindArg(connData->getArgs, "start", buff, sizeof(buff));
	if (len > 0) {
		start = atoi(buff);
		if (start < console_pos) {
			start = 0;
		} else if (start >= console_pos+console_len) {
			start = console_len;
		} else {
			start = start - console_pos;
		}
	}

	// start outputting
	len = os_sprintf(buff, "{\"len\":%d, \"start\":%d, \"text\": \"",
			console_len-start, console_pos+start);

	int rd = (console_rd+start) % BUF_MAX;
	while (len < 2040 && rd != console_wr) {
		uint8_t c = console_buf[rd];
		if (c == '\\' || c == '"') {
			buff[len++] = '\\';
			buff[len++] = c;
		} else if (c == '\r') {
			// this is crummy, but browsers display a newline for \r\n sequences
		} else if (c < ' ') {
			len += os_sprintf(buff+len, "\\u%04x", c);
		} else {
			buff[len++] = c;
		}
		rd = (rd + 1) % BUF_MAX;
	}
	os_strcpy(buff+len, "\"}"); len+=2;
	httpdSend(connData, buff, len);
	return HTTPD_CGI_DONE;
}

void ICACHE_FLASH_ATTR consoleInit() {
	console_wr = 0;
	console_rd = 0;
}