#include "port.h"
#include "main.h"
#include "cmsis_os2.h"

/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
#include <string.h>

/* ----------------------- Defines ------------------------------------------*/
#define EVENT_MBSLAVE_HANDLE_RECEIVED_DATA  0x00000001UL

/* ----------------------- Start implementation -----------------------------*/
BOOL xMBPortSerialInit(eMBRTUSlaveObj *pObj) {

    /* set serial configure parameter */
    pObj->pUartHandle.Init.BaudRate = pObj->ulBaudRate;
    pObj->pUartHandle.Init.WordLength =  UART_WORDLENGTH_8B ;
    pObj->pUartHandle.Init.StopBits = UART_STOPBITS_1;
    switch (pObj->eParity) {
        case MB_PAR_NONE:
            pObj->pUartHandle.Init.Parity = UART_PARITY_NONE;
            break;

        case MB_PAR_ODD:
            pObj->pUartHandle.Init.Parity = UART_PARITY_ODD;
            break;

        case MB_PAR_EVEN:
            pObj->pUartHandle.Init.Parity = UART_PARITY_EVEN;
            break;

        default:
            return FALSE;
    }
    pObj->pUartHandle.Init.Mode = UART_MODE_TX_RX;
    pObj->pUartHandle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    pObj->pUartHandle.Init.OverSampling = UART_OVERSAMPLING_16;
    /* open serial device */
    if (HAL_UART_Init(&pObj->pUartHandle) != HAL_OK) {
        Error_Handler();
        return FALSE;
    }
    return TRUE;
}

void rs485_slave_rx_mode(eMBRTUSlaveObj * obj) {
    HAL_GPIO_WritePin(obj->enPort, obj->enPin, GPIO_PIN_RESET);
}

void rs485_slave_tx_mode(eMBRTUSlaveObj * obj) {
    HAL_GPIO_WritePin(obj->enPort, obj->enPin, GPIO_PIN_SET);
}

void vMBPortSerialEnable(eMBRTUSlaveObj * pObj, BOOL xRxEnable, BOOL xTxEnable) {
    if (xRxEnable) {
        /* switch 485 to receive mode */
        rs485_slave_rx_mode(pObj);
        __HAL_UART_ENABLE_IT(&pObj->pUartHandle, UART_IT_RXNE);
    } else {
        /* switch 485 to transmit mode */
        rs485_slave_tx_mode(pObj);
        __HAL_UART_DISABLE_IT(&pObj->pUartHandle, UART_IT_RXNE);
    }
    if (xTxEnable) {
        __HAL_UART_ENABLE_IT(&pObj->pUartHandle, UART_IT_TXE);
    } else {
        __HAL_UART_DISABLE_IT(&pObj->pUartHandle, UART_IT_TXE);
    }

}

void vMBPortClose(eMBRTUSlaveObj *pObj) {
    HAL_UART_AbortReceive_IT(&pObj->pUartHandle);
    HAL_UART_AbortTransmit_IT(&pObj->pUartHandle);
}

BOOL xMBPortSerialPutByte(eMBRTUSlaveObj *pObj, CHAR ucByte) {
    if (HAL_UART_Transmit(&pObj->pUartHandle, (uint8_t *) &ucByte, 1, 0x01) != HAL_OK)
        return FALSE;
    else
        return TRUE;
}

/*
 * A Function to send all bytes in one call.
 */
BOOL xMBPortSerialPutBytes(eMBRTUSlaveObj *pObj, volatile UCHAR *ucByte, USHORT usSize) {
    if (HAL_UART_Transmit(&pObj->pUartHandle, (uint8_t *) ucByte, usSize, 300) != HAL_OK)
        return FALSE;
    else
        return TRUE;
}

BOOL xMBPortSerialGetByte(eMBRTUSlaveObj *pObj, CHAR *pucByte) {
    if (HAL_UART_Receive(&pObj->pUartHandle, (uint8_t *) pucByte, 1, 0x01) != HAL_OK)
        return FALSE;
    else
        return TRUE;
}

/*
 * Create an interrupt handler for the transmit buffer empty interrupt
 * (or an equivalent) for your target processor. This function should then
 * call pxMBFrameCBTransmitterEmpty( ) which tells the protocol stack that
 * a new character can be sent. The protocol stack will then call
 * xMBPortSerialPutByte( ) to send the character.
 */
void prvvUARTTxReadyISR(eMBRTUSlaveObj *pObj) {
    pxMBFrameCBTransmitterEmpty(pObj);
}

/*
 * Create an interrupt handler for the receive interrupt for your target
 * processor. This function should then call pxMBFrameCBByteReceived( ). The
 * protocol stack will then call xMBPortSerialGetByte( ) to retrieve the
 * character.
 *
 * quanghona: This function is called when IDLE is detected. thus will then
 * trigger the received message handler.
 */
void prvvUARTRxISR(eMBRTUSlaveObj *pObj) {
    pxMBFrameCBByteReceived(pObj);
}

