/*
 * Copyright (c) 2019-2021, Erich Styger
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "platform.h"
#if PL_CONFIG_USE_SHELL
#include "Shell.h"
#include "McuShell.h"
#include "McuRTOS.h"
#include "McuRTT.h"
#include "McuArmTools.h"
#if PL_CONFIG_USE_I2C_SPY
  #include "McuI2CSpy.h"
#endif
#if PL_CONFIG_USE_USB_CDC
  #include "virtual_com.h"
#endif
#if PL_CONFIG_USE_LINE_SENSOR
  #include "Reflectance.h"
#endif
#if PL_CONFIG_USE_BUZZER
  #include "Buzzer.h"
#endif
#if PL_CONFIG_USE_MOTORS
  #include "Motor.h"
#endif
#if PL_CONFIG_USE_QUADRATURE
  #include "QuadCounter.h"
#endif
#if PL_CONFIG_USE_ESP32
  #include "McuESP32.h"
#endif
#if PL_CONFIG_USE_PID
  #include "Pid.h"
#endif
#if PL_CONFIG_HAS_BATTERY_ADC
  #include "Battery.h"
#endif
#include "McuShellUart.h"
#if PL_CONFIG_APP_LINE_FOLLOWING || PL_CONFIG_APP_LINE_MAZE
  #include "LineFollow.h"
#endif
#include "Turn.h"
#if PL_CONFIG_APP_LINE_MAZE
  #include "Maze.h"
#endif
#include "Drive.h"
#include "Tacho.h"
#include "Identify.h"
#include "Application.h"
#include "Remote.h"
#if PL_CONFIG_HAS_ACCEL
  #include "McuFXOS8700.h"
#endif

static const McuShell_ParseCommandCallback CmdParserTable[] =
{
  McuShell_ParseCommand,
  McuRTOS_ParseCommand,
#if McuArmTools_CONFIG_PARSE_COMMAND_ENABLED
  McuArmTools_ParseCommand,
#endif
#if PL_CONFIG_USE_IDENTIFY
  ID_ParseCommand,
#endif
#if PL_CONFIG_USE_I2C_SPY
  McuI2CSpy_ParseCommand,
#endif
#if PL_CONFIG_USE_BUZZER
  BUZ_ParseCommand,
#endif
#if PL_CONFIG_USE_LINE_SENSOR
  REF_ParseCommand,
#endif
#if PL_CONFIG_USE_MOTORS
  MOT_ParseCommand,
#endif
#if PL_CONFIG_USE_QUADRATURE
  QuadCounter_ParseCommand,
#endif
#if PL_CONFIG_USE_TACHO
  TACHO_ParseCommand,
#endif
#if PL_CONFIG_USE_ESP32
  McuESP32_ParseCommand,
#endif
#if PL_CONFIG_USE_PID
  PID_ParseCommand,
#endif
#if PL_CONFIG_APP_LINE_FOLLOWING || PL_CONFIG_APP_LINE_MAZE
  LF_ParseCommand,
#endif
#if PL_HAS_TURN
  TURN_ParseCommand,
#endif
#if PL_CONFIG_USE_DRIVE
  DRV_ParseCommand,
#endif
#if PL_CONFIG_APP_LINE_MAZE
  MAZE_ParseCommand,
#endif
  APP_ParseCommand,
#if PL_CONFIG_HAS_BATTERY_ADC
  BATT_ParseCommand,
#endif
#if PL_CONFIG_USE_REMOTE
  REMOTE_ParseCommand,
#endif
#if PL_CONFIG_HAS_ACCEL
  McuFXOS8700_ParseCommand,
#endif
  NULL /* Sentinel */
};

#if PL_CONFIG_USE_ESP32
/* write output from the ESP to the shell too */
static void ESP_SendChar(unsigned char ch) {
#if 0
  /* Do NOT send to all shell I/O console. Because if the ESP32 has echo enabled, it will send back all characters, so they show up twice e.g. on RTT */
  SHELL_SendChar(ch);
#endif
#if PL_CONFIG_USE_REMOTE
  REMOTE_GatewayRxFromESP(ch); /* pass it to remote module for parsing */
#endif
}

static void ESP_ReadChar(uint8_t *c) {
  *c = '\0'; /* nothing received */
}

static bool ESP_CharPresent(void) {
  return false;
}

McuShell_ConstStdIOType ESP_ToShellStdio = {
    .stdIn = (McuShell_StdIO_In_FctType)ESP_ReadChar,
    .stdOut = (McuShell_StdIO_OutErr_FctType)ESP_SendChar,
    .stdErr = (McuShell_StdIO_OutErr_FctType)ESP_SendChar,
    .keyPressed = ESP_CharPresent, /* if input is not empty */
  #if McuShell_CONFIG_ECHO_ENABLED
    .echoEnabled = false,
  #endif
  };
#endif /* PL_CONFIG_USE_ESP32 */

typedef struct {
  McuShell_ConstStdIOType *stdio;
  unsigned char *buf;
  size_t bufSize;
} SHELL_IODesc;

static const SHELL_IODesc ios[] =
{
#if PL_CONFIG_USE_UART_SHELL
  {&McuShellUart_stdio,  McuShellUart_DefaultShellBuffer,  sizeof(McuShellUart_DefaultShellBuffer)},
#endif
#if PL_CONFIG_USE_RTT
  {&McuRTT_stdio,  McuRTT_DefaultShellBuffer,  sizeof(McuRTT_DefaultShellBuffer)},
#endif
#if PL_CONFIG_USE_USB_CDC_SHELL
  {&USB_CdcStdio,  USB_CdcDefaultShellBuffer,  sizeof(USB_CdcDefaultShellBuffer)},
#endif
};

#if 0
/* do not send to all I/Os. Problem is with ESP32 echo enabled:
 * everything typed in will end up twice for example in the RTT
 */
void SHELL_SendChar(unsigned char ch) {
  for(unsigned int i=0;i<sizeof(ios)/sizeof(ios[0]);i++) {
    McuShell_SendCh(ch, ios[i].stdio->stdOut);
  }
}
#endif

uint8_t SHELL_ParseCommand(unsigned char *cmd) {
  return McuShell_ParseWithCommandTable(cmd, McuShell_GetStdio(), CmdParserTable);
}

uint8_t SHELL_ParseCommandWithIO(unsigned char *cmd, McuShell_ConstStdIOType *io) {
  return McuShell_ParseWithCommandTable(cmd, io, CmdParserTable);
}

void SHELL_SendString(unsigned char *str) {
  for(unsigned int i=0;i<sizeof(ios)/sizeof(ios[0]);i++) {
    McuShell_SendStr(str, ios[i].stdio->stdOut);
  }
}

static void ShellTask(void *pv) {
  unsigned int i;

  (void)pv; /* not used */
  McuShell_SendStr((uint8_t*)"Shell task started.\r\n", McuShell_GetStdio()->stdOut);
  for(i=0;i<sizeof(ios)/sizeof(ios[0]);i++) {
    ios[i].buf[0] = '\0';
  }
  for(;;) {
    /* process all I/Os */
    for(i=0;i<sizeof(ios)/sizeof(ios[0]);i++) {
      (void)McuShell_ReadAndParseWithCommandTable(ios[i].buf, ios[i].bufSize, ios[i].stdio, CmdParserTable);
    }
    vTaskDelay(pdMS_TO_TICKS(20));
  }
}

void SHELL_Init(void) {
  if (xTaskCreate(
      ShellTask,  /* pointer to the task */
      "Shell", /* task name for kernel awareness debugging */
      800/sizeof(StackType_t), /* task stack size */
      (void*)NULL, /* optional task startup argument */
      tskIDLE_PRIORITY+2,  /* initial priority */
      (TaskHandle_t*)NULL /* optional task handle to create */
    ) != pdPASS)
  {
    for(;;){} /* error! probably out of memory */
  }
  McuShell_SetStdio(McuRTT_GetStdio()); /* use RTT as the default */
#if PL_CONFIG_USE_ESP32
  McuESP32_SetRxFromESPStdio(&ESP_ToShellStdio); /* copy ESP UART to shell console */
#endif
}

void SHELL_Deinit(void) {}

#endif /* PL_CONFIG_USE_SHELL */
