#include "mcu_ctrl_board.h"
#include "../../rtu_linux_modules/swapi/subjects/serial/serial.h"
#include "lwjson/lwjson.h"

// 模块名称
static const char MODULE_NAME[] = "MCUCtrlBoard";

// 定义与单片机进行通讯的结构体, 同时定义结构体对象
typedef struct
{
  void *h;
  lwjson_stream_parser_t parser;
  unsigned long jsonStartTick; // JSON数据包开始接收时的单调计时基准, 用于超时判断
} SMCBCom;

static SMCBCom s_myCom = { 0 };

// 声明Go中处理串口请求并返回应答结果的函数(供C调用)
extern char *RTU_JsonMsgProcCb(const char *jsonStr);

// 接收处理来自MCU控制板的数据报文帧, 串口-数据回调
static int comio_data_recv_proc(unsigned long wParam/*传递打开的串口句柄*/, unsigned long lParam/*保留暂未使用*/)
{
  SMCBCom *pComIO = &s_myCom; void *pSerial = pComIO->h;
  const unsigned char *pRecvBuf = serial_get_recv_buffer(pSerial); int nRecvBytes = serial_get_recv_buffer_bytes(pSerial);
  const char *log_prefix = serial_get_log_prefix(pSerial); lwjsonr_t ret;

  ret = lwjson_stream_parse(&pComIO->parser, pRecvBuf[nRecvBytes - 1]); // 把串口数据逐字节的喂给解析器去解析
  switch(ret)
  {
  case lwjsonERRJSON:             // 1, 无效的字符  
  case lwjsonSTREAMWAITFIRSTCHAR: // 2, 等待首字符
    serial_printf_recv_buffer(pSerial, LEVEL_TRACE);
    sw_log_warn("[%s] %s discarding invalid JSON byte: 0x%02X(buffer length: %d)!", MODULE_NAME, log_prefix, pRecvBuf[0], nRecvBytes);
    serial_clear_recv_buffer(pSerial);
    break;
  case lwjsonSTREAMINPROG:        // 3, 正在解析中
    if(0 == pComIO->jsonStartTick) xgettickcount(&pComIO->jsonStartTick);
    break;
  case lwjsonSTREAMDONE:          // 4, 解析已完成
    sw_log_debug("[%s] %s received a request(%d bytes): %s", MODULE_NAME, log_prefix, nRecvBytes, pRecvBuf);
    char *pResponse = RTU_JsonMsgProcCb((const char *)pRecvBuf);
    if(pResponse)
    {
      int nRspBytes = strlen(pResponse);
      int sendBytes = serial_send_data(pSerial, (const unsigned char *)pResponse, nRspBytes);
      if(sendBytes == nRspBytes) sw_log_debug("[%s] %s sent a response(%d bytes): %s", MODULE_NAME, log_prefix, nRspBytes, pResponse);
      else sw_log_error("[%s] %s failed to send a response(%d bytes, ret=%d): %s!!", MODULE_NAME, log_prefix, nRspBytes, sendBytes, pResponse);
      free(pResponse); // 释放Go侧C.CString()分配的堆内存
    }
    pComIO->jsonStartTick = 0; serial_clear_recv_buffer(pSerial); lwjson_stream_reset(&pComIO->parser);
    break;
  case lwjsonERRMEM:             // 5, 嵌套的太深("LWJSON_CFG_STREAM_STACK_SIZE")
    sw_log_error("[%s] %s parser stack overflow (ERRMEM), resetting parser!", MODULE_NAME, log_prefix);
    pComIO->jsonStartTick = 0; serial_clear_recv_buffer(pSerial); lwjson_stream_reset(&pComIO->parser);
    break;
  default: // 流式解析时, 其它的返回值, 都不应该出现
    sw_log_fatal("[%s] %s internal error, lwjson_stream_parse() returned: %d, data receive thread will exit!!!", MODULE_NAME, log_prefix, ret);
    return -1;
  }

  return 1;
}

// 接收处理来自MCU控制板的数据报文帧, 串口-空闲回调
static int comio_data_idle_proc(unsigned long wParam/*传递打开的串口句柄*/, unsigned long lParam/*保留暂未使用*/)
{
  SMCBCom *pComIO = &s_myCom; void *pSerial = pComIO->h;
  const unsigned char *pRecvBuf = serial_get_recv_buffer(pSerial); int nRecvBytes = serial_get_recv_buffer_bytes(pSerial);
  const char *log_prefix = serial_get_log_prefix(pSerial); unsigned long nowTick;

  xgettickcount(&nowTick);
  if(pComIO->jsonStartTick > 0 && (nowTick - pComIO->jsonStartTick) > 50/*空闲超时单位: ms*/)
  {
    sw_log_warn("[%s] %s timeout occurred while receiving a request(%d bytes): %s!", MODULE_NAME, log_prefix, nRecvBytes, pRecvBuf);
    pComIO->jsonStartTick = 0; serial_clear_recv_buffer(pSerial); lwjson_stream_reset(&pComIO->parser);
  }

  return 1;
}

// 打开与MCU控制板的串口通讯, 返回: 0成功, <0时失败
int MCBComInit()
{
  lwjson_stream_init(&s_myCom.parser, NULL);

  const char *serialName = "/dev/ttymxc2"; int baudrate = 115200;
  const char *parityCheck = "none"; // 无校检
  s_myCom.h = serial_open(serialName, baudrate, parityCheck, \
                          comio_data_recv_proc, comio_data_idle_proc, NULL);
  if(!s_myCom.h)
  {
    sw_log_error("[%s] failed to open the \"%s:%d(%s parity)\" device!!", \
                 MODULE_NAME, serialName, baudrate, parityCheck);
    MCBComExit(); return -1;
  }

  return 0;
}

// 关闭与MCU控制板的串口通讯, 返回: 0成功, <0时失败
int MCBComExit()
{
  if(s_myCom.h) serial_close(s_myCom.h, WAITTHRD_SAFEEXIT_TIMEOUT);
  memset(&s_myCom, 0, sizeof(s_myCom));
  return 0;
}

// 主动的发送指令给MCU控制板, 返回: 0成功, <0时失败
int MCBComSendCmd(const char *cmd)
{
  if(!s_myCom.h || !cmd) return -1;
  int nBytes = strlen(cmd);
  int ret = serial_send_data(s_myCom.h, (const unsigned char *)cmd, nBytes);
  if(ret != nBytes)
  {
    sw_log_error("[%s] failed to send a request(%d bytes, ret=%d): %s!!", MODULE_NAME,  ret, nBytes, cmd);
    return -2; 
  }
  sw_log_debug("[%s] sent a request(%d bytes): %s", MODULE_NAME, nBytes, cmd);
  return 0;
}
