#include "log.h"
#include "rtc.h"
#include "ringfs.h"
#include "spi_flash.h"

#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"

#include <string.h>

char logFileBuf[FILE_BUF_MAX_LEN];

const char* logsStrShortRu[] =
{
	"Перезагрузка контроллера",
    "Сброс настроек",
    "Обновление ПО",
    "Смена пароля",
    "Сохранение настроек",
    "Авторизация",
    "Тест ИБП",
    "Откл. нагрузки ИБП",
    "Авария дискр. входа 1",
    "Состояние выхода 1",
    "Состояние выхода 2",
    "Авария температуры",
    "Авария вх. напряжения",
    "Низкий заряд АКБ",
    "Авария нагрузки",
    "Авария связи с ИБП",
    "Авария отключения АКБ",
};

bool flUpdateLog = false;
static bool fLogInit = false;  // Флаг инициализации журнала

#define LOG_TIME	1000*60*10

#define LOG_FLASH_SECTOR_OFFSET	4

#define ALARM_LOG_FLASH_SECTOR_OFFSET	258

static int op_sector_erase(struct ringfs_flash_partition *flash, int address) {
	(void)flash;
	int ret;
	ret = spi_flash_erase_sector(address, 0);
	return ret;
}

static ssize_t op_program(struct ringfs_flash_partition *flash, int address, const void *data, size_t size) {
	(void)flash;
	int ret;
	ret = spi_flash_write(address, data, size, 0);
	return ret;
}

static ssize_t op_read(struct ringfs_flash_partition *flash, int address, void *data, size_t size) {
	(void)flash;
	int ret;
	ret = spi_flash_read(address, data, size, 0);
	return ret;
}

static struct ringfs_flash_partition ringfs_flash = {
	.sector_offset = LOG_FLASH_SECTOR_OFFSET,

	.sector_erase = op_sector_erase,
	.program = op_program,
	.read = op_read,
};

static struct ringfs fs;

static struct ringfs_flash_partition ringfs_flash2 = {
	.sector_offset = ALARM_LOG_FLASH_SECTOR_OFFSET,

	.sector_erase = op_sector_erase,
	.program = op_program,
	.read = op_read,
};

static struct ringfs fs2;

static SemaphoreHandle_t log_mutex;


/**
  * @brief  Отключает журнал для безопасной перезагрузки
  */
bool LOG_Disable(void)
{
    if (fLogInit) {
        /* Ожидаем завершения работы с журнал */
        if ( xSemaphoreTake(log_mutex, 10000) == pdTRUE ) {
            //fLogInit = false;
            //xSemaphoreGive(logMutex);
            return true;
        }
        else {
           return false;
        }
    }
    else {
        return true;
    }
}

void log_task(void* params)
{
	for(;;){
		flUpdateLog = true;
		vTaskDelay(LOG_TIME);
	/*	vTaskDelay(50);
		log_event_data(LOG_SYSTEM_BOOT, "Администратор");
		log_add(")215.7;215.7;220.5;000;50.1;2.30;25.0;00000001;");*/
	}
}

void log_init(bool format) {
	DBG printf(">>> Event log\n");

	if (!spi_flash_desc.present)
		return;
	ringfs_flash.sector_size = spi_flash_desc.sector_size;
	ringfs_flash.sector_count = spi_flash_desc.sector_count/2 - LOG_FLASH_SECTOR_OFFSET;

	ringfs_init(&fs, &ringfs_flash, LOG_ENTRY_VERSION, sizeof(log_entry_t));
	if (format || ringfs_scan(&fs) != 0)
		ringfs_format(&fs);

	ringfs_flash2.sector_size = spi_flash_desc.sector_size;
	ringfs_flash2.sector_count = spi_flash_desc.sector_count/2 - LOG_FLASH_SECTOR_OFFSET;

	ringfs_init(&fs2, &ringfs_flash2, LOG_ENTRY_VERSION, sizeof(log_entry_t));
	if (format || ringfs_scan(&fs2) != 0)
		ringfs_format(&fs2);

	fLogInit = true;

	log_mutex = xSemaphoreCreateMutex();

	xTaskCreate(log_task, ( char * ) "log_task", configMINIMAL_STACK_SIZE * 2, NULL, tskIDLE_PRIORITY, NULL);
}


int capacity_flash = 0;
int count_flash = 0;
int log_test(void) {
	int ret;
	log_entry_t entry;

	log_init(false);
	capacity_flash = ringfs_capacity(&fs);
	count_flash = ringfs_count_exact(&fs);
	DBG printf("\tCapacity:   %d\n", capacity_flash);
	DBG printf("\tCount:      %d\n", count_flash);

	DBG printf("\tAppending   ");
//	ret = log_event(LOG_SYSTEM_DEFCONFIG, 0, 0);
	DBG printf("%s\n", ret == 0 ? "ok" : "error");
	if (ret == 0)
		return -1;

//	ret = log_event(LOG_SYSTEM_DEFCONFIG, 0, 512);
	entry.timestamp = 0;
	entry.type = 0;
	DBG printf("\tFetching    ");
	if (log_fetch(&entry, portMAX_DELAY) == 0){
		DBG printf("ok, time=%d, type=%d\n", entry.timestamp, entry.type);
		log_fetch(&entry, portMAX_DELAY);
		entry.timestamp = 0;
			entry.type = 0;
		log_fetch(&entry, portMAX_DELAY);
		entry.timestamp = 0;
					entry.type = 0;
				log_fetch(&entry, portMAX_DELAY);
				entry.timestamp = 0;
							entry.type = 0;
						log_fetch(&entry, portMAX_DELAY);
		return 0;
	}
	else {
		DBG printf("fail\n");
		return -1;
	}

	DBG printf("\tDiscarding  ");
	if (log_discard(&entry,portMAX_DELAY) == 0)
		DBG printf("ok\n");
	else {
		DBG printf("fail\n");
		return -1;
	}

	return 0;
}

int log_append(log_entry_t *entry) {
	int ret;
	TM_RTC_t data;
	ret = xSemaphoreTake( log_mutex, portMAX_DELAY );
	if (ret == pdFALSE)
		return ret;
	if (!entry->timestamp){
		TM_RTC_GetDateTime(&data, TM_RTC_Format_BIN);
		entry->timestamp = data.unix;
	}
	if(entry->type == LOG_VALUE)
		ringfs_append(&fs, entry);
	else
		ringfs_append(&fs2, entry);
	xSemaphoreGive(log_mutex);
	return ret;
}

int log_fetch(log_entry_t *entry, uint32_t timeout) {
	int ret;
	ret = xSemaphoreTake( log_mutex, (TickType_t)timeout );
	if (ret == pdFALSE)
		return ret;
	if(entry->type == LOG_VALUE)
		ret = ringfs_fetch(&fs, entry);
	else
		ret = ringfs_fetch(&fs2, entry);
	xSemaphoreGive(log_mutex);
	return ret;
}

int log_rewind(log_entry_t *entry, uint32_t timeout) {
	int ret;
	ret = xSemaphoreTake( log_mutex, (TickType_t)timeout );
	if (ret == pdFALSE)
		return ret;
	if(entry->type == LOG_VALUE)
		ret = ringfs_rewind(&fs);
	else
		ret = ringfs_rewind(&fs2);
	xSemaphoreGive(log_mutex);
	return ret;
}

int log_discard(log_entry_t *entry, uint32_t timeout) {
	int ret;
	ret = xSemaphoreTake( log_mutex, (TickType_t)timeout );
	if (ret == pdFALSE)
		return ret;
	if(entry->type == LOG_VALUE)
		ret = ringfs_discard(&fs);
	else
		ret = ringfs_discard(&fs2);
	xSemaphoreGive(log_mutex);
	return ret;
}

void log_event_data(log_type_t type, char *data)
{
	log_entry_t entry_data;

	entry_data.timestamp = 0;
	entry_data.type = type;
	strncpy(entry_data.data, data, 49);
	if (fLogInit)
		log_append(&entry_data);
}

void log_add(char *log_data)
{
	char buf_value[50];
	uint8_t i, len;


	memset(buf_value, 0, 50);
	len = strlen(log_data);

	strncpy(buf_value, log_data, len);
	buf_value[0] = '\"';

	for(i = 0; i < len; i++)
	{
		if(buf_value[i] == ' ')
			buf_value[i] = ';';
	}
	buf_value[len - 1] = ';';

	if(fLogInit){
		if(fs.write.slot>67)
		{
			log_entry_t entry_data;
			entry_data.timestamp = 0;
			log_event_data(LOG_VALUE, buf_value);
		}
		else
		log_event_data(LOG_VALUE, buf_value);
	}

}

/**
  * @brief  Возвращает true если журнал проинициализирован
  */
bool LOG_IsInit()
{
  return fLogInit;
}

/**
  * @brief  Возвращает общее количество страниц
  */
uint32_t LOG_GetPageCount(void)
{
  return (((ringfs_count_estimate(&fs)) / 10) + 1);
}

uint32_t LOG_GetTotalSTRCount(void)
{
	return ringfs_count_estimate(&fs);
}

void LOG_GetPage(char *str, uint32_t page)
{
	TM_RTC_t rtc_data;
	log_entry_t entry;
	char buf[20];
	uint8_t i;
	int start =LOG_GetTotalSTRCount();//(fs.write.sector*fs.slots_per_sector + fs.write.slot);

	memset(buf, 0, 20);
	for(i=0; i < 10; i++){
		fs.cursor_position =  start - 10*(page-1) - 1 - i;
		if(fs.cursor_position < 0)
			break;
		else{
			fs.cursor.sector = (fs.read.sector + fs.cursor_position/fs.slots_per_sector)%fs.flash->sector_count;
			fs.cursor.slot = fs.cursor_position%fs.slots_per_sector;
		}
		entry.type = LOG_VALUE;
		log_fetch(&entry, portMAX_DELAY);
		entry.data[49] = 0;
		strncat(str, entry.data, strlen(entry.data));
		TM_RTC_GetDateTimeFromUnix(&rtc_data, entry.timestamp);
		sprintf(buf, "%02i.%02i.%02i %02i:%02i:%02i",  rtc_data.date, rtc_data.month,
		rtc_data.year, rtc_data.hours, rtc_data.minutes, rtc_data.seconds);
		strcat(str, buf);
		strcat(str, "\",");
	}
}

uint32_t LOG_GetData(int ptr, char *str, uint32_t size, bool start)
{
	TM_RTC_t rtc_data;
	log_entry_t entry;
	char buf[20];
	uint8_t i;
	entry.type = LOG_VALUE;
	if(start)
		log_rewind(&entry, portMAX_DELAY);

	fs.cursor_position =  ptr/STRING_SIZE;
	fs.cursor.sector = (fs.read.sector + fs.cursor_position/fs.slots_per_sector)%fs.flash->sector_count;
	fs.cursor.slot = fs.cursor_position%fs.slots_per_sector;

	for(i = 0; i < size/STRING_SIZE; i++)
	{
		entry.type = LOG_VALUE;
		log_fetch(&entry, portMAX_DELAY);
		entry.data[49] = 0;
		strncat(str, &entry.data[1], (strlen(entry.data) - 1));
		TM_RTC_GetDateTimeFromUnix(&rtc_data, entry.timestamp);
		sprintf(buf, "%02i.%02i.%02i %02i:%02i:%02i",  rtc_data.date, rtc_data.month,
		rtc_data.year, rtc_data.hours, rtc_data.minutes, rtc_data.seconds);
		strcat(str, buf);
		strcat(str, "\n");
	}
	return strlen(str);

}

/**
  * @brief  Возвращает общее количество страниц
  */
uint32_t History_GetPageCount(void)
{
  return (((ringfs_count_estimate(&fs2)) / 10) + 1);
}

uint32_t History_GetTotalSTRCount(void)
{
	return ringfs_count_estimate(&fs2);
}

void History_GetPage(char *str, uint32_t page)
{
	TM_RTC_t rtc_data;
	log_entry_t entry;
	char buf[20];
	uint8_t i;
	int start =History_GetTotalSTRCount();//(fs.write.sector*fs.slots_per_sector + fs.write.slot);

	memset(buf, 0, 20);
	for(i=0; i < 10; i++){
		fs2.cursor_position =  start - 10*(page-1) - 1 - i;
		if(fs2.cursor_position < 0)
			break;
		else{
			fs2.cursor.sector = (fs2.read.sector + fs2.cursor_position/fs.slots_per_sector)%fs2.flash->sector_count;
			fs2.cursor.slot = fs2.cursor_position%fs2.slots_per_sector;
		}
		entry.type = LOG_LOGIN;
		log_fetch(&entry, portMAX_DELAY);
		strcat(str, "\"");
		strncat(str, logsStrShortRu[entry.type], (strlen(logsStrShortRu[entry.type]) ));
		strcat(str, ";");
		entry.data[49] = 0;
		strncat(str, entry.data, (strlen(entry.data) ));
		strcat(str, ";");
		TM_RTC_GetDateTimeFromUnix(&rtc_data, entry.timestamp);
		sprintf(buf, "%02i.%02i.%02i %02i:%02i:%02i",  rtc_data.date, rtc_data.month,
		rtc_data.year, rtc_data.hours, rtc_data.minutes, rtc_data.seconds);
		strcat(str, buf);
		strcat(str, "\",");
	}
}

uint32_t History_GetData(int ptr, char *str, uint32_t size, bool start)
{
	TM_RTC_t rtc_data;
	log_entry_t entry;
	char buf[20];
	char temp_str[FILE_BUF_MAX_LEN];
	uint8_t i;
	uint16_t len;
	entry.type = LOG_LOGIN;
	if(start)
		log_rewind(&entry, portMAX_DELAY);

	fs2.cursor_position =  ptr/STRING_SIZE_HISTORY;
	fs2.cursor.sector = (fs2.read.sector + fs2.cursor_position/fs2.slots_per_sector)%fs2.flash->sector_count;
	fs2.cursor.slot = fs2.cursor_position%fs2.slots_per_sector;

	for(i = 0; i < size/STRING_SIZE_HISTORY; i++)
	{
		memset(temp_str, 0, 100);
		log_fetch(&entry, portMAX_DELAY);
		strncat(temp_str, logsStrShortRu[entry.type], (strlen(logsStrShortRu[entry.type])));
		strcat(temp_str, ";");
		entry.data[49] = 0;
		strncat(temp_str, entry.data, (strlen(entry.data)));
		strcat(temp_str, ";");
		TM_RTC_GetDateTimeFromUnix(&rtc_data, entry.timestamp);
		sprintf(buf, "%02i.%02i.%02i %02i:%02i:%02i",  rtc_data.date, rtc_data.month,
		rtc_data.year, rtc_data.hours, rtc_data.minutes, rtc_data.seconds);
		strcat(temp_str, buf);

		len = strlen(temp_str);

		  if (len <= STRING_SIZE_HISTORY - 1)
		  {
		    memset(&temp_str[len], ' ', STRING_SIZE_HISTORY - len - 1);
		    strcat(temp_str, "\n");
		  }
		  else
		  {
		    temp_str[STRING_SIZE - 1] = 0xa;
		  }
		  strncat(str, temp_str, STRING_SIZE_HISTORY);

	}
	return strlen(str);

}
