/* Reflow Oven Controller
 *
 * Copyright (C) 2020  Mario Hüttel <mario.huettel@gmx.net>
 *
 * This file is part of the Reflow Oven Controller Project.
 *
 * The reflow oven controller is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * The Reflow Oven Control Firmware is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with the reflow oven controller project.
 * If not, see <http://www.gnu.org/licenses/>.
 */

#include <stm32/stm32f4xx.h>
#include <cmsis/core_cm4.h>
#include <reflow-controller/shell.h>
#include <stm-periph/uart.h>
#include <string.h>
#include <reflow-controller/adc-meas.h>
#include <reflow-controller/digio.h>
#include <stdlib.h>
#include <malloc.h>
#include <helper-macros/helper-macros.h>
#include <reflow-controller/systick.h>
#include <stm-periph/unique-id.h>
#include <reflow-controller/calibration.h>
#include <reflow-controller/temp-converter.h>
#include <fatfs/ff.h>
#include <reflow-controller/safety/stack-check.h>
#include <reflow-controller/rotary-encoder.h>
#include <reflow-controller/safety/safety-controller.h>
#include <reflow-controller/settings/settings.h>
#include <reflow-controller/button.h>
#include <reflow-controller/safety/fault.h>
#include <reflow-controller/safety/safety-memory.h>
#include <reflow-controller/hw-version-detect.h>
#include <reflow-controller/temp-profile-executer.h>

#ifndef GIT_VER
#define GIT_VER "VERSION NOT SET"
#endif

extern struct stm_uart shell_uart;
static shellmatta_instance_t shell;
static char shell_buffer[512];
static char IN_SECTION(.ccm.bss) history_buffer[512];

static bool check_opt(const char *args, uint32_t len, const char *opt_to_check)
{
	(void)len;
	char str[128];
	const char *ptr;
	static const char * const tokens = "\t ";

	strncpy(str, args, sizeof(str));
	str[sizeof(str) - 1] = 0;

	/* Tokenize the string */
	ptr = strtok(str, tokens);

	while (ptr) {
		if (strcmp(ptr, opt_to_check) == 0)
			return true;
		ptr = strtok(NULL, tokens);
	}

	return false;
}

static shellmatta_retCode_t shell_cmd_ver(const shellmatta_handle_t   handle,
					  const char                  *arguments,
					  uint32_t                    length)
{
	(void)arguments;
	(void)length;
	uint32_t low_id;
	uint32_t mid_id;
	uint32_t high_id;
	const char *hw_rev_str;
	enum hw_revision pcb_rev;

	unique_id_get(&high_id, &mid_id, &low_id);

	shellmatta_printf(handle, "Reflow Oven Controller Firmware " xstr(GIT_VER) "\r\n"
										   "Compiled: " __DATE__ " at " __TIME__ "\r\n");
	shellmatta_printf(handle, "Serial: %08X-%08X-%08X\r\n", high_id, mid_id, low_id);

	pcb_rev = get_pcb_hardware_version();
	switch (pcb_rev) {
	case HW_REV_V1_2:
		hw_rev_str = "Hardware: v1.2";
		break;
	case HW_REV_V1_3:
		hw_rev_str = "Hardware: v1.3";
		break;
	default:
		hw_rev_str = "Hardware: Unknown Revision. You might have to update the firmware!";
		break;
	}
	shellmatta_printf(handle, "%s", hw_rev_str);

	return SHELLMATTA_OK;
}

static shellmatta_retCode_t shell_cmd_digio_get(const shellmatta_handle_t   handle,
						const char                  *arguments,
						uint32_t                    length)
{
	(void)arguments;
	(void)length;

	shellmatta_printf(handle,
			  "DIGIO0 DIGIO1 DIGIO2 DIGIO3 LED0 LED1 LS\r\n"
			  "%d      %d      %d      %d      %d    %d    %d\r\n",
			  digio_get(0), digio_get(1), digio_get(2), digio_get(3),
			  led_get(0), led_get(1), loudspeaker_get());

	return SHELLMATTA_OK;
}

static shellmatta_retCode_t shell_cmd_digio_set(const shellmatta_handle_t   handle,
						const char                  *arguments,
						uint32_t                    length)
{
	(void)length;
	(void)handle;
	int num = 100;
	int state;
	char buff[64];
	char *curr_token;

	strncpy(buff, arguments, sizeof(buff)-1);
	buff[63] = '\0';

	curr_token = strtok(buff, " ");
	curr_token = strtok(NULL, " ");
	if (!curr_token)
		return SHELLMATTA_ERROR;

	num = atoi(curr_token);

	if (!curr_token)
		return SHELLMATTA_ERROR;

	curr_token = strtok(NULL, " ");
	state = atoi(curr_token);

	if (num < 4 && num >= 0)
		digio_set(num, state);
	else if (num >= 4 && num <= 5)
		led_set(num - 4, state);
	else if (num == 6)
		loudspeaker_set(state);
	else
		return SHELLMATTA_ERROR;

	return SHELLMATTA_OK;
}

static shellmatta_retCode_t shell_cmd_pt1000_res(const shellmatta_handle_t   handle,
						 const char                  *arguments,
						 uint32_t length)
{
	(void)arguments;
	(void)length;
	float resistance;
	int pt1000_status;
	char display_status[100];
	float temp;
	int temp_conv_status;
	const char *temp_prefix;

	display_status[0] = 0;

	pt1000_status = adc_pt1000_get_current_resistance(&resistance);

	if (pt1000_status == 2) {
		strcat(display_status, " UNSTABLE ");
	} else if (pt1000_status) {
		strcpy(display_status, "ERROR");
	} else {
		strcpy(display_status, "VALID");
	}

	temp_conv_status = temp_converter_convert_resistance_to_temp(resistance, &temp);
	switch (temp_conv_status) {
	case 1:
		temp_prefix = ">";
		break;
	case -1:
		temp_prefix = "<";
		break;
	case 0:
		/* FALLTHRU */
	default:
		temp_prefix = "";
		break;
	}

	shellmatta_printf(handle, "PT1000 resistance: %.2f Ohm (%s%.1f °C) [%s]\r\n", resistance, temp_prefix,temp, display_status);

	return SHELLMATTA_OK;
}

static shellmatta_retCode_t shell_cmd_uptime(const shellmatta_handle_t   handle,
					     const char                  *arguments,
					     uint32_t length)
{
	(void)arguments;
	(void)length;
	uint32_t days;
	uint32_t hours;
	uint32_t mins;
	uint32_t secs;

	systick_get_uptime_from_tick(&days, &hours, &mins, &secs);

	shellmatta_printf(handle, "Uptime: %u day%s %02u:%02u:%02u",
			  days, (days == 1 ? "" : "s"),
			  hours,
			  mins,
			  secs);
	return SHELLMATTA_OK;
}

static shellmatta_retCode_t shell_cmd_cal(const shellmatta_handle_t   handle,
					  const char                  *arguments,
					  uint32_t length)
{
	(void)arguments;
	(void)length;

	return calibration_sequence_shell_cmd(handle, arguments, length);
}

static shellmatta_retCode_t shell_meminfo(const shellmatta_handle_t   handle,
					  const char                  *arguments,
					  uint32_t length)
{
	(void)arguments;
	(void)length;
	struct mallinfo mi;

	shellmatta_printf(handle,
			  "Stack pointer: %p\r\n"
			  "Stack usage: 0x%x bytes\r\n"
			  "Lim to heap: 0x%x bytes\r\n",
			  read_stack_pointer(),
			  stack_check_get_usage(),
			  stack_check_get_free());

	mi = mallinfo();
	shellmatta_printf(handle, "\r\nDynamic Memory Management\r\n");
	shellmatta_printf(handle, "Allocated bytes: %d\r\n", mi.arena, mi.arena);
	shellmatta_printf(handle, "Number of free chunks: %d\r\n", mi.ordblks);
	shellmatta_printf(handle, "Top-most, releasable space: %d\r\n", mi.keepcost);
	shellmatta_printf(handle, "Total free space: %d\r\n", mi.fordblks);
	shellmatta_printf(handle, "Total allocated space: %d\r\n", mi.uordblks);

	return SHELLMATTA_OK;
}

static shellmatta_retCode_t shell_cmd_rot(const shellmatta_handle_t   handle,
					  const char *arguments,
					  uint32_t length)
{
	(void)arguments;
	(void)length;
	uint32_t rot_val;

	rot_val = rotary_encoder_get_abs_val();
	//delta = rotary_encoder_get_change_val();
	shellmatta_printf(handle, "Rotary encoder value: %u\r\n", rot_val);

	return SHELLMATTA_OK;
}

static shellmatta_retCode_t shell_cmd_pt1000_res_loop(const shellmatta_handle_t handle, const char *arguments,
						      uint32_t length)
{
	(void)arguments;
	(void)length;
	static uint64_t timestamp = 0ULL;

	if (systick_ticks_have_passed(timestamp, 150)) {
		shell_cmd_pt1000_res(handle, "", 0UL);
		timestamp = systick_get_global_tick();
	}

	return SHELLMATTA_CONTINUE;
}

static shellmatta_retCode_t shell_cmd_ls(const shellmatta_handle_t handle, const char *arguments,
					 uint32_t length)
{
	(void)length;
	(void)arguments;
	DIR dir;
	FRESULT res;
	FILINFO fno;

	res = f_opendir(&dir, "/");
	if (res != FR_OK) {
		shellmatta_printf(handle, "Filesystem inaccessible. Is an SD Card inserted?\r\n");
		return SHELLMATTA_OK;
	}

	while (f_readdir(&dir, &fno) == FR_OK) {
		if (fno.fname[0] == 0)
			break;
		shellmatta_printf(handle, "%c\t%s\r\n", (fno.fattrib & AM_DIR ? 'd' : 'f'), fno.fname);
	}

	f_closedir(&dir);

	return SHELLMATTA_OK;
}

static shellmatta_retCode_t shell_cmd_reset(const shellmatta_handle_t handle, const char *arguments,
					    uint32_t length)
{
	(void)handle;
	(void)length;
	(void)arguments;

	NVIC_SystemReset();

	return SHELLMATTA_BUSY;
}

static shellmatta_retCode_t shell_cmd_read_flags(const shellmatta_handle_t handle, const char *arguments,
						 uint32_t length)
{
	(void)length;
	(void)arguments;
	uint32_t count;
	uint32_t i;
	char name[64];
	bool flag;
	bool tryack;
	int status;
	enum safety_flag flag_enum;
	struct analog_monitor_info amon_info;
	struct timing_monitor_info timing_info;

	/* Check for the --ack option */
	tryack = check_opt(arguments, length, "--ack");

	shellmatta_printf(handle, "Error Flags\r\n"
				  "-----------\r\n");

	count = safety_controller_get_flag_count();
	for (i = 0; i < count; i++) {
		status = safety_controller_get_flag_name_by_index(i, name, sizeof(name));
		if (status) {
			shellmatta_printf(handle, "Error getting flag name %lu\r\n", i);
			continue;
		}

		status = safety_controller_get_flag_by_index(i, &flag, &flag_enum);
		if (status) {
			shellmatta_printf(handle, "Error getting flag value %lu\r\n", i);
			continue;
		}

		if (tryack)
			safety_controller_ack_flag(flag_enum);

		shellmatta_printf(handle, "\t%2lu) %-20s\t[%s]\r\n", i+1, name, (flag ? "\e[1;31mERR\e[m" : "\e[32mOK\e[m"));
	}

	shellmatta_printf(handle, "\r\nAnalog Monitors\r\n"
				  "---------------\r\n");
	count = safety_controller_get_analog_monitor_count();

	for (i = 0; i < count; i++) {
		status = safety_controller_get_analog_mon_name_by_index(i, name, sizeof(name));
		if (status) {
			shellmatta_printf(handle, "Error getting analog value monitor %lu name\r\n", i);
			continue;
		}

		status = safety_controller_get_analog_mon_by_index(i, &amon_info);
		if (status) {
			shellmatta_printf(handle, "Error reading analog monitor status %lu\r\n", i);
			continue;
		}

		shellmatta_printf(handle, "\t%2lu) %-20s\t[%s%8.2f%s]", i+1, name,
				  amon_info.status == ANALOG_MONITOR_OK ? "\e[32m" : "\e[1;31m",
				  amon_info.value,
				  "\e[m");
		if (amon_info.status == ANALOG_MONITOR_INACTIVE) {
			shellmatta_printf(handle, "Inactive\r\n");
		} else {
			shellmatta_printf(handle, " valid from %-8.2f to %-8.2f", amon_info.min, amon_info.max);
			shellmatta_printf(handle, "\tchecked %llu ms ago\r\n",
					  systick_get_global_tick() - amon_info.timestamp);
		}
	}

	shellmatta_printf(handle, "\r\nTiming Monitors\r\n"
				  "---------------\r\n");


	count = safety_controller_get_timing_monitor_count();
	for (i = 0; i < count; i++) {
		(void)safety_controller_get_timing_mon_by_index(i, &timing_info);
		(void)safety_controller_get_timing_mon_name_by_index(i, name, sizeof(name));

		shellmatta_printf(handle, "\t%2lu) %-20s\t", i+1, name);
		if (timing_info.enabled)
			shellmatta_printf(handle, "last tick: %lu ms\r\n", (unsigned long int)timing_info.delta);
		else
			shellmatta_printf(handle, "\e[1;31mDISABLED\e[m\r\n");
	}

	return SHELLMATTA_OK;
}

static shellmatta_retCode_t shell_cmd_save_cal(const shellmatta_handle_t handle, const char *arguments,
					       uint32_t length)

{
	(void)length;
	(void)arguments;
	int res;
	float offset, sens_dev;
	bool active;

	adc_pt1000_get_resistance_calibration(&offset, &sens_dev, &active);
	res = settings_save_calibration(sens_dev, offset, active);

	if (res) {
		shellmatta_printf(handle, "Error saving %d\r\n", res);
	} else {
		shellmatta_printf(handle, "Saved!\r\n");
	}

	return SHELLMATTA_OK;
}

static shellmatta_retCode_t shell_cmd_hang(const shellmatta_handle_t handle, const char *arguments,
					   uint32_t length)
{
	(void)handle;
	(void)arguments;
	(void)length;

	while (1337);

	return SHELLMATTA_OK;
}

static shellmatta_retCode_t shell_cmd_ui_emulation(const shellmatta_handle_t handle, const char *arguments,
						   uint32_t length)
{
	(void)length;
	(void)arguments;
	uint32_t i;
	uint32_t len;
	char *buff;

	shellmatta_read(handle, &buff, &len);

	for (i = 0; i < len; i++) {
		switch (buff[i]) {
		case 'W':
		case 'w':
			rotary_encoder_override_delta(4);
			break;
		case 's':
		case 'S':
			rotary_encoder_override_delta(-4);
			break;
		case '\r':
			button_override_event(BUTTON_SHORT_RELEASED);
			break;
		case 'l':
		case 'L':
			button_override_event(BUTTON_LONG);
			break;
		case 'k':
		case 'K':
			button_override_event(BUTTON_SHORT);
			break;
		case 'r':
		case 'R':
			button_override_event(BUTTON_LONG_RELEASED);
			break;
		}
	}

	return SHELLMATTA_CONTINUE;
}

static shellmatta_retCode_t shell_cmd_panic(const shellmatta_handle_t handle, const char *arguments,
					    uint32_t length)

{
	(void)handle;
	(void)arguments;
	(void)length;

	panic_mode();

	return SHELLMATTA_OK;
}

static char *get_safety_mem_dump(size_t *used_bytes)
{
	char *buffer;
	int res;

	buffer = (char *)malloc(5470);

	res = safety_memory_dump_base64(buffer, 5470UL, used_bytes);
	if (res) {
		if (buffer)
			free(buffer);
		buffer = NULL;
	}

	return buffer;
}

static shellmatta_retCode_t shell_cmd_dump_safety_mem(const shellmatta_handle_t handle, const char *arguments,
						      uint32_t length)
{
	static char *buffer;
	static const char *ptr;
	size_t used_bytes;
	static size_t full_lines = 0;
	static size_t current_line;
	size_t remainder;
	static const char *hline = "----------------------------------------------------------------";
	char string[200];
	const char *token;
	const char * const token_delim = "\t ";
	FRESULT fres;
	FIL file;
	UINT bw;

	/* Check if the dump shall be stored to disk */
	strncpy(string, arguments, MIN(sizeof(string), length+1));
	string[sizeof(string) - 1] = '\0';

	token = strtok(string, token_delim);
	token = strtok(NULL, token_delim);

	if (token) {
		buffer = get_safety_mem_dump(&used_bytes);
		if (!buffer) {
			shellmatta_printf(handle, "Error generating dump");
			return SHELLMATTA_OK;
		}
		fres = f_open(&file, token, FA_CREATE_NEW | FA_WRITE);
		if (fres == FR_EXIST) {
			free(buffer);
			shellmatta_printf(handle, "File already esists!\r\n");
			return SHELLMATTA_OK;
		} else if (fres != FR_OK) {
			free(buffer);
			shellmatta_printf(handle, "Error opening file %s\r\n", token);
			return SHELLMATTA_OK;
		}

		fres = f_write(&file, buffer, used_bytes - 1, &bw);
		if (fres != FR_OK) {
			shellmatta_printf(handle, "Error writing to file %s\r\n", token);
		}
		free(buffer);
		f_close(&file);
		return SHELLMATTA_OK;
	}

	if (full_lines == 0) {
		shellmatta_printf(handle, "Safety memory content\r\n%s\r\n", hline);

		buffer = get_safety_mem_dump(&used_bytes);
		if (!buffer) {
			shellmatta_printf(handle, "Error dumping memory!\r\n");
			return SHELLMATTA_OK;
		}

		full_lines = (used_bytes - 1) / 64;
		remainder = (used_bytes - 1) % 64;
		if (remainder)
			full_lines++;

		ptr = buffer;
		current_line = 0;
		return SHELLMATTA_BUSY;
	} else {
		if (current_line < full_lines) {
			shellmatta_printf(handle, "%.64s\r\n", ptr);
			ptr += 64;
			current_line++;
		} else {
			shellmatta_printf(handle, "%s\r\n", hline);
			full_lines = 0;
			if (buffer)
				free(buffer);
			buffer = NULL;
			return SHELLMATTA_OK;
		}
	}
	return SHELLMATTA_BUSY;
}

shellmatta_retCode_t shell_cmd_reset_cal(const shellmatta_handle_t handle, const char *arguments, uint32_t length)
{
	(void)handle;
	(void)arguments;
	(void)length;

	adc_pt1000_set_resistance_calibration(0.0f, 0.0f, false);

	return SHELLMATTA_OK;
}

shellmatta_retCode_t shell_cmd_update(const shellmatta_handle_t handle, const char *arguments, uint32_t length)
{
	(void)handle;
	(void)arguments;
	(void)length;
	struct safety_memory_boot_status status;

	safety_memory_get_boot_status(&status);
	status.reboot_to_bootloader = 0xFFFFFFFFUL;
	safety_memory_set_boot_status(&status);

	NVIC_SystemReset();

	return SHELLMATTA_OK;
}

shellmatta_retCode_t shell_cmd_overtemp_cfg(const shellmatta_handle_t handle, const char *args, uint32_t len)
{
	float overtemp_lim;
	shellmatta_retCode_t ret;
	char *argument;
	uint32_t arg_length;
	char option;
	bool temp_passed = false;
	bool persistent = false;
	(void)args;
	(void)len;

	static const shellmatta_opt_long_t options[] = {
		{"persistent", 'p', SHELLMATTA_OPT_ARG_NONE},
		{NULL, '\0', SHELLMATTA_OPT_ARG_NONE},
	};

	do {
		ret = shellmatta_opt_long(handle, options, &option, &argument, &arg_length);
		if (ret != SHELLMATTA_OK)
			break;
		switch (option) {
		case 'p':
			persistent = true;
			break;
		case '\0':
			temp_passed = true;
			overtemp_lim = strtof(argument, NULL);
			break;
		}
	} while (1);

	if (temp_passed) {
		safety_controller_set_overtemp_limit(overtemp_lim);
		if (persistent)
			settings_save_overtemp_limit(overtemp_lim, true);
	}

	overtemp_lim = safety_controller_get_overtemp_limit();
	shellmatta_printf(handle, "Overtemperature configured for: %.1f °C\r\n", overtemp_lim);

	return ret;
}

shellmatta_retCode_t shell_cmd_execute(const shellmatta_handle_t handle, const char *args, uint32_t len)
{
	enum pl_ret_val res;
	const struct tpe_current_state *state;
	static bool running = false;
	char *data;
	uint32_t dlen;
	(void)args;
	(void)len;

	shellmatta_read(handle, &data, &dlen);

	if (!running) {
		res = temp_profile_executer_start("profile.tpr");

		if (res != PL_RET_SUCCESS) {
			switch (res) {
			case PL_RET_DISK_ERR:
				shellmatta_printf(handle, "Error reading file\r\n");
				break;
			case PL_RET_LIST_FULL:
				shellmatta_printf(handle, "Script too long!\r\n");
				break;
			case PL_RET_SCRIPT_ERR:
				shellmatta_printf(handle, "Error in script\r\n");
				break;
			default:
				shellmatta_printf(handle, "Unspecified error occured\r\n");
				break;
			}

			return SHELLMATTA_ERROR;
		}
		running = true;
	} else {
		state = temp_profile_executer_status();
		if (state->status != TPE_RUNNING) {
			shellmatta_printf(handle, "Profile executed.\r\n");
			running = false;
			if(state->status == TPE_ABORT) {
				shellmatta_printf(handle, "Profile execution aborted!\r\n");
			}
			return SHELLMATTA_OK;
		}

		if (dlen > 0 && *data == '\x03') {
			temp_profile_executer_stop();
			return SHELLMATTA_OK;
		}
	}

	return SHELLMATTA_CONTINUE;
}

//typedef struct shellmatta_cmd
//{
//    char                    *cmd;       /**< command name                           */
//    char                    *cmdAlias;  /**< command alias                          */
//    char                    *helpText;  /**< help text to print in "help" command   */
//    char                    *usageText; /**< usage text to print on parameter error */
//    shellmatta_cmdFct_t     cmdFct;     /**< pointer to the cmd callack function    */
//    struct shellmatta_cmd   *next;      /**< pointer to next command or NULL        */
//} shellmatta_cmd_t;
static shellmatta_cmd_t cmd[21] = {
	{
		.cmd = "version",
		.cmdAlias = "ver",
		.helpText = "Print firmware version",
		.usageText = NULL,
		.cmdFct = shell_cmd_ver,
		.next = &cmd[1],
	},
	{
		.cmd = "pt1000",
		.cmdAlias = "pt",
		.helpText = "Get current filtered and calibrated PT1000 resistance",
		.usageText = NULL,
		.cmdFct = shell_cmd_pt1000_res,
		.next = &cmd[2],
	},
	{
		.cmd = "pt1000-dump",
		.cmdAlias = "ptdump",
		.helpText = "Get current filtered and calibrated PT1000 resistance in a loop",
		.usageText = "pt1000-dump",
		.cmdFct = shell_cmd_pt1000_res_loop,
		.next = &cmd[3],
	},
	{
		.cmd = "digio-get",
		.cmdAlias = "dg",
		.helpText = "Read all digital input/output ports",
		.usageText = NULL,
		.cmdFct = shell_cmd_digio_get,
		.next = &cmd[4],
	},
	{
		.cmd = "digio-set",
		.cmdAlias = "ds",
		.helpText = "Set DIGIO Port",
		.usageText = "digio-set <num> <state>",
		.cmdFct = shell_cmd_digio_set,
		.next = &cmd[5],
	},
	{
		.cmd = "uptime",
		.cmdAlias = "upt",
		.helpText = "Get uptime in seconds",
		.usageText = "",
		.cmdFct = shell_cmd_uptime,
		.next = &cmd[6],
	},
	{
		.cmd = "calibrate",
		.cmdAlias = "cal",
		.helpText = "Calibrate resistance measurement",
		.usageText = "",
		.cmdFct = shell_cmd_cal,
		.next = &cmd[7],
	},
	{
		.cmd = "meminfo",
		.cmdAlias = NULL,
		.helpText = "Get information about memory usage",
		.usageText = "",
		.cmdFct = shell_meminfo,
		.next = &cmd[8],
	},
	{
		.cmd = "rotary-encoder",
		.cmdAlias = "rot",
		.helpText = "Get current rotary encoder value",
		.usageText = "",
		.cmdFct = shell_cmd_rot,
		.next = &cmd[9],
	},
	{
		.cmd = "ls",
		.cmdAlias = NULL,
		.helpText = "List filesystem contents",
		.usageText = "",
		.cmdFct = shell_cmd_ls,
		.next = &cmd[10],
	},
	{
		.cmd = "reset",
		.cmdAlias = NULL,
		.helpText = "Reset controller",
		.usageText = "reset",
		.cmdFct = shell_cmd_reset,
		.next = &cmd[11],
	},
	{
		.cmd = "safety-flags",
		.cmdAlias = "flags",
		.helpText = "Reads and may clear safety flags",
		.usageText = "flags [--ack]",
		.cmdFct = shell_cmd_read_flags,
		.next = &cmd[12],
	},
	{
		.cmd = "save-calibration",
		.cmdAlias = "save-cal",
		.helpText = "",
		.usageText = "",
		.cmdFct = shell_cmd_save_cal,
		.next = &cmd[13],
	},
	{
		.cmd = "hang",
		.cmdAlias = NULL,
		.helpText = "",
		.usageText = "",
		.cmdFct = shell_cmd_hang,
		.next = &cmd[14],
	},
	{
		.cmd = "ui-emulate",
		.cmdAlias = NULL,
		.helpText = "",
		.usageText = "",
		.cmdFct = shell_cmd_ui_emulation,
		.next = &cmd[15],
	},
	{
		.cmd = "panic",
		.cmdAlias = NULL,
		.helpText = "Panic Mode!",
		.usageText = "",
		.cmdFct = shell_cmd_panic,
		.next = &cmd[16],
	},
	{
		.cmd = "safety-mem-dump",
		.cmdAlias = NULL,
		.helpText = "",
		.usageText = "safety-mem-dump [output-file]",
		.cmdFct = shell_cmd_dump_safety_mem,
		.next = &cmd[17],
	},
	{
		.cmd = "reset-cal",
		.cmdAlias = NULL,
		.helpText = "Reset Calibration",
		.usageText = "",
		.cmdFct = shell_cmd_reset_cal,
		.next = &cmd[18],
	},
	{
		.cmd = "update",
		.cmdAlias = NULL,
		.helpText = "Update Firmware",
		.usageText = "",
		.cmdFct = shell_cmd_update,
		.next = &cmd[19],

	},
	{
		.cmd = "overtemp",
		.cmdAlias = NULL,
		.helpText = "Overtemperature Config",
		.usageText = "",
		.cmdFct = shell_cmd_overtemp_cfg,
		.next = &cmd[20],
	},
	{
		.cmd = "execute",
		.cmdAlias = NULL,
		.helpText = "Execute Temp Profile",
		.usageText = "",
		.cmdFct = shell_cmd_execute,
		.next = NULL,
	}
};

shellmatta_handle_t shell_init(shellmatta_write_t write_func)
{
	shellmatta_handle_t handle;
	shellmatta_retCode_t ret;

	ret = shellmatta_doInit(&shell, &handle, shell_buffer, sizeof(shell_buffer), history_buffer, sizeof(history_buffer),
				"\e[1;32mReflow Controller>\e[m ", cmd, write_func);
	if (ret != SHELLMATTA_OK)
		handle = NULL;

	return handle;
}

void shell_print_motd(shellmatta_handle_t shell)
{
	/* Clear display and set cursor to home position */
	shellmatta_printf(shell, "\e[2J\e[H");
	shellmatta_printf(shell, "Shimatta Reflow Controller ready\r\n\r\n");
	shell_cmd_ver(shell, NULL, 0UL);
	shell_handle_input(shell, "\r\n", 2UL);
}

void shell_handle_input(shellmatta_handle_t shell, const char *data, size_t len)
{
	if (!shell)
		return;

	shellmatta_processData(shell, (char *)data, (uint32_t)len);
}

void shell_print_string(shellmatta_handle_t shell, const char *string)
{
	if (!shell)
		return;

	shellmatta_write(shell, (char *)string, strlen(string));
}
