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

#include "platform.h"
#include "Shell.h"
#include "McuShell.h"
#include "McuRTT.h"
#include "McuArmTools.h"
#include "McuShellUart.h"
#include "McuLog.h"
#include "McuUtility.h"

#define SHELL_CMD_ELEM_SIZE	(sizeof(Shell_cmd_s))

static QueueHandle_t shellCmdQueue;

static uint8_t splitFlapId = 0;

static void vQueueShellCmd(Shell_cmd_s shellCmd){
	if(xQueueSendToBack(shellCmdQueue, &shellCmd, pdMS_TO_TICKS(100)) != pdPASS){
		McuLog_warn("Failed adding shell cmd to queue, maybe full?");
	}
}

static uint8_t ParseCommand(const uint8_t *cmd, bool *handled, McuShell_ConstStdIOType *io){
	uint8_t size = sizeof(SHELL_CMD_IDENTIFY_SF)-1;
	if (McuUtility_strcmp((char*)cmd, McuShell_CMD_HELP)==0) {
	    McuShell_SendStr((unsigned char*)"\r\n", io->stdOut);
	    McuShell_SendStr((unsigned char*)McuShell_DASH_LINE, io->stdOut);
	    McuShell_SendStr((unsigned char*)"\r\n", io->stdOut);
	    McuShell_SendHelpStr((unsigned char*)"SplitFlap Help", (const unsigned char*)"Group of SplitFlap commands\r\n", io->stdOut);
	    McuShell_SendHelpStr((unsigned char*)"  help", (const unsigned char*)"Print help this message\r\n", io->stdOut);
	    McuShell_SendHelpStr((unsigned char*)SHELL_CMD_IDENTIFY_SF, (const unsigned char*)"<SplitFlapId> (uint8_t)\r\n", io->stdOut);
	    McuShell_SendHelpStr((unsigned char*)" ", (const unsigned char*)"the number printed on the splitflap\r\n", io->stdOut);
	    McuShell_SendHelpStr((unsigned char*)" ", (const unsigned char*)"Splitflap Number:16\r\n", io->stdOut);
	    McuShell_SendHelpStr((unsigned char*)SHELL_CMD_POWEROFF_RPI, (const unsigned char*)"power of the raspberry pi\r\n", io->stdOut);
	    *handled = TRUE;
	    return ERR_OK;
	  } else if(McuUtility_strncmp((char*)cmd, SHELL_CMD_IDENTIFY_SF, size) == 0){
		*handled = TRUE;
		cmd += sizeof(SHELL_CMD_IDENTIFY_SF)-1;
		if(McuUtility_ScanDecimal8uNumber(&cmd, &splitFlapId) != ERR_OK){
			// Do not save id
			return ERR_FAILED;
		}
		Shell_cmd_s shellCmd = {.shellCmd = Shell_Identify_SF, .value = (uint32_t)splitFlapId};
		vQueueShellCmd(shellCmd);
	  } else if(McuUtility_strcmp((char*)cmd,SHELL_CMD_POWEROFF_RPI) == 0){
		  // shutown rpi
		  Shell_cmd_s shellCmd = {.shellCmd = Shell_Powerof_rpi, .value = 0};
		  vQueueShellCmd(shellCmd);
	  }
	return ERR_OK;

}

static const McuShell_ParseCommandCallback CmdParserTable[] =
{
  ParseCommand,
  McuShell_ParseCommand,
  McuRTOS_ParseCommand,
  NULL /* Sentinel */
};

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

static const SHELL_IODesc ios[] =
{
  {&McuShellUart_stdio,  McuShellUart_DefaultShellBuffer,  sizeof(McuShellUart_DefaultShellBuffer)},
  {&McuRTT_stdio,  McuRTT_DefaultShellBuffer,  sizeof(McuRTT_DefaultShellBuffer)},
//  {&USB_CdcStdio,  USB_CdcDefaultShellBuffer,  sizeof(USB_CdcDefaultShellBuffer)},
};

QueueHandle_t SHELL_GetShellCmdQueueHandle() {
	return shellCmdQueue;
}

void SHELL_SendChar(unsigned char ch) {
  for(int i=0;i<sizeof(ios)/sizeof(ios[0]);i++) {
    McuShell_SendCh(ch, ios[i].stdio->stdOut);
  }
}

uint8_t SHELL_ParseCommandIO(const unsigned char *command, McuShell_ConstStdIOType *io, bool silent) {
  if (io==NULL) { /* use a default */
    io = McuShell_GetStdio();
  }
  return McuShell_ParseWithCommandTableExt(command, io, CmdParserTable, silent);
}

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

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

  McuLog_info("Shell task started");
  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)
  {
    McuLog_fatal("failed creating Shell task");
    for(;;){} /* error! probably out of memory */
  }
//  McuShell_SetStdio(McuRTT_GetStdio()); /* use RTT as the default */
  if(McuShell_SetStdio(&McuShellUart_stdio) != ERR_OK){
	  // couldnt set uart as stdio, try set RTT
	  McuLog_warn("Failed to choose UART for shell, try RTT");
	  if(McuShell_SetStdio(McuRTT_GetStdio())!= ERR_OK){ /* use RTT as the default */
		  //couldnt set rtt!!
		  McuLog_fatal("failed to set rtt as default for shell");
		     for(;;){}
	  }else{
		  McuLog_info("RTT set for shell");
	  }
  }else{
	  McuLog_info("UART set for shell");
  }
  shellCmdQueue = xQueueCreate(SHELL_CMD_QUEUE_LENGTH,SHELL_CMD_ELEM_SIZE);
  if(shellCmdQueue == NULL){
	  McuLog_fatal("failed to create shell cmd queue!");
	  for(;;){}
  }
  vQueueAddToRegistry(shellCmdQueue, "Shell CMD Queue");
}

void SHELL_Deinit(void) {}

