#include "at32f403a_407.h"
#include "common_config.h"
#include "modbus.h"
#include "modbus_params.h"
#include "FreeRTOS.h"
#include "task.h"
#include "mb.h"
#include "mbport.h"
#include "mbrtu.h"
#include "tim_delay.h"
#include "settings_api.h"
#include "iap.h"
#include <stdio.h>
#include <string.h>
#include <stdbool.h>

#define REG_HOLDING_START		( 1 )
#define REG_HOLDING_NREGS		( 5 )



static USHORT	usRegHoldingStart = REG_HOLDING_START;
extern UCHAR    rcvAddress;

//osThreadId modbus_task_handle;
static void modbus_task(void *params);
//osThreadId modbus_params_handle;
static void modbus_params(void *params);

//osTimerId reset_timer_handle;
//osTimerId settings_timer_handle;
//osTimerId modbus_timer_handle;

mb_delay_action_t mb_action = MB_NO_ACTION;
uint8_t new_slave_addr;
bool mb_package_flag = false;



//
void mb_init(void)
{
    uint32_t baud;
    eMBParity par;
    UCHAR mb_addr = cm_gpio_get_mb_addr();
    unsigned int stop_bits = 1;
	const UCHAR ucSlaveID[] = {0xAA, 0xBB, 0xCC};

    iap_init();
    
	mb_init_params();

    settings_set_modbus_params(settings.com_settings.mb_port);

    baud = settings_get_mb_baud(&mb_port_settings);
    par = settings_get_mb_par(&mb_port_settings);
    
    mb_helper_tim_init(baud);
    eMBInit(MB_RTU, mb_addr, 4, baud, par, stop_bits);
	eMBSetSlaveID(0x34, TRUE, ucSlaveID, 3);
	eMBEnable();
    
    
    xTaskCreate(modbus_task, "modbus_task", 2*configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL);
    
    xTaskCreate(modbus_params, "modbus_params", 4*configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL);
    
    
#if 0    
	osTimerDef(vResetTimer, modbus_reset);
	reset_timer_handle = osTimerCreate(osTimer(vResetTimer), osTimerOnce, NULL);
    
    osTimerDef(vSettingsTimer, settings_timer);
    settings_timer_handle = osTimerCreate(osTimer(vSettingsTimer), osTimerOnce, NULL);
    
    osTimerDef(vModbusTimer, modbus_port_timer);
    modbus_timer_handle = osTimerCreate(osTimer(vModbusTimer), osTimerOnce, NULL);
#endif    
}

//
void modbus_task(void *params)
{
	(void)params;

	for (;;)
	{
		eMBPoll();
	}

}

//
void modbus_params(void *params)
{
    for (;;)
    {
        if (mb_action != MB_NO_ACTION)
        {
            switch (mb_action)
            {
                // Установка нового адреса
                case MB_CHANGE_ADDR :
    
                    //settings.com_settings.mb_addr = new_slave_addr;  
                    eMBSetSlaveAddr(new_slave_addr);
                      
                break;
                
                case MB_CHANGE_PORT_SETTINGS :
#if 0                  
                    osDelay(200);
                    eMBRTUStop();
                    settings_set_modbus_params(temp_modbus_port);
                    settings_init_mb_port(settings.com_settings.mb_addr);
                    MBDBG settings_print_modbus();
                    eMBRTUStart();
                    eMBEnable();
                    mb_package_flag = false;
                    osTimerStart(modbus_timer_handle, 9800);
#endif                  
                break;
                
                // Полное восстановление дефолтных настроек
                case MB_DEF_SETTINGS :
#if 0
                    settings_set_default(true);
                    settings_save();
#endif                  
                break;  
                
                // Частичное восстановление дефолтных настроек
                case MB_PART_DEF_SETTINGS :
#if 0
                    settings_set_default(false);
                    settings_save();
#endif                  
                break;
                
                  
                case MB_SAVE_SETTINGS :
#if 0                
                    settings_save();
#endif                  
                break;
                
                case MB_SAVE_SYS_SETTINGS :
#if 0
                    if (set_sys_settings_flag) {
                        memcpy(&sys_settings, &temp_sys_settings, sizeof(sys_settings));
                        sys_settings_save();
                    }
#endif                  
                break;
                
                case MB_RESET :
                  
                    vTaskDelay(100);
                    NVIC_SystemReset();
                  
                break;
                
                default : break;
            }
            
            mb_action = MB_NO_ACTION;
        }
        vTaskDelay(10);
    }
}


// Отложенная перезагрузка в Bootloader
void modbus_reset(void const * params)
{
#if 0  
	MBDBG printf("Switch to bootloader!\r\n");
	rtc_set_load_mode(1);
	HAL_NVIC_SystemReset();
#endif    
}


// Запуск таймера на изменение системных настроек
void settings_timer(void const * params)
{
#if 0  
    set_sys_settings_flag = false;
    psw_ok = false;
#endif    
}


// Запуск таймера для изменение настроек modbus
void modbus_port_timer(void const * params)
{
#if 0
    eMBRTUStop();
  
    // Были транзакции по modbus с новыми настройками. 
    // Нужно сохранить новые настройки.
    if (mb_package_flag)
    {
        settings.com_settings.mb_port = temp_modbus_port;
        settings_set_modbus_params(settings.com_settings.mb_port);
        settings_init_mb_port(settings.com_settings.mb_addr);
        settings_save();
        
    }
    // Нужно вернуть старые настройки
    else
    {
        temp_modbus_port = settings.com_settings.mb_port;
        settings_set_modbus_params(settings.com_settings.mb_port);
        settings_init_mb_port(settings.com_settings.mb_addr);
    }
  
    
    MBDBG settings_print_modbus();
    eMBRTUStart();
    eMBEnable();
#endif    
}

// ------------------------------------------------------------------- //
//						Modbus callback's
// ------------------------------------------------------------------- //


// 04 (0x04) Read Input Registers	 
eMBErrorCode
eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
{
    return MB_ENOREG;
}


// 03 (0x03) Read Holding Registers
// 16 (0x10) Write Multiple registers
#if 0
eMBErrorCode
eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode )
{
    return MB_ENOREG;
}
#endif

// 17 (0x11) Report Slave ID (Serial Line only) 
eMBException
eMBFuncReportSlaveID( UCHAR * pucFrame, USHORT * usLen )
{
    return MB_EX_ILLEGAL_FUNCTION;
}


// чтение/запись значений из нескольких регистров флагов (Coil Status).
// чтения N регистров параметров
eMBErrorCode
eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode )
{
    return MB_ENOREG;
}

eMBErrorCode
eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
{
    return MB_ENOREG;
}

// 0x42
eMBException    
eMBSetAddrIdCB( UCHAR * pucFrame, USHORT * usLen )
{
   
    return MB_EX_ILLEGAL_FUNCTION;
}


// 0x43
eMBException
eMBSetAddrSerialCB( UCHAR * pucFrame, USHORT * usLen )
{
    return MB_EX_ILLEGAL_FUNCTION;
}

// 03 (0x03) Read Holding Registers
// чтение N регистров управления и уставок
// 16 (0x10) Write Multiple registers
// запись N регистров управления и уставок (0x10)
eMBErrorCode
eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode )
{
	eMBErrorCode eStatus = MB_ENOERR;

	int			 iRegIndex;
    
    mb_delay_action_t mb_act = MB_NO_ACTION;
	uint16_t regs_cnt = usNRegs;
	uint16_t index_param;
	uint8_t *ptr_buf = pucRegBuffer;
	uint16_t size;
#if 0
	printf("usAddress: 0x%X\r\n", usAddress);
	printf("usNRegs: %u\r\n", usNRegs);
	printf("eMode: %u\r\n", eMode);
    printf("pucRegBuffer :%X\r\n", pucRegBuffer[0]);
#endif
    mb_package_flag = true;
    
	iRegIndex = (int)(usAddress - usRegHoldingStart);

#if 1    
	switch (eMode)
	{
		case MB_REG_READ :

			while (regs_cnt)
			{
				// Поиск регистра
				if (!mb_find_param(iRegIndex, &index_param, &size))
				{
					return MB_ENOREG; // нет запрашиваемого регистра
				}
				else
				{
					MBDBG printf("reg: %X, index: %u, size: %u\r\n", iRegIndex, index_param, size);
                    MBDBG printf("reg: %X\r\n", iRegIndex);
					mb_get_param(ptr_buf,  index_param); // Вызов функции записи
					iRegIndex += size;
					ptr_buf += 2*size;
					regs_cnt -= size;
				}
			}


		break;

		case MB_REG_WRITE :

			while (regs_cnt)
			{
				// Поиск регистра
				if (!mb_find_param(iRegIndex, &index_param, &size))
				{
					return MB_ENOREG; // нет запрашиваемого регистра
				}
				else
				{
					MBDBG printf("reg: %X, index: %u, size: %u\r\n", iRegIndex, index_param, size);
					mb_act = mb_set_param(ptr_buf, index_param); // Вызов функции записи

                    if (mb_act != MB_NO_ACTION)
                        mb_action = mb_act;
                    
					iRegIndex += size;
					ptr_buf += 2*size;
					regs_cnt -= size;
                    
                    if (mb_act == MB_PAS_ERR)
                        return MB_EINVAL;
				}
			}

		break;
	}
#endif
	return eStatus;
}