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

#include <esp8266.h>
#include "uart.h"
#include "cgi.h"
#include "config.h"
#include "log.h"

// Web log for the esp8266 to replace outputting to uart1.
// The web log has a 1KB circular in-memory buffer which os_printf prints into and
// the HTTP handler simply displays the buffer content on a web page.

// see console.c for invariants (same here)
#define BUF_MAX (1400)
static char log_buf[BUF_MAX];
static int log_wr, log_rd;
static int log_pos;
static bool log_no_uart; // start out printing to uart
static bool log_newline; // at start of a new line

// called from wifi reset timer to turn UART on when we loose wifi and back off
// when we connect to wifi AP. Here this is gated by the flash setting
void ICACHE_FLASH_ATTR
log_uart(bool enable) {
	if (!enable && !log_no_uart && flashConfig.log_mode != LOG_MODE_ON) {
		// we're asked to turn uart off, and uart is on, and the flash setting isn't always-on
#if 1
		os_printf("Turning OFF uart log\n");
		os_delay_us(4*1000L); // time for uart to flush
		log_no_uart = !enable;
#endif
	} else if (enable && log_no_uart && flashConfig.log_mode != LOG_MODE_OFF) {
		// we're asked to turn uart on, and uart is off, and the flash setting isn't always-off
		log_no_uart = !enable;
		os_printf("Turning ON uart log\n");
	}
}

static void ICACHE_FLASH_ATTR
log_write(char c) {
	log_buf[log_wr] = c;
	log_wr = (log_wr+1) % BUF_MAX;
	if (log_wr == log_rd) {
		log_rd = (log_rd+1) % BUF_MAX; // full, eat first char
		log_pos++;
	}
}

#if 0
static char ICACHE_FLASH_ATTR
log_read(void) {
	char c = 0;
	if (log_rd != log_wr) {
		c = log_buf[log_rd];
		log_rd = (log_rd+1) % BUF_MAX;
	}
	return c;
}
#endif

static void ICACHE_FLASH_ATTR
log_write_char(char c) {
	// Uart output unless disabled
	if (!log_no_uart) {
		if (log_newline) {
			char buff[16];
			int l = os_sprintf(buff, "%6d> ", (system_get_time()/1000)%1000000);
			for (int i=0; i<l; i++)
				uart0_write_char(buff[i]);
			log_newline = false;
		}
		uart0_write_char(c);
		if (c == '\n') {
			log_newline = true;
			uart0_write_char('\r');
		}
	}
	// Store in log buffer
	if (c == '\n') log_write('\r');
	log_write(c);
}

int ICACHE_FLASH_ATTR
ajaxLog(HttpdConnData *connData) {
	char buff[2048];
	int len; // length of text in buff
	int log_len = (log_wr+BUF_MAX-log_rd) % BUF_MAX; // num chars in log_buf
	int start = 0; // offset onto log_wr to start sending out chars

	if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up.
	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 < log_pos) {
			start = 0;
		} else if (start >= log_pos+log_len) {
			start = log_len;
		} else {
			start = start - log_pos;
		}
	}

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

	int rd = (log_rd+start) % BUF_MAX;
	while (len < 2040 && rd != log_wr) {
		uint8_t c = log_buf[rd];
		if (c == '\\' || c == '"') {
			buff[len++] = '\\';
			buff[len++] = c;
		} 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;
}

static char *dbg_mode[] = { "auto", "off", "on" };

int ICACHE_FLASH_ATTR
ajaxLogDbg(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, "mode", buff, sizeof(buff));
	if (len > 0) {
		int8_t mode = -1;
		if (os_strcmp(buff, "auto") == 0) mode = LOG_MODE_AUTO;
		if (os_strcmp(buff, "off") == 0)  mode = LOG_MODE_OFF;
		if (os_strcmp(buff, "on") == 0)   mode = LOG_MODE_ON;
		if (mode >= 0) {
			flashConfig.log_mode = mode;
			if (mode != LOG_MODE_AUTO) log_uart(mode == LOG_MODE_ON);
			status = configSave() ? 200 : 400;
		}
	} else if (connData->requestType == HTTPD_METHOD_GET) {
		status = 200;
	}

	jsonHeader(connData, status);
	os_sprintf(buff, "{\"mode\": \"%s\"}", dbg_mode[flashConfig.log_mode]);
	httpdSend(connData, buff, -1);
	return HTTPD_CGI_DONE;
}


void ICACHE_FLASH_ATTR logInit() {
	log_no_uart = flashConfig.log_mode == LOG_MODE_OFF; // ON unless set to always-off
	log_wr = 0;
	log_rd = 0;
  os_install_putc1((void *)log_write_char);
}