#include "crc16.h"
#include "mcu_ctrl_board.h"
#include "../swapi/subjects/serial/serial.h"

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

// 告诉编译器使用一字节内存对齐
#pragma pack(push, 1)

// 单片机控制板运行配置(小端序)
typedef struct {
  uint8_t version[4];     // 版本号
  uint8_t ctrl_mode;      // 定时控制模式(0=光控, 1=时控)
  uint8_t light_duration; // 光控定时时长(单位: 小时, 范围: 1-10, 仅光控模式有效)
  uint8_t start_hour;     // 时控开始时间(小时, 0-23, 一天24小时, 仅时控模式有效)
  uint8_t end_hour;       // 时控结束时间(小时, 0-23, 一天24小时, 仅时控模式有效)
  uint8_t tp_intvl_min[2];// 拍照间隔(分钟,其值不得超过当前控制模式下允许的最大值)
} MCUScheduleCfg;

// 恢复之前原有内存字节对齐方式
#pragma pack(pop)

// 定义与单片机进行通讯的结构体
typedef struct
{
  void *h;                // 打开的串口句柄
  char work_state[4];     // 工作状态位图(共32个状态)
  void *state_rwlock;     // 状态位图读写锁
  SensorEnvData data[24]; // 接收传感器采集的环境数据
  void *edata_rwlock;     // 环境数据读写锁
} SMCBCom;

static MCUScheduleCfg cfg_params;
static SMCBCom s_myCom;

// 声明Go中的判断RTU是否已联网在线的函数(Go实现, 供C/C++调用)
extern int RTU_IsInetAvailable(); // 0未联网, 1已联网

// 声明Go中的判断RTU是否已同步时间的函数(Go实现, 供C/C++调用)
extern int RTU_IsSyncedNtpTime(); // 0未同步, 1已同步

// 声明Go中的判断RTU是否已登录后台服务器(Go实现, 供C/C++调用)
extern int RTU_IsLoginMqServer(); // 0未登录, 1已登录

// 声明Go中发送一组 温湿度光照数据的函数(Go实现, 供C/C++调用)
extern int RTU_SendGrpEnvData(SensorEnvData *pGrpData, int cnt/*数量, 一次全发是24*/); // 0成功, <0时失败

// 声明Go中发送一条 温湿度光照数据的函数(Go实现, 供C/C++调用)
extern int RTU_SendOneEnvData(SensorEnvData *pOneData); // 0成功, <0时失败

// 声明Go中通知RTU需要进行一次拍照的函数(Go实现, 供C/C++调用)
extern int RTU_RequestTakePhoto(); // 0成功, <0时失败

// 声明Go中加载MCU配置项"运行参数"的函数(Go实现, 供C/C++调用)
extern int RTU_LoadMCUParamsCfg(); // 0成功, <0时失败

// 声明Go中通知RTU进行一次掉电关机的函数(Go实现, 供C/C++调用)
extern int RTU_NotifyPwrWillOff(); // 0成功, <0时失败

// 反转字节, 可用于处理大小端字节序的相互转换,"n"不必是2的倍数
static inline void swap_bytes(char *a, size_t n)
{
  for(char *p = a, *q = a + n - 1; p < q; p++, q--) { char t = *p; *p = *q; *q = t; }
}

// 获取32位秒级时间戳(存在"​​2038-01-19 03:14:07 UTC​​"溢出风险)
static inline uint32_t now_sec32(void)
{
  return (uint32_t)time(NULL);
}

// 发送数据前, 处理帧内容的转义, 返回添加转义后的数据帧及其长度
static int frame_doEscapeChars(const unsigned char *src, int len, unsigned char **dst)
{
  if(!src ||!dst || len < 2 || src[0]!= 0x7D || src[len-1]!= 0x7E) return -1;
  unsigned char *ptr = (unsigned char *)sw_heap_malloc(2+2*(len-2));
  if(!ptr) return -2;
  ptr[0] = src[0]; int j = 1;
  for(int i = 1; i < (len-1); i++)
  {
    if(0);
    else if(src[i] == 0x7D) { ptr[j++] = 0x7D; ptr[j++] = 0x5D; }
    else if(src[i] == 0x7E) { ptr[j++] = 0x7D; ptr[j++] = 0x5E; }
    else ptr[j++] = src[i];
  }
  ptr[j++] = src[len-1]; *dst = ptr;
  return j;
}

// 接收数据后, 反转义帧数据内容, 返回去掉转义后的原始帧及其长度
static int frame_unescapeChars(const unsigned char *src, int len, unsigned char **dst)
{
  if(!src ||!dst || len < 2 || src[0]!= 0x7D || src[len-1]!= 0x7E) return -1;
  unsigned char *ptr = (unsigned char *)sw_heap_malloc(2+1*(len-2));
  if(!ptr) return -2;
  ptr[0] = src[0]; int j = 1;
  for(int i = 1; i < (len-1); i++)
  {
    if(0);
    else if(src[i] == 0x7D && src[i+1] == 0x5D) { ptr[j++] = 0x7D; i++; }
    else if(src[i] == 0x7D && src[i+1] == 0x5E) { ptr[j++] = 0x7E; i++; }
    else ptr[j++] = src[i];
  }
  ptr[j++] = src[len-1]; *dst = ptr;
  return j;
}

// 接收、处理来自MCU控制板的数据报文帧, 串口-线程回调
#define MIN_FRAME_LEN 6 // 最小帧(数据区为零)的长度
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); int ret; bool bPrintRecvBuf = true;
  unsigned char *req = NULL; int nReqBytes; u_int16_t crc1, crc2, crc3; int nReqDataLen;
  unsigned char sendBuf[MAX_LINE_CHARS] = { 0 }; int nSendBytes;
  unsigned char *rsp = NULL; int nRspBytes; char log_rsp[MAX_LINE_CHARS] = { 0 }; static bool bTakenPhoto = false; /*标记本次是否已拍照*/

  // 0, 判定接收有效的请求帧
  if((nRecvBytes == 1 && pRecvBuf[0] != 0x7D) || (nRecvBytes == 2 && pRecvBuf[1] == 0x5D)) goto ret_p3;
  else if(nRecvBytes >= MIN_FRAME_LEN && pRecvBuf[nRecvBytes-1] == 0x7E) goto ret_p1;
  else goto ret_p4;

  // 1, 收到一个完整的请求帧
ret_p1:
  // 1.1, 打印接收到的帧内容
  bPrintRecvBuf = false;
  serial_printf_recv_buffer(pSerial, LEVEL_DEBUG);
  sw_log_debug("[%s] %s received a request(%d bytes)", MODULE_NAME, log_prefix, nRecvBytes);

  // 1.2, 去掉转义得到原始帧
  ret = frame_unescapeChars(pRecvBuf, nRecvBytes, &req);
  if(ret < MIN_FRAME_LEN)
  {
    sw_log_error("[%s] %s failed to unescape the received request(%d)!!", MODULE_NAME, log_prefix, ret);
    goto ret_p3;
  }
  nReqBytes = ret;

  // 1.3, 该请求帧合法性校验
  crc1 = MAKEWORD(req[nReqBytes-3], req[nReqBytes-2]);
  crc2 = CRC16(req+1, nReqBytes-4);
  if(crc1 != crc2)
  {
    sw_log_error("[%s] %s received a request with invalid crc16(0x%02x != 0x%02x)!!", MODULE_NAME, log_prefix, crc1, crc2);
    goto ret_p3;
  }

  // 1.4, 解析处理该帧的请求
  nReqDataLen = nReqBytes-MIN_FRAME_LEN; // 数据区长度
  switch(req[2])
  {
  case 0x01: // MCU查询RTU的工作状态
    if(nReqDataLen != 1) { sw_log_error("[%s] %s received a request with invalid data length!!", MODULE_NAME, log_prefix); goto ret_p3; }

    int pwr_reason = (int)req[3];
    bool bTakePhoto = ((pwr_reason == 1 || pwr_reason == 2) ? true : false);
    if(bTakePhoto && !bTakenPhoto) { RTU_RequestTakePhoto()/*请求RTU进行拍照(异步执行)*/; bTakenPhoto = true/*只请求一次*/; }

    unsigned char tmpBuf1[10] = { 0x7D, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E };

    sw_rwlock_wrlock(pComIO->state_rwlock, WAIT_FOREVER);
    if(RTU_IsInetAvailable() == 1 && RTU_IsLoginMqServer() == 1) sw_chararry_setBit(pComIO->work_state, 4, 0, 0x01); // 联机
    else sw_chararry_setBit(pComIO->work_state, 4, 0, 0x00); // 脱机
    for(int i = 0; i < 4; i++) { tmpBuf1[3+i] = pComIO->work_state[3-i]; } // 小端转大端
    sw_rwlock_unlock(pComIO->state_rwlock);

    crc3 = CRC16(tmpBuf1+1, 6);
    memcpy(&tmpBuf1[7], &crc3, 2); // 小端字节序

    nSendBytes = sizeof(tmpBuf1); memcpy(sendBuf, tmpBuf1, nSendBytes);
    goto ret_p2;
  case 0x02: // 24小时温湿度光照数据, 每组14字节, 正常是24组
  case 0x05: // RTU单次的请求MCU, 发送一组当前实时的环境数据
    if(nReqDataLen <= 0 || (nReqDataLen%14) != 0 || (nReqDataLen/14) <= 0 || (nReqDataLen/14) > 24)
    { sw_log_error("[%s] %s received a request with invalid data length!!", MODULE_NAME, log_prefix); goto ret_p3; }

    int groupCnt = nReqDataLen/14;
    if(req[2] == 0x05 && groupCnt != 1)
    { sw_log_error("[%s] %s received a request with invalid data length!!", MODULE_NAME, log_prefix); goto ret_p3; }

    sw_rwlock_wrlock(pComIO->edata_rwlock, WAIT_FOREVER);
    for(int i = 0; i < groupCnt; i++)
    {
      memcpy(&pComIO->data[i].timestamp,   req+3 +i*14, 4); // 时间戳(秒, 存在"​​2038-01-19 03:14:07 UTC​​"溢出风险)
      swap_bytes((char *)&pComIO->data[i].timestamp,    4); // 大端转小端
      memcpy(&pComIO->data[i].temperature, req+7 +i*14, 2); // 温度, 单位: 0.1°C
      swap_bytes((char *)&pComIO->data[i].temperature,  2); // 大端转小端
      memcpy(&pComIO->data[i].humidity,    req+9 +i*14, 2); // 湿度, 单位: 0.1%RH
      swap_bytes((char *)&pComIO->data[i].humidity,     2); // 大端转小端
      memcpy(&pComIO->data[i].illuminance, req+11+i*14, 4); // 光照, 单位: lux
      swap_bytes((char *)&pComIO->data[i].illuminance,  4); // 大端转小端
      memcpy(&pComIO->data[i].voltage,     req+15+i*14, 2); // 电压, 单位: 0.1V
      swap_bytes((char *)&pComIO->data[i].voltage,      2); // 大端转小端
    }
    if(req[2] == 0x02) RTU_SendGrpEnvData(&pComIO->data[0], groupCnt); // 异步发送一组环境数据到RTU
    if(req[2] == 0x05) RTU_SendOneEnvData(&pComIO->data[0]); // 异步发送一条环境数据到RTU
    sw_rwlock_unlock(pComIO->edata_rwlock);

    if(req[2] == 0x05) goto ret_p3; // 应答帧不需要再回复

    unsigned char tmpBuf2[6] = { 0x7D, 0x01, 0x02, 0x81, 0xE1, 0x7E };
    nSendBytes = sizeof(tmpBuf2); memcpy(sendBuf, tmpBuf2, nSendBytes);
    goto ret_p2;
  case 0x03: // MCU请求RTU, 进行对时
    if(nReqDataLen != 4) { sw_log_error("[%s] %s received a request with invalid data length!!", MODULE_NAME, log_prefix); goto ret_p3; }

    unsigned char tmpBuf3[10] = { 0x7D, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E };
    if(RTU_IsSyncedNtpTime() == 1)
    { // 使用RTU时间同步返回
      uint32_t now = now_sec32();
      swap_bytes((char *)&now, 4); // 小端转大端
      memcpy(&tmpBuf3[3], &now, 4);
    }
    else
    { // 使用MCU时间原路返回
      tmpBuf3[3] = req[3]; tmpBuf3[4] = req[4]; tmpBuf3[5] = req[5]; tmpBuf3[6] = req[6];
      uint32_t now = ((uint32_t)req[3] << 24) | ((uint32_t)req[4] << 16) | ((uint32_t)req[5] << 8) | (uint32_t)req[6];
      char timestamp[MAX_LINE_CHARS]; sprintf(timestamp, "%u", now);
      if(xsetlocaltime(timestamp) == 0) sw_log_info("[%s] %s set local time to %s with MCU clock", MODULE_NAME, log_prefix, timestamp);
      else sw_log_warn("[%s] %s failed to set local time to %s with MCU clock!", MODULE_NAME, log_prefix, timestamp);
    }
    crc3 = CRC16(tmpBuf3+1, 6);
    memcpy(&tmpBuf3[7], &crc3, 2); // 小端字节序

    nSendBytes = sizeof(tmpBuf3); memcpy(sendBuf, tmpBuf3, nSendBytes);
    goto ret_p2;
  case 0x04: // MCU请求RTU, 同步参数
    if(nReqDataLen != 4) { sw_log_error("[%s] %s received a request with invalid data length!!", MODULE_NAME, log_prefix); goto ret_p3; }

    if(RTU_LoadMCUParamsCfg() != 0) goto ret_p3;

    unsigned char tmpBuf4[16] = { 0x7D, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E };
    tmpBuf4[3]  = cfg_params.version[3]; tmpBuf4[4] = cfg_params.version[2];
    tmpBuf4[5]  = cfg_params.version[1]; tmpBuf4[6] = cfg_params.version[0];
    tmpBuf4[7]  = cfg_params.ctrl_mode;       // 定时控制模式
    tmpBuf4[8]  = cfg_params.light_duration;  // 光控定时时长
    tmpBuf4[9]  = cfg_params.start_hour;      // 时控开始时间
    tmpBuf4[10] = cfg_params.end_hour;        // 时控结束时间
    tmpBuf4[11] = cfg_params.tp_intvl_min[1]; // 拍照时间间隔
    tmpBuf4[12] = cfg_params.tp_intvl_min[0]; // 拍照时间间隔
    crc3 = CRC16(tmpBuf4+1, 12);
    memcpy(&tmpBuf4[13], &crc3, 2); // 小端字节序

    nSendBytes = sizeof(tmpBuf4); memcpy(sendBuf, tmpBuf4, nSendBytes);
    goto ret_p2;
  case 0x06: // MCU通知RTU, 准备掉电
    if(nReqDataLen != 1) { sw_log_error("[%s] %s received a request with invalid data length!!", MODULE_NAME, log_prefix); goto ret_p3; }

    unsigned char tmpBuf6[10] = { 0x7D, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E };
    uint32_t work_state;

    sw_rwlock_rdlock(pComIO->state_rwlock, WAIT_FOREVER);
    memcpy(&work_state, pComIO->work_state, 4); // 保持小端序
    for(int i = 0; i < 4; i++) { tmpBuf6[3+i] = pComIO->work_state[3-i]; } // 小端转大端
    sw_rwlock_unlock(pComIO->state_rwlock);

    crc3 = CRC16(tmpBuf6+1, 6);
    memcpy(&tmpBuf6[7], &crc3, 2); // 小端字节序

    if(work_state == 0x00000000 || work_state == 0x00000001)
    { // 空闲状态, 通知RTU准备掉电, 非空闲状态时MCU应继续查询等待
      RTU_NotifyPwrWillOff();
      sw_log_warn("[%s] %s notified \"RTU\" to power off!", MODULE_NAME, log_prefix);
    }

    nSendBytes = sizeof(tmpBuf6); memcpy(sendBuf, tmpBuf6, nSendBytes);
    goto ret_p2;
  default:
    sw_log_warn("[%s] %s unknown a data req(type=0x%02x)!", MODULE_NAME, log_prefix, req[2]);
    goto ret_p3;
  }

  // 2, 回复该请求帧的应答包
ret_p2:
  ret = frame_doEscapeChars(sendBuf, nSendBytes, &rsp);
  if(ret < MIN_FRAME_LEN)
  {
    sw_log_error("[%s] %s failed to escape the response(%d)!!", MODULE_NAME, log_prefix, ret);
    goto ret_p3;
  }
  nRspBytes = ret;
  xstrfromhex(rsp, nRspBytes, log_rsp, NULL, NULL);
  ret = serial_send_data(pComIO->h, rsp, nRspBytes);
  if(ret == nRspBytes) sw_log_debug("[%s] %s sent a response(%d bytes)(hex): %s", MODULE_NAME, log_prefix, nRspBytes, log_rsp);
  else sw_log_error("[%s] %s failed to send a response(%d bytes, ret=%d)(hex): %s!!", MODULE_NAME, log_prefix, nRspBytes, ret, log_rsp);

  // 3, 清空接收缓存区(重置)
ret_p3:
  if(req) sw_heap_free(req);
  if(rsp) sw_heap_free(rsp);
  if(nRecvBytes > 0 && bPrintRecvBuf)
  {
    serial_printf_recv_buffer(pSerial, LEVEL_TRACE);
    sw_log_warn("[%s] %s discarded %d bytes of meaningless data!", MODULE_NAME, log_prefix, nRecvBytes);
  }
  if(nRecvBytes > 0) serial_clear_recv_buffer(pSerial);

  // 4, 持续累加的接收新数据
ret_p4:
  return 1;
}

// 打开与MCU控制板的通讯, 返回: 0成功, <0时失败
int MCBComInit()
{
  s_myCom.state_rwlock = sw_rwlock_create();
  s_myCom.edata_rwlock = sw_rwlock_create();
  if(!s_myCom.state_rwlock || !s_myCom.edata_rwlock)
  {
    sw_log_error("[%s] failed to create the rwlocks!!", MODULE_NAME);
    MCBComExit(); return -1;
  }

#ifdef _DEBUG // 上位机单元测试时使用
  const char *serialName = "/dev/ttyS0";   int baudrate = 115200;
#else
  const char *serialName = "/dev/ttymxc2"; int baudrate = 115200;
#endif
  const char *parityCheck = "none"; // 无校检
  s_myCom.h = serial_open(serialName, baudrate, parityCheck, \
                          comio_data_recv_proc, comio_data_recv_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 -2;
  }

  return 0;
}

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

// 配置单片机控制板-运行参数, 返回: 0成功, <0时失败
int MCBConfParameters(uint32_t version, uint8_t ctrl_mode, uint8_t light_duration, uint8_t start_hour, uint8_t end_hour, uint16_t tp_intvl_min)
{
  memcpy(cfg_params.version, &version, 4);
  cfg_params.ctrl_mode = ctrl_mode;
  cfg_params.light_duration = light_duration;
  cfg_params.start_hour = start_hour;
  cfg_params.end_hour = end_hour;
  memcpy(cfg_params.tp_intvl_min, &tp_intvl_min, 2);
  return 0;
}

// 设置工作状态位图-固件运维, 返回: 0成功, <0时失败
int MCBSetMNTStateBit(uint8_t state/*1bit位, 0x00:空闲, 0x01:运维中*/)
{
  if(!s_myCom.state_rwlock || (state != 0x00 && state != 0x01)) return -1;
  sw_rwlock_wrlock(s_myCom.state_rwlock, WAIT_FOREVER);
  if(state == 0x01) sw_chararry_setBit(s_myCom.work_state, 4, 1, 0x01); // 升级程序
  else sw_chararry_setBit(s_myCom.work_state, 4, 1, 0x00); // 正常工作
  sw_rwlock_unlock(s_myCom.state_rwlock);
  return 0;
}

// 设置工作状态位图-相机拍照, 返回: 0成功, <0时失败
int MCBSetCamStateBit(uint8_t state/*2bit位, 0x00:空闲, 0x01:拍照中, 0x10:上传中, 0x11:保留*/)
{
  if(!s_myCom.state_rwlock || (state != 0x00 && state != 0x01 && state != 0x10)) return -1;
  sw_rwlock_wrlock(s_myCom.state_rwlock, WAIT_FOREVER);
  switch(state)
  {
  case 0x00: // 空闲
    sw_chararry_setBit(s_myCom.work_state, 4, 2, 0x00);
    sw_chararry_setBit(s_myCom.work_state, 4, 3, 0x00);
    break;
  case 0x01: // 拍照中
    sw_chararry_setBit(s_myCom.work_state, 4, 2, 0x01);
    sw_chararry_setBit(s_myCom.work_state, 4, 3, 0x00);
    break;
  case 0x10: // 上传中
    sw_chararry_setBit(s_myCom.work_state, 4, 2, 0x00);
    sw_chararry_setBit(s_myCom.work_state, 4, 3, 0x01);
    break;
  }
  sw_rwlock_unlock(s_myCom.state_rwlock);
  return 0;
}

// 设置工作状态位图-环境数据, 返回: 0成功, <0时失败
int MCBSetEnvStateBit(uint8_t state/*2bit位, 0x00:空闲, 0x01:收集中, 0x10:上传中, 0x11:保留*/)
{
  if(!s_myCom.state_rwlock || (state != 0x00 && state != 0x01 && state != 0x10)) return -1;
  sw_rwlock_wrlock(s_myCom.state_rwlock, WAIT_FOREVER);
  switch(state)
  {
  case 0x00: // 空闲
    sw_chararry_setBit(s_myCom.work_state, 4, 4, 0x00);
    sw_chararry_setBit(s_myCom.work_state, 4, 5, 0x00);
    break;
  case 0x01: // 收集中
    sw_chararry_setBit(s_myCom.work_state, 4, 4, 0x01);
    sw_chararry_setBit(s_myCom.work_state, 4, 5, 0x00);
    break;
  case 0x10: // 上传中
    sw_chararry_setBit(s_myCom.work_state, 4, 4, 0x00);
    sw_chararry_setBit(s_myCom.work_state, 4, 5, 0x01);
    break;
  }
  sw_rwlock_unlock(s_myCom.state_rwlock);
  return 0;
}

// 请求获取当前实时-环境数据, 返回: 0成功, <0时失败
int MCBReqEnvCurData()
{
  if(!s_myCom.h) return -1;
  unsigned char req[6] = { 0x7D, 0x01, 0x05, 0xC0, 0x23, 0x7E };
  int ret = serial_send_data(s_myCom.h, req, sizeof(req));
  if(ret != sizeof(req)) return -2;
  sw_log_debug("[%s] sent a request(6 bytes)(hex): %s", MODULE_NAME, "7d0105c0237e");
  return 0;
}

#ifdef _DEBUG
void *MCBGetSerialHandle()
{
  return s_myCom.h;
}
#endif
