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

#include "stm32f4xx.h"
#include "snmp_api.h"
#include "trap_api.h"
#include "settings_api.h"
#include "common_config.h"
#include "rtc.h"
#include "megatec.h"
#include "log.h"
#include "parameters.h"

#include "apps/snmp.h"
#include "snmp_mib2.h"

#include "snmp_core.h"
#include "private_mib.h"

#include "snmp_trap_pdu2.h"

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

#include "lwip/opt.h"
#include "lwip/api.h"
#include "lwip/sys.h"
#include "lwip/udp.h"

//#include "snmp.h"
//#include "snmp_msg.h"
//#include "private_mib.h"

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

/**
  * @brief  Пул всех возможных трапов устройства
  */
extern TRAP_t traps[];

/**
  * @brief  Общая структура настроек
  */
extern SETTINGS_t sSettings;

extern bool isIpReceived;

/**
  * @brief  Очередь для отправки трапов
  */
QueueHandle_t    SNMP_TrapQueue;

/**
  * @brief  Инициализация параметров SNMP
  * @retval
  */
void SNMP_Init(void)
{
    //snmp_mib *my_snmp_mibs[] = { &mib2, &private_mib };
    //SETTINGS_SetSnmpDef();
    SNMP_AgentInit();
    lwip_privmib_init();
    //snmp_set_mibs(my_snmp_mibs, LWIP_ARRAYSIZE(my_snmp_mibs));
}


/**
  * @brief  Системный тик SNMP. Таск должен вызываться с частотой 100Гц.
  * @retval
  */
void SNMP_SysUpTimeTask(void *arg)
{
    TickType_t xLastWakeTime;
    const TickType_t xFrequency = 10;

    xLastWakeTime = xTaskGetTickCount();

    while (1) {
        vTaskDelayUntil( &xLastWakeTime, xFrequency );
        //snmp_inc_sysuptime();
    }
}

/**
  * @brief  Тестовый таск для проверки отправки трапов
  * @retval
  */
void snmp_trap_tread(void *arg)
{
    uint8_t trapName;

    while (1) {
        if (isIpReceived) {
            if (xQueueReceive(SNMP_TrapQueue, &trapName, 0) == pdTRUE) {
#ifdef NOTIFICATION_CONTROL_ENABLE
                if (sSettings.sFlagNotification[trapName])
#endif
                {
                    SNMP_SetManagerIP(sSettings.sSnmp.managerIP);
                    SNMP_SendVarbindTrap(&traps[trapName]);

                    SNMP_SetManagerIP(sSettings.sSnmp.managerIP2);
                    SNMP_SendVarbindTrap(&traps[trapName]);

                    SNMP_SetManagerIP(sSettings.sSnmp.managerIP3);
                    SNMP_SendVarbindTrap(&traps[trapName]);

                    SNMP_SetManagerIP(sSettings.sSnmp.managerIP4);
                    SNMP_SendVarbindTrap(&traps[trapName]);

                    SNMP_SetManagerIP(sSettings.sSnmp.managerIP5);
                    SNMP_SendVarbindTrap(&traps[trapName]);
                }
            }
        }
        vTaskDelay(10);
    }
}

/**
  * @brief  Инициализация SNMP агента
  * @retval
  */
void SNMP_AgentInit(void)
{
    SNMP_SetObjDescr();

    SNMP_SetReadCommunity(sSettings.sSnmp.readCommunity);
    SNMP_SetWriteCommunity(sSettings.sSnmp.writeCommunity);
    SNMP_SetSysContact(&sSettings.sSnmp.sysContact);
    SNMP_SetSysName(&sSettings.sSnmp.sysName);
    SNMP_SetSysLocation(&sSettings.sSnmp.sysLocation);
    SNMP_SetManagerIP(sSettings.sSnmp.managerIP);  //SNMP_SetManagerIP("192.168.14.37");
    SNMP_SetObjID();
    SNMP_SetTrapOnOff(1);

    SNMP_InitTrapsBase();

    snmp_init();
    udp_init();

    SNMP_TrapQueue = xQueueCreate(SNMP_TRAP_QUEUE_SIZE, sizeof(uint8_t));
}

/**
  * @brief  Пользовательская функция для отправки трапа из массива traps[]
  *         Трап помещается в очередь. Работа с очередью происходит по принципу
  *         FIFO буфера.
  *         Если в настройках трапа отправка отключена, то трап игнорируется.
  * @retval
  */
void SNMP_SendUserTrap(uint8_t trapName)
{
    uint16_t availableSpace;
    uint8_t  dummyTrap;

    if (traps[trapName].trapEnable) {
        availableSpace = uxQueueSpacesAvailable(SNMP_TrapQueue);

        if (availableSpace == 0) {
            xQueueReceive(SNMP_TrapQueue, &dummyTrap, 0);
        }

        xQueueSend(SNMP_TrapQueue, &trapName, 0);
    }
}

/**
  * @brief  Установить SNMP Descriptor
  * @retval
  */
// TODO
void SNMP_SetObjDescr(void)
{
    char str[50];
    uint8_t len = 0;

    memset(sSettings.sSnmp.sysDesc.description, 0, sizeof(sSettings.sSnmp.sysDesc.description));

#if HARDWARE_BT6711 || HARDWARE_BT6711_V1
    strcpy(sSettings.sSnmp.sysDesc.description, HW_REV);
#elif HARDWARE_BT6709_MTS || HARDWARE_BT6710
    strcat(sSettings.sSnmp.sysDesc.description, UPS.model);
#else
    strcpy(sSettings.sSnmp.sysDesc.description, HW_REV);
    strcat(sSettings.sSnmp.sysDesc.description, " ");
    strcat(sSettings.sSnmp.sysDesc.description, VERSION);
    strcat(sSettings.sSnmp.sysDesc.description, " ");
    sSettings.sSnmp.sysContact.contact[sSettings.sSnmp.sysContact.len] = 0;
    strcat(sSettings.sSnmp.sysDesc.description, sSettings.sSnmp.sysContact.contact);
    strcat(sSettings.sSnmp.sysDesc.description, " ");
    strcat(sSettings.sSnmp.sysDesc.description, sSettings.sInfo.serialNumber);
    strcat(sSettings.sSnmp.sysDesc.description, " ");
    strcat(sSettings.sSnmp.sysDesc.description, UPS.model);
    strcat(sSettings.sSnmp.sysDesc.description, " ");
    memset(str, 0, sizeof(str));
    GetUPSSerialStr(str, &len);
    strcat(sSettings.sSnmp.sysDesc.description, str);
#endif
    sSettings.sSnmp.sysDesc.len = strlen(sSettings.sSnmp.sysDesc.description);
    snmp_mib2_set_sysdescr((u8_t *)sSettings.sSnmp.sysDesc.description, &sSettings.sSnmp.sysDesc.len);
}

void SNMP_SettingsSave(void)
{
    SETTINGS_Save();
    log_event_data(LOG_SETTING_SAVE, "Администратор (SNMP)");
    SNMP_SetObjDescr();
}

/**
  * @brief  Установить SNMP Community для чтения
  */
void SNMP_SetReadCommunity(char *comm)
{
    //strcpy(sSettings.sSnmp.readCommunity, comm);
    snmp_set_community(comm);
}

/**
  * @brief  Установить SNMP Community для записи
  */
void SNMP_SetWriteCommunity(char *comm)
{
    //strcpy(sSettings.sSnmp.writeCommunity, comm);
    snmp_set_community_write(comm);
}

/**
  * @brief  Установить SNMP SysContact
  * @retval
  */
void SNMP_SetSysContact(SNMP_SYS_CONTACT_t *con)
{
    snmp_mib2_set_syscontact((u8_t *)con->contact, &con->len, (sizeof(con->contact) - 1));
}

/**
  * @brief  Установить SNMP SysName
  * @retval
  */
void SNMP_SetSysName(SNMP_SYS_NAME_t *name)
{
    snmp_mib2_set_sysname((u8_t *)name->name, &name->len, (sizeof(name->name) - 1));
}

/**
  * @brief  Установить SNMP SysLocation
  * @retval
  */
void SNMP_SetSysLocation(SNMP_SYS_LOCATION_t *loc)
{
    snmp_mib2_set_syslocation((u8_t *)loc->location, &loc->len, (sizeof(loc->location) - 1));
}

/**
  * @brief  Установить SNMP SysManagerIP
  * @retval
  */
void SNMP_SetManagerIP(char *ip)
{
    static ip_addr_t trap_addr;

    ipaddr_aton(ip, &trap_addr);
    // snmp_trap_dst_ip_set(0, &trap_addr);
    snmp_trap_pduv2_dst_ip_set(0, &trap_addr);
}

/**
  * @brief  Установить SNMP Object ID
  * @retval
  */
void SNMP_SetObjID(void)
{
    static struct snmp_obj_id my_object_id = {9, {1, 3, 6, 1, 4, 1, 41752, 911, SNMP_DEV_ROOT_OID}};

    snmp_set_device_enterprise_oid(&my_object_id);
}

/**
  * @brief  Установка sysObjectID согласно логике МТС (данное решение согласовано с заказчиком)
  */
const struct snmp_obj_id* SNMP_GetSysObjectIDforMTS(void)
 {
    if (strlen(UPS.model) == 0) {
      static struct snmp_obj_id my_object_id = {9, {1, 3, 6, 1, 4, 1, 41752, 911, SNMP_DEV_ROOT_OID}};
      return &my_object_id;
    } else if(strncmp(UPS.model, "RTMP-I 600r", 11) == 0) {
     static struct snmp_obj_id my_object_id = {10, {1, 3, 6, 1, 4, 1, 41752, 911, SNMP_DEV_ROOT_OID, 600}};
     return &my_object_id;
    } else if(strncmp(UPS.model, "RTMP-II r", 9) == 0) {
      static struct snmp_obj_id my_object_id = {10, {1, 3, 6, 1, 4, 1, 41752, 911, SNMP_DEV_ROOT_OID, 1000}};
      return &my_object_id;
    } else if(strncmp(UPS.model, "RTMP-II v2r", 11) == 0) {
      static struct snmp_obj_id my_object_id = {10, {1, 3, 6, 1, 4, 1, 41752, 911, SNMP_DEV_ROOT_OID, 2000}};
      return &my_object_id;
    } else if(strncmp(UPS.model, "RTMP-II v3r", 11) == 0) {
      static struct snmp_obj_id my_object_id = {10, {1, 3, 6, 1, 4, 1, 41752, 911, SNMP_DEV_ROOT_OID, 3000}};
      return &my_object_id;
    } else if(strncmp(UPS.model, "RTMP-II v10r", 12) == 0) {
      static struct snmp_obj_id my_object_id = {10, {1, 3, 6, 1, 4, 1, 41752, 911, SNMP_DEV_ROOT_OID, 10000}};
      return &my_object_id;
    } else if(strncmp(UPS.model, "RTPS-v3310r", 11) == 0) {
      static struct snmp_obj_id my_object_id = {10, {1, 3, 6, 1, 4, 1, 41752, 911, SNMP_DEV_ROOT_OID, 3310}};
      return &my_object_id;
    } else {
      static struct snmp_obj_id my_object_id = {9, {1, 3, 6, 1, 4, 1, 41752, 911, SNMP_DEV_ROOT_OID}};
      return &my_object_id;
    }
    return 0;
 }

/**
  * @brief  Вкл/выкл трапы
  * @retval
  */
void SNMP_SetTrapOnOff(uint8_t state)
{
    //snmp_trap_dst_enable(0, (u8_t)state);
    snmp_trap_pduv2_dst_enable(0, (u8_t)state);
}


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