/********************************* (C) ROTEK ***********************************
 * @module  web_params_api
 * @file    web_params_api.c
 * @version 1.0.0
 * @date    XX.XX.XXXX
 * $brief   web_params_api
 *******************************************************************************
 * @history     Version  Author         Comment
 * XX.XX.XXXX   1.0.0    Telenkov D.A.  First release.
 *******************************************************************************
 */

#include "common_config.h"
#include "stm32f4xx.h"
#include "web_params_api.h"
#if defined HTTP_SERVER_ENABLE
#include "http_server.h"
#elif defined SSL_SERVER_ENABLE
#include "my_ssl_server.h"
#endif
#include "parameters.h"
#include "snmp_api.h"
#include "trap_api.h"
#include "settings_api.h"
#include "snmp.h"
#include "log.h"
#include "hal.h"
#include "cJSON.h"

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

#ifdef PRINTF_STDLIB
#include <stdio.h>
#endif
#ifdef PRINTF_CUSTOM
#include "tinystdio.h"
#endif
#include <string.h>
#include "syslog.h"

typedef  void (*pFunction)(void);
pFunction Jump_To_Application;
uint32_t JumpAddress;

/**
  * @brief  Возвращяет строку с настройками на первой странице
  * @retval None
  */
char *HTTP_GetParamsPage(uint32_t reqNum, char *bufIn, char *bufOut, uint16_t lenBufIn, uint16_t *lenBufOut)
{
    uint8_t len;

    (void)bufIn;
    (void)lenBufIn;
    (void)reqNum;

    cJSON *monitor_param = NULL;

    memset(bufOut, 0, SEND_BUF_MAX_LEN);

    // Headers для поддержки saffari
    strcpy(bufOut, "HTTP/1.1 200 OK\r\nContent-Type:text/html\r\n\r\n");

    monitor_param = cJSON_CreateObject();
#define XJSON_PARAMS_TAG(tag, get_param) JSON_ADD_TAG(tag, get_param, monitor_param);
		WEB_PARAMS_TAGS_TABLE
#undef XJSON_PARAMS_TAG
    if(cJSON_PrintPreallocated(monitor_param, &bufOut[strlen(bufOut)], SEND_BUF_MAX_LEN, 0)) {
        *lenBufOut = strlen(bufOut);
    } else {
        *lenBufOut = 0;
    }
    cJSON_Delete(monitor_param);

    return bufOut;
}

/**
  * @brief  Возвращяет строку с настройками на второй странице
  * @retval None
  */
char *HTTP_GetSettingsPage(uint32_t reqNum, char *bufIn, char *bufOut, uint16_t lenBufIn, uint16_t *lenBufOut)
{
    uint8_t len;

    memset(bufOut, 0, SEND_BUF_MAX_LEN);

    (void)bufIn;
    (void)lenBufIn;
    (void)reqNum;

    cJSON *settings_param = NULL;

    /* Headers для поддержки saffari */
    strcpy(bufOut, "HTTP/1.1 200 OK\r\nContent-Type:text/html\r\n\r\n");

    settings_param = cJSON_CreateObject();
#define XJSON_SETTINGS_TAG(tag, get_param, set_param) JSON_ADD_TAG(tag, get_param, settings_param);
		WEB_SETTINGS_TAGS_TABLE
#undef XJSON_SETTINGS_TAG
    if(cJSON_PrintPreallocated(settings_param, &bufOut[strlen(bufOut)], SEND_BUF_MAX_LEN, 0)) {
        *lenBufOut = strlen(bufOut);
    } else {
        *lenBufOut = 0;
    }
    cJSON_Delete(settings_param);

    return bufOut;
}

uint8_t ConvertStringForJSON(char *inStr, char *paramValue, uint8_t inLen)
{
    int  len = 0, len2 = 0;
    char *strPtr = 0;
    uint8_t i = 0;

    while (i < inLen) {

        strPtr = strstr(inStr, "\"");

        if (strPtr != 0) {
            len = strPtr - inStr;
            if (len != 0) {
                strncat(paramValue, inStr, len);
            }
            strncat(paramValue, "\\", 1);
            strncat(paramValue, &inStr[len], 1);
            inStr = inStr + len + 1;
            i = i + len + 1;
        } else {
            if (paramValue != 0) {
                len2 = inLen - i;
                strncat(paramValue, inStr, len2);
                return 1;
            } else {
                return 0;
            }
        }
    }
    if (inLen == 0) {
        return 0;
    }
    return 1;
}

#ifdef NOTIFICATION_CONTROL_ENABLE
/**
  * @brief  Возвращяет строку с настройками уведомлений (есть/нет отправка)
  * @retval None
  */
char *HTTP_GetTrapAccess(char *params, uint16_t len_params, char *buf, uint16_t *len_out)
{
    char str[64];
    uint8_t len, i, first_item = 1;
    char value[20];

    (void)len_params;

    ClearParamString(params);
    memset(value, 0, 20);
    memset(str, 0, 64);

    /* Headers для поддержки saffari */
    strcpy(buf, "HTTP/1.0 200 OK\r\nContent-Type:text/html\r\n\r\n");

    /* Формирование списка уведомлений */
    strcat(buf, "{\"params\":[");

    for (i = 1; i < ALL_TRAPS; i++) {
        if (first_item) {
            first_item = 0;
        } else {
            strcat(buf, ",");
        }

        sprintf(str, "%d", i);
        len = strlen(str);
        strcat(buf, "{\"name\":\"");
        strncat(buf, str, len);

        GetTrapLongNameStr(str, &len, i);
        strcat(buf, "\",\"label\":\"");
        strncat(buf, str, len);

        GetNotificationTrapsStateStr(str, &len, i);
        strcat(buf, "\",\"snmp\":\"");
        strncat(buf, str, len);

        strcat(buf, "\"}");
    }
    strcat(buf, "]");

    /* Признак изменения сетевых настроек */
    GetWebReinitFlag(str, &len);
    strcat(buf, ",\"netsettings_changed\":\"");
    strncat(buf, str, len);
    strcat(buf, "\"}");

    *len_out = strlen(buf);
    return buf;
}
#endif

/**
  * @brief  Возвращяет строку с информацией об устройстве
  * @retval None
  */
// TODO Согласовать максимальную длину строк
char *HTTP_GetInfo(uint32_t reqNum, char *bufIn, char *bufOut, uint16_t lenBufIn, uint16_t *lenBufOut)
{
    char str[110];
    char tmp_str[220];
    uint8_t len;
    char tempStr[30];
    strncpy(tempStr, bufIn, 30);

    (void)lenBufIn;
    (void)reqNum;

    /* В запросе нет параметров, нужно формировать JSON ответ */
    if (strpbrk(tempStr, "?") == 0) {
        memset(bufOut, 0, SEND_BUF_MAX_LEN);
        /* Headers для поддержки saffari */
        strcpy(bufOut, "HTTP/1.0 200 OK\r\nContent-Type:text/html\r\n\r\n");

        GetWorkTimeStr(str, &len);
        strcat(bufOut, "{\"uptime\":\"");
        strncat(bufOut, str, len);

        GetModelStr(str, &len);
        strcat(bufOut, "\",\"model\":\"");
        strncat(bufOut, str, len);

        GetProductionDataStr(str, &len);
        strcat(bufOut, "\",\"prodate\":\"");
        strncat(bufOut, str, len);

        GetVersionStr(str, &len);
        strcat(bufOut, "\",\"fwversion\":\"");
        strncat(bufOut, str, len);

        GetMacStr(str, &len);
        strcat(bufOut, "\",\"macaddr\":\"");
        strncat(bufOut, str, len);

        GetSerialNumberStr(str, &len);
        strcat(bufOut, "\",\"serno\":\"");
        strncat(bufOut, str, len);

        GetNameDeviceStr(str, &len);
        str[len] = 0;
        strcat(bufOut, "\",\"sysname\":\"");
        memset(tmp_str, 0, sizeof(tmp_str));
        if (ConvertStringForJSON(str, tmp_str, len)) {
            strncat(bufOut, tmp_str, strlen(tmp_str));
        } else {
            strncat(bufOut, str, len);
        }

        GetOwnerStr(str, &len);
        str[len] = 0;
        strcat(bufOut, "\",\"owner\":\"");
        memset(tmp_str, 0, sizeof(tmp_str));
        if (ConvertStringForJSON(str, tmp_str, len)) {
            strncat(bufOut, tmp_str, strlen(tmp_str));
        } else {
            strncat(bufOut, str, len);
        }

        GetLocationStr(str, &len);
        str[len] = 0;
        strcat(bufOut, "\",\"sysLocation\":\"");
        memset(tmp_str, 0, sizeof(tmp_str));
        if (ConvertStringForJSON(str, tmp_str, len)) {
            strncat(bufOut, tmp_str, strlen(tmp_str));
        } else {
            strncat(bufOut, str, len);
        }

        GetCommentsStr(str, &len);
        str[len] = 0;
        strcat(bufOut, "\",\"comment\":\"");
        memset(tmp_str, 0, sizeof(tmp_str));
        if (ConvertStringForJSON(str, tmp_str, len)) {
            strncat(bufOut, tmp_str, strlen(tmp_str));
        } else {
            strncat(bufOut, str, len);
        }

        GetUPSModelStr(str, &len);
        strcat(bufOut, "\",\"ups_model\":\"");
        strncat(bufOut, str, len);

        GetUPSSerialStr(str, &len);
        strcat(bufOut, "\",\"ups_sn\":\"");
        strncat(bufOut, str, len);

        GetUPSVersionStr(str, &len);
        strcat(bufOut, "\",\"ups_fwversion\":\"");
        strncat(bufOut, str, len);

        /* Признак изменения сетевых настроек */
        GetWebReinitFlag(str, &len);
        strcat(bufOut, "\",\"netsettings_changed\":\"");
        strncat(bufOut, str, len);

        strncat(bufOut, "\"}", 2);

        *lenBufOut = strlen(bufOut);
        return bufOut;
    }
    *lenBufOut = 0;
    return NULL;
}

void HTTP_GetHistoryPage(char *buf, uint32_t pageNumber)
{
    char str[16];
    uint16_t len;

    strcpy(buf, "HTTP/1.1 200 OK\r\nContent-Type:text/html\r\n\r\n");

    strcat(buf, "{\"page\": [");
    History_GetPage(buf, pageNumber);
    len = strlen(buf);
    buf += len - 1;
    *buf = 0;
    buf -= len - 1;

    /* Количество страниц */
    memset(str, 0, 16);
    sprintf(str, "%i", History_GetPageCount());
    strcat(buf, "],\"pages\":\"");
    strcat(buf, str);

    /* Часовой пояс */
    GetSntpTimeZoneStr(str, (uint8_t *)&len);
    strcat(buf, "\",\"utc\":\"");
    strncat(buf, str, len);

    GetUnixTimeStr(str, (uint8_t *)&len);
    strcat(buf, "\",\"utm\":\"");
    strncat(buf, str, len);

    strcat(buf, "\"}");

    //printf(buf);
}

void HTTP_GetUpsHistoryPage(char *buf, uint32_t pageNumber)
{
    char str[16];
    uint16_t len;

    strcpy(buf, "HTTP/1.1 200 OK\r\nContent-Type:text/html\r\n\r\n");

    strcat(buf, "{\"page\": [");
    LOG_GetPage(buf, pageNumber);
    len = strlen(buf);
    buf += len - 1;
    *buf = 0;
    buf -= len - 1;

    /* Количество страниц */
    memset(str, 0, 16);
    sprintf(str, "%i", LOG_GetPageCount());
    strcat(buf, "],\"pages\":\"");
    strcat(buf, str);

    /* Часовой пояс */
    GetSntpTimeZoneStr(str, (uint8_t *)&len);
    strcat(buf, "\",\"utc\":\"");
    strncat(buf, str, len);

    GetUnixTimeStr(str, (uint8_t *)&len);
    strcat(buf, "\",\"utm\":\"");
    strncat(buf, str, len);

    strcat(buf, "\"}");

    //printf(buf);
}

/**
  * @brief  Возвращает uptime, freq, dutycicle
  */
void HTTP_GetProgonParams(char *buf)
{
    char str[20];
    u32_t tick;

    strcpy(buf, "HTTP/1.0 200 OK\r\nContent-Type:text/html\r\n\r\n");

    strcat(buf, "{\"uptime\":\"");
    //  snmp_get_sysuptime(&tick);
    tick = tick / 100;
    memset(str, 0, 20);
    sprintf(str, "%d", tick);
    strncat(buf, str, strlen(str));

    strncat(buf, "\"}", 2);
}

/**
  * @brief  Сброс настроек (всё кроме сетевых параметров)
  */
void HTTP_ResetSettings(void)
{
    //taskENTER_CRITICAL();

    SNMP_SendUserTrap(DEVICE_RESTORED);
    syslog_str(SYSLOG_INFORMATIONAL, "Сброс настроек (Администратор)");
    log_event_data(LOG_SYSTEM_DEFCONFIG, name_login);
    vTaskDelay(500);
    SETTINGS_SetPartDefault();
    SETTINGS_Save();

    //taskEXIT_CRITICAL();
}

/**
  * @brief  Сохранение настроек
  */
void HTTP_SaveSettings(void)
{
    //  taskENTER_CRITICAL();

    SETTINGS_Save();
    log_event_data(LOG_SETTING_SAVE, name_login);

    //  taskEXIT_CRITICAL();

}

/**
  * @brief  Перезагрузка контроллера
  */
char *HTTP_Reboot(uint32_t reqNum, char *bufIn, char *bufOut, uint16_t lenBufIn, uint16_t *lenBufOut)
{
    (void)bufIn;
    (void)lenBufIn;
    (void)reqNum;

    strcpy(bufOut, "HTTP/1.1 200 OK\r\n");
    *lenBufOut = strlen(bufOut);

    /* Запускаем задачу отложенной перезагрузки. Контроллер должен успеть
     * отправить ответ серверу об успешном выполнении команды */
    HTTP_StartResetTask(false);

    return bufOut;
}

/**
  * @brief  Запускает задачу ресета контроллера
  * @param fBootMode:
  *        true - перед перезагрузкой будет установлен флаг входа в режим
  *        bootloader и перезаписаны настройки
  *
  *        false - обычная перезагрузка
  */
void HTTP_StartResetTask(bool fBootMode)
{
    static bool temp;

    temp = fBootMode;
    xTaskCreate(vTaskReboot, "RebootTask", configMINIMAL_STACK_SIZE,
        (void *)&temp, tskIDLE_PRIORITY, NULL);
}

/**
  * @brief
  */
void vTaskReboot(void *pvParameters)
{
    bool mode = false;  // тип перезагрузки (нужно ли устанавливать флаг bootloader)

    for (;;) {
        mode = *(bool *)pvParameters;

        if (mode) {
            SNMP_SendUserTrap(FW_VERSION_UPDATE);
            source_act_t src_act = get_act_source();
            syslog(SYSLOG_INFORMATIONAL, "Запуск обновления ПО (%s)", src_act == FTP_ACT ? "FTP" : "HTTP");
#ifdef CLI_ENABLE
            if (src_act == CLI_ACT) {
                log_event_data(LOG_UPDATE_SOFT, "Администратор");
            } else
#endif
                log_event_data(LOG_UPDATE_SOFT, name_login);
            SetLoadMode();
#ifdef CLI_ENABLE
            if (src_act == CLI_ACT) {
                SETTINGS_Save();
                log_event_data(LOG_SETTING_SAVE, "Администратор");
            } else
#endif
            HTTP_SaveSettings();
            vTaskDelay(2000);
            vTaskDelay(1010);
            Reboot(src_act);
        } else {
            vTaskDelay(1000);
            vTaskDelay(1010);
            Reboot(SNMP_ACT);
        }
    }
}

/********************************* (C) ROTEK **********************************/