/*
 * debug.c
 *
 *  Created on: Apr 16, 2024
 *      Author: colorbass
 */

#include "main.h"
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdarg.h>
#include "debug.h"
#include "board.h"
#include "usart.h"
#include <time.h>
#include <connector.h>
#include "serial_control.h"

// Кольцевой буфер для отладочных сообщений
#define DEBUG_BUFFER_SIZE 1024
#define DEBUG_BUFFER_MAX_COUNT 128

typedef struct {
	uint8_t buffer[DEBUG_BUFFER_SIZE];
	volatile uint16_t write_index;
	volatile uint16_t read_index;
	volatile uint16_t count;
} DebugBuffer_t;

static DebugBuffer_t debug_buffer = {
	.buffer = {0},
	.write_index = 0,
	.read_index = 0,
	.count = 0
};




#if defined(__GNUC__)
int _write(int fd, char * ptr, int len)
{
	debug_buffer_add((const uint8_t*)ptr, len);
    return len;
}
#endif

// Добавляет данные в кольцевой буфер
void debug_buffer_add(const uint8_t* data, uint16_t len)
{
	__disable_irq();
	
	for (uint16_t i = 0; i < len; i++) {
		// Если буфер полон, перезаписываем старые данные
		if (debug_buffer.count >= DEBUG_BUFFER_SIZE) {
			debug_buffer.read_index = (debug_buffer.read_index + 1) % DEBUG_BUFFER_SIZE;
			debug_buffer.count--;
		}
		
		debug_buffer.buffer[debug_buffer.write_index] = data[i];
		debug_buffer.write_index = (debug_buffer.write_index + 1) % DEBUG_BUFFER_SIZE;
		debug_buffer.count++;
	}
	
	__enable_irq();
}

// Возвращает количество доступных данных в буфере
uint16_t debug_buffer_available(void)
{
	__disable_irq();
	uint16_t count = debug_buffer.count;
	__enable_irq();
	return count;
}

// Отправляет один пакет данных из буфера через SC_SendPacket (не более 250 байт)
void debug_buffer_send(void)
{
	__disable_irq();
	
	// Если буфер пуст, ничего не делаем
	if (debug_buffer.count == 0) {
		__enable_irq();
		return;
	}
	
	// Определяем сколько байт можно отправить (не более 250)
	uint16_t bytes_to_send = debug_buffer.count;
	if (bytes_to_send > DEBUG_BUFFER_MAX_COUNT) {
		bytes_to_send = DEBUG_BUFFER_MAX_COUNT;
	}
	
	// Вычисляем сколько байт до конца буфера
	uint16_t bytes_to_end = DEBUG_BUFFER_SIZE - debug_buffer.read_index;
	
	// Отправляем только непрерывный блок (до конца буфера или до bytes_to_send)
	if (bytes_to_send > bytes_to_end) {
		bytes_to_send = bytes_to_end;
	}
	
	// Отправляем данные напрямую из буфера
    if(bytes_to_send == debug_buffer.count){
	    SC_SendPacket(&debug_buffer.buffer[debug_buffer.read_index], bytes_to_send, CMD_GET_LOG);
    }else{
        SC_SendPacket(&debug_buffer.buffer[debug_buffer.read_index], bytes_to_send, CMD_GET_LOG_CONTINUE);
    }
	debug_buffer.read_index = (debug_buffer.read_index + bytes_to_send) % DEBUG_BUFFER_SIZE;
	debug_buffer.count -= bytes_to_send;
	
	__enable_irq();
}

#define LOG_BUFFER_SIZE 128
uint8_t log_buffer[LOG_BUFFER_SIZE];

// Кастомный printf с приоритетом лога
int log_printf(LogLevel_t level, const char *format, ...)
{
	va_list args;
	int result;
	
	// Добавляем приоритет первым байтом
	log_buffer[0] = (uint8_t)level;
	
	// Форматируем строку начиная со второго байта
	va_start(args, format);
	result = vsnprintf((char*)&log_buffer[1], LOG_BUFFER_SIZE - 2, format, args);
	va_end(args);
	
	// Проверяем, не переполнился ли буфер
	if (result < 0) {
		return result;
	}
	
	// Ограничиваем размер, чтобы оставить место для нуль-терминатора
	if (result >= (LOG_BUFFER_SIZE - 2)) {
		result = LOG_BUFFER_SIZE - 2;
	}
	
	// Добавляем нуль-терминатор в конец
	log_buffer[result + 1] = '\0';
	
	// Отправляем в буфер (приоритет + строка + нуль-терминатор)
	debug_buffer_add(log_buffer, result + 2);
	
	return result;
}

#ifndef USE_WEB_INTERFACE

#include "isr_opt.h"

extern UART_HandleTypeDef huart2;


uint8_t  debug_rx_buffer[256];
uint8_t debug_cmd_received;
uint8_t debug_rx_buffer_size = 0;


ISR_FAST void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size){

//	if(huart->Instance == USART1){
//		mm_rx_interrupt(huart, Size);
//	}
	if(huart->Instance == USART2){
		debug_rx_interrupt(huart, Size);
	}
}

ISR_FAST void debug_rx_interrupt(UART_HandleTypeDef *huart, uint16_t Size){
	debug_rx_buffer[Size] = '\0';
	debug_rx_buffer_size = Size;
	debug_cmd_received = 1;
}

void debug_init(){
	HAL_UARTEx_ReceiveToIdle_IT(&huart2,debug_rx_buffer,255);
}

void parse_command(uint8_t* buffer, size_t length) {
	// ignore \r \n symbols
    size_t i = 0;
    for (i = 0; i < length; i++) {
        if (buffer[i] == '\r' || buffer[i] == '\n') {
            buffer[i] = '\0';
            length = i;
            break;
        }
    }
    if (buffer[0] == 0) return;
    if (strncmp((const char*)buffer, "reset", length) == 0) {
        log_printf(LOG_INFO, "Resetting...\n");
        NVIC_SystemReset();

    } else if (strncmp((const char*)buffer, "relayaux", length) == 0) {
        log_printf(LOG_INFO, "Relaying...\n");
        RELAY_Write(RELAY_AUX, 1);
        HAL_Delay(2000);
        RELAY_Write(RELAY_AUX, 0);
    } else if (strncmp((const char*)buffer, "relaycc", length) == 0) {
        log_printf(LOG_INFO, "Relaying...\n");
        RELAY_Write(RELAY_CC, 1);
        HAL_Delay(200);
        RELAY_Write(RELAY_CC, 0);
    } else if (strncmp((const char*)buffer, "relaydc", length) == 0) {
        log_printf(LOG_INFO, "Relaying...\n");
        RELAY_Write(RELAY_DC, 1);
        HAL_Delay(200);
        RELAY_Write(RELAY_DC, 0);
    } else if (strncmp((const char*)buffer, "relayac", length) == 0) {
        log_printf(LOG_INFO, "Relaying...\n");
        RELAY_Write(RELAY_AC, 1);
        HAL_Delay(200);
        RELAY_Write(RELAY_AC, 0);

    } else if (strncmp((const char*)buffer, "adc", length) == 0) {
        log_printf(LOG_INFO, "CC1=%.2f\n", CONN_CC_GetAdc());

    } else if (strncmp((const char*)buffer, "lock_state", length) == 0) {
        log_printf(LOG_INFO, "Lock state=%d\n", GBT_LockGetState());

    } else if (strncmp((const char*)buffer, "lock_lock", length) == 0) {
        log_printf(LOG_INFO, "Locked\n");
        GBT_Lock(1);

    } else if (strncmp((const char*)buffer, "lock_unlock", length) == 0) {
        log_printf(LOG_INFO, "Unlocked\n");
        GBT_Lock(0);

    } else if (strncmp((const char*)buffer, "complete", length) == 0) {
    	CONN_SetState(Finished);

    } else if (strncmp((const char*)buffer, "start", length) == 0) {
        log_printf(LOG_INFO, "Started\n");
        GBT_Start();

    } else if (strncmp((const char*)buffer, "stop", length) == 0) {
        log_printf(LOG_INFO, "Stopped\n");
        GBT_StopEVSE(GBT_CST_SUSPENDS_ARTIFICIALLY);

    } else if (strncmp((const char*)buffer, "stop1", length) == 0) {
        log_printf(LOG_INFO, "Stopped\n");
        GBT_ForceStop();

//    } else if (strncmp((const char*)buffer, "force", length) == 0) {
//        log_printf(LOG_INFO, "Stopped\n");
//		GBT_Lock(1);
//		GBT_SwitchState(GBT_S2_LOCKED);
//		GBT_Delay(500);

    } else if (strncmp((const char*)buffer, "cc_state", length) == 0) {
    	switch(CONN_CC_GetState()){
    	case GBT_CC_UNKNOWN:
    		log_printf(LOG_INFO, "GBT_CC_UNKNOWN\n");
    		break;
    	case GBT_CC_12V:
    		log_printf(LOG_INFO, "GBT_CC_12V\n");
    		break;
    	case GBT_CC_6V:
    		log_printf(LOG_INFO, "GBT_CC_6V\n");
    		break;
    	case GBT_CC_4V:
    		log_printf(LOG_INFO, "GBT_CC_4V\n");
    		break;
    	case GBT_CC_2V:
    		log_printf(LOG_INFO, "GBT_CC_2V\n");
    		break;

    	}
    } else if (strncmp((const char*)buffer, "temp", length) == 0) {
    	log_printf(LOG_INFO, "temp1 %d\n",GBT_ReadTemp(0));
    	log_printf(LOG_INFO, "temp2 %d\n",GBT_ReadTemp(1));
    } else if (strncmp((const char*)buffer, "info1", length) == 0) {
        log_printf(LOG_INFO, "Battery info:\n");
        log_printf(LOG_INFO, "maxCV %dV\n",GBT_BATStat.maxCellVoltage/100); 		// 0.01v/bit
        log_printf(LOG_INFO, "maxCC %dA\n",GBT_BATStat.maxChargingCurrent/10); 	// 0.1A/bit
        log_printf(LOG_INFO, "totE %dkWh\n",GBT_BATStat.totalEnergy/10); 			// 0.1kWh
        log_printf(LOG_INFO, "maxCV %dV\n",GBT_BATStat.maxChargingVoltage/10); 	// 0.1V/ bit
        log_printf(LOG_INFO, "maxT %dC\n",(int16_t)GBT_BATStat.maxTemp-50); 		// 1C/bit, -50C offset
        log_printf(LOG_INFO, "SOC %dp\n",GBT_BATStat.SOC/10); 					// 0.1%/bit , 0..100%
        log_printf(LOG_INFO, "Volt. %dV\n",GBT_BATStat.measVoltage/10); 			// 0.1V/bit

    } else if (strncmp((const char*)buffer, "info2", length) == 0) {
        log_printf(LOG_INFO, "EV info:\n");
        log_printf(LOG_INFO, "GBT_ver V%d.%d%d\n",GBT_EVInfo.version[0],GBT_EVInfo.version[1],GBT_EVInfo.version[2]);
        log_printf(LOG_INFO, "Battery type: %d\n",GBT_EVInfo.batteryType);
        log_printf(LOG_INFO, "Battery capacity: %d\n", GBT_EVInfo.batteryCapacity);	// 0.1Ah/bit
    	log_printf(LOG_INFO, "Battery voltage: %d\n", GBT_EVInfo.batteryVoltage);  	// 0.1V/bit
    	log_printf(LOG_INFO, "Battery vendor: %.4s\n", GBT_EVInfo.batteryVendor); 	// Battery vendor (ASCII string)
    	log_printf(LOG_INFO, "Battery SN: %lu\n", GBT_EVInfo.batterySN);			// int
    	log_printf(LOG_INFO, "Battery manufacture date: %02d.%02d.%04d\n", GBT_EVInfo.batteryManuD, GBT_EVInfo.batteryManuM ,GBT_EVInfo.batteryManuY+1985);		// year (offset 1985)
    	log_printf(LOG_INFO, "Battery cycles: %d\n", GBT_EVInfo.batteryCycleCount); //uint24_t
    	log_printf(LOG_INFO, "Own auto: %d\n", GBT_EVInfo.ownAuto);			// 0 = lizing, 1 = own auto
    	log_printf(LOG_INFO, "EVIN: %.17s\n", GBT_EVInfo.EVIN);			//EVIN
    	log_printf(LOG_INFO, "EV_SW_VER: %.8s\n", GBT_EVInfo.EV_SW_VER);

    } else if (strncmp((const char*)buffer, "info3", length) == 0) {
        log_printf(LOG_INFO, "GBT_MaxLoad info:\n");
        log_printf(LOG_INFO, "Output max current: %d\n",GBT_MaxLoad.maxOutputCurrent);
        log_printf(LOG_INFO, "Output min current: %d\n",GBT_MaxLoad.minOutputCurrent);
        log_printf(LOG_INFO, "Output max voltage: %d\n",GBT_MaxLoad.maxOutputVoltage);
        log_printf(LOG_INFO, "Output min voltage: %d\n",GBT_MaxLoad.minOutputVoltage);
        log_printf(LOG_INFO, "\nGBT_ChargerInfo info:\n");
        log_printf(LOG_INFO, "BMS Recognized: %d\n",GBT_ChargerInfo.bmsIdentified);
        log_printf(LOG_INFO, "Charger location: %.3s\n",GBT_ChargerInfo.chargerLocation);
        log_printf(LOG_INFO, "Charger number: %lu\n",GBT_ChargerInfo.chargerNumber);


    } else if (strncmp((const char*)buffer, "help", length) == 0) {
        log_printf(LOG_INFO, "Command list:\n");
        log_printf(LOG_INFO, "reset\n");
        log_printf(LOG_INFO, "help\n");
        log_printf(LOG_INFO, "cc_state\n");
        log_printf(LOG_INFO, "lock_lock\n");
        log_printf(LOG_INFO, "lock_unlock\n");
        log_printf(LOG_INFO, "lock_state\n");
        log_printf(LOG_INFO, "adc\n");
        log_printf(LOG_INFO, "relay(cc,aux,ac,dc)\n");
        log_printf(LOG_INFO, "start\n");
        log_printf(LOG_INFO, "stop\n");
        log_printf(LOG_INFO, "stop1\n");
//        log_printf(LOG_INFO, "force\n");
        log_printf(LOG_INFO, "temp\n");
        log_printf(LOG_INFO, "info1\n");
        log_printf(LOG_INFO, "info2\n");
        log_printf(LOG_INFO, "info3\n");
        log_printf(LOG_INFO, "time\n");
        log_printf(LOG_INFO, "cantest\n");

        //TODO: info commands

    } else if (strncmp((const char*)buffer, "time", length) == 0) {

		time_t unix_time = (time_t)get_Current_Time();
		struct tm *parts = localtime(&unix_time);

		log_printf(LOG_INFO, "Year: %d\n", parts->tm_year + 1900);
		log_printf(LOG_INFO, "Month: %d\n", parts->tm_mon + 1);
		log_printf(LOG_INFO, "Day: %d\n", parts->tm_mday);
		log_printf(LOG_INFO, "Hour: %d\n", parts->tm_hour);
		log_printf(LOG_INFO, "Minute: %d\n", parts->tm_min);
		log_printf(LOG_INFO, "Second: %d\n", parts->tm_sec);

    } else if (strncmp((const char*)buffer, "cantest", length) == 0) {
    	//GBT_SendCHM();
    	GBT_Error(0xFDF0C0FC); //BRM Timeout
        log_printf(LOG_INFO, "can test\n");

    } else {
        log_printf(LOG_INFO, "Unknown command\n");
    }
}

void debug_task(){
	if(debug_cmd_received){
		parse_command(debug_rx_buffer, debug_rx_buffer_size);
		HAL_UARTEx_ReceiveToIdle_IT(&huart2,debug_rx_buffer,255);
		debug_cmd_received = 0;
	}
}

#else

#endif // USE_WEB_INTERFACE
