/*
 * shell.c
 *
 *  Created on: Aug 1, 2016
 *      Author: tkl
 */

#include <stddef.h>
#include <stdbool.h>
#include <string.h>

#include "stack.h"
#include "queue.h"
#include "driver.h"
#include "kernel.h"
#include "list.h"
#include "shell_data.h"
#include "shell.h"
#include "shell_commands.h"


#define TH_STACK_SIZE  256
stack_t th_stack[TH_STACK_SIZE];
struct thread_context th_ctx;

static void parse(const char *buffer, unsigned int len)
{
	if(NULL == buffer)
		return;
	struct list_node *it = shell_object.command_list.front;
	while(it != NULL) {
		struct command *cmd = (struct command *)it->data;
		if(strstr(buffer, cmd->command)) {
			cmd->command_callback(buffer);
			return;
		}
		it = it->next;
	}
}

static void rx_func(void *arg)
{
	char buffer[81];
	unsigned int index = 0;
	int ret = 0;
	drv_open(shell_object.shell_device);
	while(1) {
		ret = drv_read(shell_object.shell_device, &buffer[index],
				sizeof(buffer) / sizeof(buffer[0]) - index - 1);
		if(ret) {
			if(shell_object.echo_on) {
				if((buffer[index + ret - 1] == '\n') && (buffer[index + ret -2] != '\r')) {
					drv_write(shell_object.shell_device, "\r\n", 2);
				}
				else if((buffer[index + ret - 1] == '\r') && (buffer[index + ret -2] != '\n')) {
					drv_write(shell_object.shell_device, "\r\n", 2);
				}
				else {
					drv_write(shell_object.shell_device, &buffer[index], ret);	// echo
				}
			}
			if((buffer[index + ret - 1] == '\n') || (buffer[index + ret - 1] == '\r')) {
				buffer[index + ret - 1] = '\n';
				parse(buffer, index + ret);
				buffer[index + ret] = '\0';
				index = 0;
			}
			else
				index += ret;
		}
	}
}

int shell_init(const struct driver *shell_device)
{
	if(NULL == shell_device)
		return -1;
	list_init(&shell_object.command_list);
	shell_object.shell_device = shell_device;
	shell_object.echo_on = true;

	shell_add_command(&cmd_kosmos_version);
	shell_add_command(&cmd_list_all_commands);
	shell_add_command(&cmd_echo_on);
	shell_add_command(&cmd_echo_off);

	thread_create(&th_ctx, th_stack, TH_STACK_SIZE, rx_func, NULL, THREAD_PRIO_LOW);
	return 0;
}

int shell_add_command(struct command *command)
{
	if(NULL == command)
		return -1;
	command->item.data = (unsigned int) command;
	list_add(&shell_object.command_list, &command->item);
	return 1;
}
