// shell.c


#include <912d60.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "kernel.h"
#include "semlib.h"




// FUNCTION PROTOTYPES
void shellcmd_help(char *argv1);
void shellcmd_tcb(void);
void shellcmd_rcb(void);
void shellcmd_ticks(void);
void shellcmd_start_task(unsigned char id);

/*void shellcmd_stop_task(unsigned char id);
void shellcmd_set_task_priority(unsigned char id, unsigned char priority);
void shellcmd_quit_shell(void);*/




// GLOBAL VARIABLES
extern unsigned long int system_tick;
extern unsigned int current;		   	 	// current task id number


// LOCAL GLOBAL VARIABLES (context is this file only)
extern char error_msg[6][25];


void shell(void) {
	
	// LOCAL CONSTANTS
	#define cmd_size (32+1)

	
	
	// LOCAL ENUMERATIONS
	enum command_type {	help = 1,
		 			  	tcb, 
		 			  	rcb, 
						ticks,
						start,
						stop,
						priority,
						quit
	};
						
		
						
	// LOCAL VARIABLES
	char command[cmd_size], i, j, **argv, *cursor1, *cursor2;
	int cmd_length, argc;
	enum command_type cmd;
	
	
	
	
	// INITIALIZE SHELL
	sem_get(COM1);		  // get comm port 1 semaphore
	INTR_ON();			  // enable interrupts
	
	
	
	// SHELL COMMAND LINE INTERPRETER
	while (1) {
	
		  i = 0;
		  cursor1 = command;
		  cursor2 = command;
		  
		  
		  // put a PROMPT
		  putchar('>'); putchar('>'); putchar(' ');
		  
		  
		  // GET AND ECHO THE COMMAND on the console
		  while (((command[i]=getchar()) != 0xD) && (i<cmd_size)) {
		  		putchar(command[i]);
				
				// allow backspace to correct typing mistakes
				if (command[i] == 8) {
				    putchar(32);  		 // space
					putchar(8);			 // backspace
					command[i] = '\0';	 // null
					i--;
					} 
				else
				  	i++;
		  		}
		  // terminate the string
		  command[i] = NULL;
		  putchar('\n');
		  
		  
		  
		  // PUT COMMAND LINE INTO ARGV & ARGC to extract cmd/args		  
		  // make argc and argv arrays according to the command line input
		  i = 0;
		  argc = 1;
		  
		  // count command + args: argc
		  while (command[i] != NULL) {
		  		if (command[i] == ' ') 
				   argc++;
				i++;
		  		}
		  
		  
		  // allocate memory to argv
		  if ((argv = malloc(argc)) == NULL) {
		  	 puts(error_msg[5]);
			 exit(1);
		  	 }
	 
 
		  // extract the cmd/args
		  i = 0;
		  for (j=0; j<argc; j++) {
		  	  
			  // search command for letters/numbers
			  while (isalnum(command[i])) {
			  		i++;
					cursor2++;
			  		}
			  

			  // found a cmd/arg, so make a string array for it
			  // must manually add a NULL char to the end
		  	  if ((argv[j] = malloc(cursor2 - cursor1 + 1)) == NULL) {
			  	 puts(error_msg[5]);
				 exit(1);
			  	 }

			  
			  // move finding to argv[j] array, and add NULL char
			  memmove(argv[j], cursor1, cursor2 - cursor1 + 1);
			  argv[j][i] = NULL;
			  
			  // update cursors
		  	  cursor1 = command + i + 1;
			  cursor2 = cursor1;
			  i++;
			   
		  	   }  // end for j
		  
		  
		  
		  // CLASSIFY THE COMMAND
		  if (command[0] == 0xD);	 // CR, so start over
		  else if (strcmp(argv[0], "help") == 0)
		  	   cmd = help;
		  else if (strcmp(argv[0], "tcb") == 0)
		  	   cmd = tcb;
		  else if (strcmp(argv[0], "rcb") == 0)
		  	   cmd = rcb;
		  else if (strcmp(argv[0], "ticks") == 0)
		  	   cmd = ticks;
		  else if (strcmp(argv[0], "start") == 0)
		  	   cmd = start;
		  else if (strcmp(argv[0], "stop") == 0)
		  	   cmd = stop;
		  else if (strcmp(argv[0], "priority") == 0)
		  	   cmd = priority;
		  else if (strcmp(argv[0], "quit") == 0)
		  	   cmd = quit;
		  else if (strcmp(argv[0], "") == 0) 
		  	   cmd = 0;
		  else {
		  	   puts(error_msg[1]); 
			   putchar('\n');
			   cmd = 0;
		  	   }
		
		
		  // CLASSIFY/VALIDATE THE PARAMETERS and DISPATCH the command
		  if (cmd) {
		  switch (cmd) {
		   		 case help:
				 	  if (argc == 1) {
					  	 puts("---HELP---\n");
					  	 puts("<> -- required arguments");
					  	 puts("[] -- optional arguments\n");
					  	 puts("help [cmd] -- help on a specific command");
					  	 puts("tcb [task_id] -- prints task control block");
					  	 puts("rcb [task_id] -- prints resource control block");
					 	 puts("ticks -- shows system tick");
					  	 puts("start <task_id> -- start a task");
					  	 puts("stop <task_id>  -- stop a task");
					 	 puts("priority <task_id>, <task_priority>  -- change prio");
					 	 puts("quit -- quit the shell (carefull!)");
					 	 putchar('\n');
					  	 }
					  else if (argc == 2) 
					  	   shellcmd_help(argv[1]);
				  	  break;
				 case tcb:
				 	  if (argc == 1)
				  	  	 shellcmd_tcb();
					  else if (argc > 1)
					  	   puts(error_msg[0]);
				  	  break;
				 case rcb:
				 	  puts(error_msg[0]);
				  	  break;
				 case ticks:
				 	  if (argc > 1)
					  	 puts(error_msg[1]);
					  else
				  	  	  shellcmd_ticks();
					  break;
				 case start:
				 	  if ((argc != 2) || 
					  	 (!isdigit(*argv[1])))
					  	 puts(error_msg[1]);
					  else
				  	  	shellcmd_start_task(atoi(argv[1]));
					  break;
				 case stop:
				 	  puts(error_msg[0]);
				 	  break;
				 case priority:
				 	  puts(error_msg[0]);
				 	  break;
				 case quit:
				 	  puts(error_msg[0]);
				 	  break;
				 default:
				  	  break;
					  
		  		}}	  // end switch/if		
		  
		
		
		  // free the argc and argv array memory
		  for (i=0; i<argc; i++)
		  	  free(argv[i]);
		  free(argv);
		  
		  putchar('\n');
		  
	}  // end while(1)
	
	
	INTR_OFF();
	sem_give(COM1);
	
}	// end shell()




void shellcmd_help(char *argv1) {

	 puts(error_msg[0]);
}



void shellcmd_tcb(void) {

	 // LOCAL VARIABLES
	 int id;

	 
	 /* ??this takes a while, so treat the printf() as a critical section 
	 	in order to get reliable data  */
		
	 
	 for (id=0; id<numtasks; id++) {
	 puts  ("-----------+----------+-----------+------------+");
	 
	 //INTR_OFF();
	 printf("TaskID: %2d | State: %d | Prio: %3d | Msgs: 0x0%x |\n", id,
	 				 	  		 	  			   get_task_state(id),
												   get_task_priority(id),
												   get_task_messages(id));
	 											   }
	 //INTR_ON();
	 
	 puts  ("-----------+----------+-----------+------------+");
	 puts("\nState: 0=idle, 1=pending, 2=running, 3=waiting, 4=finished");
	 puts("Message: [ bit7 | bit6 | bit5 | bit4 | bit3 | bit2 | prio | state ]"); 
	 
}




void shellcmd_ticks(void) {
	 
	 INTR_OFF();
	 printf("Ticks: %ld\n", system_tick);
	 INTR_ON();
}




void shellcmd_start_task(unsigned char id) {

	 if (set_task_state(id, pending) == 0)
	  	 printf("task %d started\n", id);
}

