/******************************************************************************
 * file    Core/Src/flash_log.c 
 * author  YuLiang
 * version 1.0.0
 * date    26-Dec-2022
 * brief   This file provides all the log related operation functions.
 ******************************************************************************
 * Attention
 *
 * <h2><center>&copy; COPYRIGHT(c) 2022 LandPower</center></h2>
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *   1. Redistributions of source code must retain the above copyright notice,
 *      this list of conditions and the following disclaimer.
 *   2. Redistributions in binary form must reproduce the above copyright notice,
 *      this list of conditions and the following disclaimer in the documentation
 *      and/or other materials provided with the distribution.
 *   3. Neither the name of WTOE nor the names of its contributors may be used to 
 *      endorse or promote products derived from this software without specific
 *      prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 ******************************************************************************/
/* Includes ------------------------------------------------------------------*/
#include <stdio.h>
#include <string.h>
#include <stdarg.h>

#include "cmsis_os.h"
#include "rtc.h"

#include "common.h"
#include "dev_config.h"
#include "flash_if.h"
#include "flash_log.h"

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/

static osMutexId_t log_mutex;
static const osMutexAttr_t log_mutex_attr = {
    .name = "flash_log"
};  

static RTC_DateTypeDef log_date;
static RTC_TimeTypeDef log_time;
static struct tm log_tm;
static falsh_log_t flash_log_buf;
uint64_t flash_log_id;
static char *type_str[FLASH_LOG_TYPE_CNT] = 
{
    "ERROR",
    "WARNING",
    "NOTIFY",
    "INFO"
};

uint64_t fd_id;
static fd_date_t fd_buf;

uint64_t position_id;
static position_data_t position_buf;

uint64_t st_id;
static st_date_t st_buf;

/* Private function prototypes -----------------------------------------------*/
/* Internal functions --------------------------------------------------------*/
/* 获取互斥锁,这里判断NULL是为了在rtos没有初始化前,也可以操作log,下同. */
static void _flash_log_mutex_lock(void)
{
    if (log_mutex != NULL)
        osMutexAcquire(log_mutex, osWaitForever);
}

/* 释放互斥锁. */
static void _flash_log_mutex_unlock(void)
{
    if (log_mutex != NULL)
        osMutexRelease(log_mutex);
}

/* 获取log中目前最大的id号. */
static HAL_StatusTypeDef _flash_log_max_id_get(void)
{   
    uint32_t addr = LOG_ADDRESS;
    uint64_t id = 0;

    while(addr < LOG_ADDRESS_END)
    {
        /* 理论上不会出错,如果出错应该是出现硬件致命错误. */
        if (spi_flash_read(addr, (uint8_t*)(&id), FLASH_LOG_ID_LEN) != HAL_OK)
        {
            return HAL_ERROR;
        }
        
        if (FLASH_LOG_INVALID_ID == id)
        {
            break;
        }
        
        if (id > flash_log_id)
        {
            flash_log_id = id;
        }

        addr += FLASH_LOG_BUF_LEN;
    }

    return HAL_OK;
}

/* 根据log id获取log在flash中的地址. */
__STATIC_INLINE uint32_t _flash_log_addr_get(uint64_t log_id)
{   
    return LOG_ADDRESS + FLASH_LOG_BUF_LEN * ((log_id - 1) & (FLASH_LOG_NUM - 1));
}

/* 根据id读取log,id从1开始,如果id为0,则读取当前log. */
static HAL_StatusTypeDef _flash_log_read(uint64_t log_id, falsh_log_t *log_data)
{
    uint32_t addr = 0;

    addr = _flash_log_addr_get(log_id);

    HAL_E_RETURN(spi_flash_read(addr, (uint8_t*)log_data, FLASH_LOG_BUF_LEN));

    return HAL_OK;
}

/* 根据log id获取log在flash中的地址. */
__STATIC_INLINE uint32_t _fd_addr_get(uint64_t id)
{
    return FD_ADDRESS + FD_BUF_LEN * ((id - 1) & (FD_NUM - 1));
}

/* 获取log中目前最大的id号. */
static HAL_StatusTypeDef _fd_max_id_get(void)
{   
    uint32_t addr = FD_ADDRESS;
    uint64_t id = 0;

    while(addr < FD_ADDRESS_END)
    {
        /* 理论上不会出错,如果出错应该是出现硬件致命错误. */
        if (spi_flash_read(addr, (uint8_t*)(&id), FD_ID_LEN) != HAL_OK)
        {
            return HAL_ERROR;
        }
        
        if (FD_INVALID_ID == id)
        {
            break;
        }
        
        if (id > fd_id)
        {
            fd_id = id;
        }

        addr += FD_BUF_LEN;
    }

    return HAL_OK;
}

/* 根据 id 读取 data, id从1开始,如果id为0,则读取当前log. */
static HAL_StatusTypeDef _fd_read(uint64_t id, fd_date_t *data)
{
    uint32_t addr = 0;

    addr = _fd_addr_get(id);

    HAL_E_RETURN(spi_flash_read(addr, (uint8_t*)data, sizeof(fd_date_t)));

    return HAL_OK;
}

/* 根据log id获取log在flash中的地址. */
__STATIC_INLINE uint32_t _position_addr_get(uint64_t id)
{
    return POSITION_ADDRESS + POSITION_BUF_LEN * ((id - 1) & (POSITION_NUM - 1));
}

/* 获取log中目前最大的id号. */
static HAL_StatusTypeDef _position_max_id_get(void)
{   
    uint32_t addr = POSITION_ADDRESS;
    uint64_t id = 0;
    uint64_t i = 1;

    while(addr < POSITION_ADDRESS_END)
    {
        /* 理论上不会出错,如果出错应该是出现硬件致命错误. */
        if (spi_flash_read(addr, (uint8_t*)(&id), POSITION_ID_LEN) != HAL_OK)
        {
            return HAL_ERROR;
        }
        
        if (POSITION_INVALID_ID == id)
        {
            break;
        }

        if (id != i)
        {
            break;
        }
        
        if (id > position_id)
        {
            position_id = id;
        }

        addr += POSITION_BUF_LEN;
        i++;
    }

    return HAL_OK;
}

/* 根据 id 读取 data, id从1开始,如果id为0,则读取当前log. */
static HAL_StatusTypeDef _position_read(uint64_t id, position_data_t *data)
{
    uint32_t addr = 0;

    addr = _position_addr_get(id);

    HAL_E_RETURN(spi_flash_read(addr, (uint8_t*)data, sizeof(position_data_t)));

    return HAL_OK;
}

/* 根据log id获取log在flash中的地址. */
__STATIC_INLINE uint32_t _st_addr_get(uint64_t id)
{
    return ST_ADDRESS + ST_BUF_LEN * ((id - 1) & (ST_NUM - 1));
}

/* 根据 id 读取 data, id从1开始,如果id为0,则读取当前log. */
static HAL_StatusTypeDef _st_read(uint64_t id, st_date_t *data)
{
    uint32_t addr = 0;

    addr = _st_addr_get(id);

    HAL_E_RETURN(spi_flash_read(addr, (uint8_t*)data, sizeof(st_date_t)));

    return HAL_OK;
}

/* 获取状态中目前最大的 id 号. */
static HAL_StatusTypeDef _st_max_id_get(void)
{   
    uint32_t addr = ST_ADDRESS;
    uint64_t id = 0;

    while(addr < ST_ADDRESS_END)
    {
        /* 理论上不会出错,如果出错应该是出现硬件致命错误. */
        if (spi_flash_read(addr, (uint8_t*)(&id), ST_ID_LEN) != HAL_OK)
        {
            return HAL_ERROR;
        }
        
        if (ST_INVALID_ID == id)
        {
            break;
        }
        
        if (id > st_id)
        {
            st_id = id;
        }

        addr += ST_BUF_LEN;
    }

    return HAL_OK;
}

static void _st_default(void)
{
    memset(&st_data, 0, sizeof(st_date_t));
}

/* Interface functions -------------------------------------------------------*/
/* 向flash中写入1条log数据,大小不能超过128byte. */
HAL_StatusTypeDef flash_log_write(uint8_t type, char *format, ...)
{   
    uint8_t *pos = NULL;
    uint32_t addr = 0;  
    uint8_t len = 0;
    va_list ap;

    _flash_log_mutex_lock();
    flash_log_buf.id = ++flash_log_id;
    
    HAL_RTC_GetTime(&hrtc, &log_time, RTC_FORMAT_BIN);
    HAL_RTC_GetDate(&hrtc, &log_date, RTC_FORMAT_BIN);

    /* 组装数据. */
    pos = flash_log_buf.data;
    snprintf((char*)pos, FLASH_LOG_DATA_LEN, "[%lld][%d/%02d/%02d %02d:%02d:%02d][%s]: ", flash_log_id, \
        log_date.Year, log_date.Month, log_date.Date, log_time.Hours, log_time.Minutes, log_time.Seconds, type_str[type]);
    len = strlen((char*)pos);
    pos = flash_log_buf.data + len;
    
    va_start(ap, format);
    vsnprintf((char*)pos, FLASH_LOG_BUF_LEN - len, format, ap);  
    va_end(ap);
    len += strlen((char*)pos);

    /* 获取当前写入地址. */
    addr = _flash_log_addr_get(flash_log_id);
    
    /* 如果写入地址是页对其的, 需要先擦除, 再写入. */
    if (0 == (addr & (SPI_FLASH_SECTOR_SIZE - 1)))
        FLASH_LOG_RETURN_MUTEX(spi_flash_erase(addr, SPI_CMD_SECTOR_ERASE));

    /* 写入 flash, 写入长度是数据长度 + id 大小. */
    FLASH_LOG_RETURN_MUTEX(spi_flash_write(addr, (uint8_t*)(&flash_log_buf), len + FLASH_LOG_ID_LEN + 1));

    _flash_log_mutex_unlock();
    return HAL_OK;
}

/* 根据id读取log,id从1开始,如果id为0,则读取当前log. */
HAL_StatusTypeDef flash_log_read(uint64_t log_id, falsh_log_t *log_data)
{
    if (0 == log_id || log_id > flash_log_id)
    {
        return HAL_ERROR;
    }

    _flash_log_mutex_lock();

    FLASH_LOG_RETURN_MUTEX(_flash_log_read(log_id, log_data));

    _flash_log_mutex_unlock();
    return HAL_OK;
}

/* 显示num条log数据. */
void flash_log_show(uint32_t num)
{
    uint16_t index = 0;

    _flash_log_mutex_lock();
    
    /* 没有log. */
    if (0 == flash_log_id)
    {
        _flash_log_mutex_unlock();
        return;
    }

    /* 初始化变量 */
    num = num > FLASH_LOG_NUM ? FLASH_LOG_NUM : num;
    flash_log_buf.id = flash_log_id;
    
    for(index = 0; index < num; index++)
    {    
        if (_flash_log_read(flash_log_buf.id, &flash_log_buf) != HAL_OK)
            break;

        /* 读到全F,表示结束了. */
        if (FLASH_LOG_INVALID_ID == flash_log_buf.id)
            break;

        vty_print("%s", flash_log_buf.data);

        flash_log_buf.id--;
        if (0 == flash_log_buf.id)
            break;
    }

    _flash_log_mutex_unlock();
}

/* 清空log. */
HAL_StatusTypeDef flash_log_clear(void)
{
    _flash_log_mutex_lock();

    flash_log_id = 0;
    
    _flash_log_mutex_unlock();
    return HAL_OK;
}

/* 初始化log系统. */
void flash_log_init(void)
{
    /* 初始化互斥锁. */
    log_mutex = osMutexNew(&log_mutex_attr);
    if (NULL == log_mutex)
    {
        /* 致命错误,此处不应该被运行,记录错误log,打印错误信息. */
        ERROR_PRINT(-1);
    }
    
    /* 获取目前最大log id. */
    if (_flash_log_max_id_get() != HAL_OK)
        ERROR_PRINT(-1);

    if (_fd_max_id_get() != HAL_OK)
        ERROR_PRINT(-1);

    if (_st_max_id_get() != HAL_OK)
        ERROR_PRINT(-1);

    if (DEV_TYPE_CT_P == dev_info.type_s)
    {
        if (_position_max_id_get() != HAL_OK)
            ERROR_PRINT(-1);
    }
}

/* 向flash中写入1条log数据,大小不能超过128byte. */
HAL_StatusTypeDef fd_write(fd_date_t *data)
{   
    uint32_t addr = 0;

    _flash_log_mutex_lock();

    /* 组装数据. */
    data->id = ++fd_id;
    
    HAL_RTC_GetTime(&hrtc, &log_time, RTC_FORMAT_BIN);
    HAL_RTC_GetDate(&hrtc, &log_date, RTC_FORMAT_BIN);
    log_tm.tm_year = log_date.Year + 100;
    log_tm.tm_mon = log_date.Month - 1;
    log_tm.tm_mday = log_date.Date;
    log_tm.tm_hour = log_time.Hours;
    log_tm.tm_min = log_time.Minutes;
    log_tm.tm_sec = log_time.Seconds;
    data->run_time = mktime(&log_tm) - 28800;

    /* 获取当前写入地址. */
    addr = _fd_addr_get(fd_id);
    
    /* 如果写入地址是页对其的, 需要先擦除, 再写入. */
    if (0 == (addr & (SPI_FLASH_SECTOR_SIZE - 1)))
        FLASH_LOG_RETURN_MUTEX(spi_flash_erase(addr, SPI_CMD_SECTOR_ERASE));

    /* 写入 flash, 写入长度是数据长度 + id 大小. */
    FLASH_LOG_RETURN_MUTEX(spi_flash_write(addr, (uint8_t*)data, sizeof(fd_date_t)));

    _flash_log_mutex_unlock();
    return HAL_OK;
}

/* 修改数据. */
HAL_StatusTypeDef fd_modify(uint64_t id, fd_date_t *data)
{   
    uint32_t addr = 0;

    if (0 == id || id > fd_id)
    {
        return HAL_ERROR;
    }

    _flash_log_mutex_lock();

    /* 获取当前写入地址. */
    addr = _fd_addr_get(id);
    
    /* 写入 flash, 写入长度是数据长度. */
    FLASH_LOG_RETURN_MUTEX(spi_flash_write(addr, (uint8_t*)data, sizeof(fd_date_t)));

    _flash_log_mutex_unlock();
    return HAL_OK;
}

/* 根据 id 读取 data, id从1开始,如果id为0,则读取当前log. */
HAL_StatusTypeDef fd_read(uint64_t id, fd_date_t *data)
{
    if (0 == id || id > fd_id)
    {
        return HAL_ERROR;
    }

    _flash_log_mutex_lock();

    FLASH_LOG_RETURN_MUTEX(_fd_read(id, data));

    _flash_log_mutex_unlock();
    return HAL_OK;
}

/* 清空log. */
HAL_StatusTypeDef fd_clear(void)
{
    _flash_log_mutex_lock();

    fd_id = 0;
    
    _flash_log_mutex_unlock();
    return HAL_OK;
}

/* 显示num条log数据. */
void fd_show(uint32_t num)
{
    uint16_t index = 0;
    uint32_t time = 0;
    struct tm *day;

    _flash_log_mutex_lock();
    
    /* 没有log. */
    if (0 == fd_id)
    {
        _flash_log_mutex_unlock();
        return;
    }

    /* 初始化变量 */
    num = num > FD_NUM ? FD_NUM : num;
    fd_buf.id = fd_id;
    
    for(index = 0; index < num; index++)
    {    
        if (_fd_read(fd_buf.id, &fd_buf) != HAL_OK)
            break;

        /* 读到全F,表示结束了. */
        if (FD_INVALID_ID == fd_buf.id)
            break;

        time = fd_buf.run_time + 28800;
        day = localtime(&time);
        vty_print("%-08lld %-04d/%-02d/%-02d %-02d:%-02d:%-02d ", fd_buf.id, day->tm_year + 1900, day->tm_mon + 1,
            day->tm_mday, day->tm_hour, day->tm_min, day->tm_sec);

        vty_print("%-06d %-06d %-06d %-06d %-06d\r\n", fd_buf.elec[0], fd_buf.elec[1], fd_buf.elec[2], fd_buf.elec[3],
            fd_buf.is_not_updata);

        fd_buf.id--;
        if (0 == fd_buf.id)
            break;
    }

    _flash_log_mutex_unlock();
}

/* 向flash中写入1条log数据,大小不能超过128byte. */
HAL_StatusTypeDef position_write(position_data_t *data)
{   
    uint32_t addr = 0;

    _flash_log_mutex_lock();

    /* 组装数据. */
    data->id = ++position_id;

    HAL_RTC_GetTime(&hrtc, &log_time, RTC_FORMAT_BIN);
    HAL_RTC_GetDate(&hrtc, &log_date, RTC_FORMAT_BIN);
    log_tm.tm_year = log_date.Year + 100;
    log_tm.tm_mon = log_date.Month - 1;
    log_tm.tm_mday = log_date.Date;
    log_tm.tm_hour = log_time.Hours;
    log_tm.tm_min = log_time.Minutes;
    log_tm.tm_sec = log_time.Seconds;
    data->run_time = mktime(&log_tm) - 28800;

    /* 获取当前写入地址. */
    addr = _position_addr_get(position_id);
    
    /* 如果写入地址是页对其的, 需要先擦除, 再写入. */
    if (0 == (addr & (SPI_FLASH_SECTOR_SIZE - 1)))
        FLASH_LOG_RETURN_MUTEX(spi_flash_erase(addr, SPI_CMD_SECTOR_ERASE));

    /* 写入 flash, 写入长度是数据长度 + id 大小. */
    FLASH_LOG_RETURN_MUTEX(spi_flash_write(addr, (uint8_t*)data, sizeof(position_data_t)));

    _flash_log_mutex_unlock();
    return HAL_OK;
}

/* 根据 id 读取 data, id从1开始,如果id为0,则读取当前log. */
HAL_StatusTypeDef position_read(uint64_t id, position_data_t *data)
{
    if (0 == id || id > position_id)
    {
        return HAL_ERROR;
    }

    _flash_log_mutex_lock();

    FLASH_LOG_RETURN_MUTEX(_position_read(id, data));

    _flash_log_mutex_unlock();
    return HAL_OK;
}

/* 清空log. */
HAL_StatusTypeDef position_clear(void)
{
    _flash_log_mutex_lock();

    position_id = 0;
    
    _flash_log_mutex_unlock();
    return HAL_OK;
}

/* 显示num条log数据. */
void position_show(uint32_t num)
{
    uint16_t index = 0;
    uint32_t time = 0;
    struct tm *day;

    _flash_log_mutex_lock();
    
    /* 没有log. */
    if (0 == position_id)
    {
        _flash_log_mutex_unlock();
        return;
    }

    /* 初始化变量 */
    num = num > POSITION_NUM ? POSITION_NUM : num;
    position_buf.id = position_id;
    
    for(index = 0; index < num; index++)
    {    
        if (_position_read(position_buf.id, &position_buf) != HAL_OK)
            break;

        /* 读到全F,表示结束了. */
        if (POSITION_INVALID_ID == position_buf.id)
            break;

        time = position_buf.run_time + 28800;
        day = localtime(&time);
        vty_print("%-08lld %-04d/%-02d/%-02d %-02d:%-02d:%-02d ", position_buf.id, day->tm_year + 1900, day->tm_mon + 1,
            day->tm_mday, day->tm_hour, day->tm_min, day->tm_sec);

        vty_print("%-02d/%-02d %f/%d/%d %f/%d/%d %f/%d/%d %d/%d/%d\r\n",
            position_buf.vol, position_buf.temperature,
            position_buf.wave_info.v[0], position_buf.wave_info.s[0], position_buf.wave_info.ns[0],
            position_buf.wave_info.v[1], position_buf.wave_info.s[1], position_buf.wave_info.ns[1],
            position_buf.wave_info.v[2], position_buf.wave_info.s[2], position_buf.wave_info.ns[2],
            position_buf.wave_info.file_s, position_buf.wave_info.file_ns, position_buf.wave_info.file_feq);

        position_buf.id--;
        if (0 == position_buf.id)
            break;
    }

    _flash_log_mutex_unlock();
}

/* 向flash中写入1条log数据,大小不能超过128byte. */
HAL_StatusTypeDef st_write(st_date_t *data)
{   
    uint32_t addr = 0;

    _flash_log_mutex_lock();

    /* 组装数据. */
    data->id = ++st_id;

    /* 获取当前写入地址. */
    addr = _st_addr_get(st_id);
    
    /* 如果写入地址是页对其的, 需要先擦除, 再写入. */
    if (0 == (addr & (SPI_FLASH_SECTOR_SIZE - 1)))
        FLASH_LOG_RETURN_MUTEX(spi_flash_erase(addr, SPI_CMD_SECTOR_ERASE));

    /* 写入 flash, 写入长度是数据长度 + id 大小. */
    FLASH_LOG_RETURN_MUTEX(spi_flash_write(addr, (uint8_t*)data, sizeof(st_date_t)));

    _flash_log_mutex_unlock();
    return HAL_OK;
}

/* 根据 id 读取 data, id从1开始,如果id为0,则读取当前log. */
HAL_StatusTypeDef st_read(uint64_t id, st_date_t *data)
{
    if (0 == id || id > st_id)
    {
        return HAL_ERROR;
    }

    _flash_log_mutex_lock();

    FLASH_LOG_RETURN_MUTEX(_st_read(id, data));

    _flash_log_mutex_unlock();
    return HAL_OK;
}

/* 清空log. */
HAL_StatusTypeDef st_clear(void)
{
    _flash_log_mutex_lock();

    st_id = 0;
    
    _flash_log_mutex_unlock();
    return HAL_OK;
}

void st_show(uint32_t num)
{
    uint16_t index = 0;

    _flash_log_mutex_lock();
    
    /* 没有log. */
    if (0 == st_id)
    {
        _flash_log_mutex_unlock();
        return;
    }

    /* 初始化变量 */
    num = num > ST_NUM ? ST_NUM : num;
    st_buf.id = st_id;
    
    for(index = 0; index < num; index++)
    {    
        if (_st_read(st_buf.id, &st_buf) != HAL_OK)
            break;

        /* 读到全F,表示结束了. */
        if (ST_INVALID_ID == st_buf.id)
            break;

        vty_print("%-08lld %-08lld %-03d %-03d %-03d %-03d\r\n", st_buf.id, st_buf.position_up_id, st_buf.energy_mode);

        st_buf.id--;
        if (0 == st_buf.id)
            break;
    }

    _flash_log_mutex_unlock();
}

/* 初始化设备状态. */
void st_init(void)
{
    if (st_read(st_id, &st_data) != HAL_OK)
    {
        _st_default();
    }
    else
    {
        if(0xffff == st_data.wave_index)
        {
            _st_default();
        }
    }
}
/******************* (C) COPYRIGHT LandPower ***** END OF FILE ****************/
