// shell.c

#include "912d60.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "kernel.h"




// FUNCTION PROTOTYPES
void shellcmd_help(char *argv1);
void shellcmd_tcb(signed char id);
void shellcmd_mcb(signed char id);
void shellcmd_mcbPrint(signed char rid);
void shellcmd_ticks(void);
void shellcmd_startTask(unsigned char id);
void shellcmd_stopTask(unsigned char id);
void shellcmd_time(void);
void shellcmd_freeMemCheck(void);
void shellcmd_kdb(int onoff);
void shellcmd_quitShell(void);
/*
void shellcmd_setTaskPriority(unsigned char id, unsigned char priority);
*/




// GLOBAL VARIABLES
extern unsigned long int system_tick;
extern unsigned int current;		   	 	// current task id number

extern char error_msg[8][25];
extern char error_src[5][18];

extern char kdb_trace;




int shell(void) {
	
	// LOCAL CONSTANTS
	#define cmd_size (32+1)

	
	
	// LOCAL ENUMERATIONS
	enum command_type {	help = 1,
		 			  	tcb, 
		 			  	mcb, 
						ticks,
						start,
						stop,
						priority,
						time,
						freemem,
						kdb,
						quit,
						taskname
	};
						
		
						
	// LOCAL VARIABLES
	char command[cmd_size], i, j, **argv, *cursor1, *cursor2;
	int cmd_length, argc;
	enum command_type cmd;
	
	
	
	
	// INITIALIZE SHELL
	get_mutex(COM1);		  	   	  // get COM1 mutex
	set_task_priority(current, 200);
	INTR_ON();			  		  // enable interrupts
	
	
	
	
	
	// SHELL COMMAND LINE INTERPRETER
	while (1) {
	
		  INTR_ON();
		  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 (isgraph(command[i])) {
			  		i++;
					cursor2++;
			  		}
			  

			  // found a cmd/arg, so make a string array for it
		  	  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], "mcb") == 0)
		  	   cmd = mcb;
		  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], "time") == 0)
		  	   cmd = time;
		  else if (strcmp(argv[0], "freemem") == 0)
		  	   cmd = freemem;
		  else if (strcmp(argv[0], "kdb") == 0)
			   cmd = kdb;
		  else if (strcmp(argv[0], "quit") == 0)
		  	   cmd = quit;
		  else 
		  	   cmd = taskname;

		
		
		  // 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("mcb [[task_id] | [-b]] -- prints mutex 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("time [<hrs> <min> <sec> ]-- display/set the current system time");
						 puts("freemem -- get approximate free RAM space");
					 	 puts("kdb [1|0] -- turn kernel debug on/off");
						 puts("quit -- quit the shell (carefull!)");
					 	 putchar('\n');
					  	 }
					  else if (argc == 2) 
					  	   shellcmd_help(argv[1]);
				  	  break;
				 case tcb:
				 	  if (argc == 1)
				  	  	 shellcmd_tcb(-1);
					  else if (argc == 2)
					  	 shellcmd_tcb(atoi(argv[1]));
					  else if (argc > 2)
					  	   puts(error_msg[1]);
				  	  break;
				 case mcb:
				 	  if (argc == 1)		  		 // print entire rcb
					  	 shellcmd_mcb(-1);
					  else if (argc == 2) {
					  	 if (!strcmp(argv[1], "-b")) {	 // print 'busy' only
						 	shellcmd_mcb(-2);
							}
						 else	
					  	 	shellcmd_mcb(atoi(argv[1]));	  // single
						 } 
					  else if (argc > 2)
				 	  	 puts(error_msg[1]);
				  	  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_startTask(atoi(argv[1]));
					  break;
				 case stop:
				 	  if ((argc != 2) || 
					  	 (!isdigit(*argv[1])))
					  	 puts(error_msg[1]);
					  else
				  	  	shellcmd_stopTask(atoi(argv[1]));
				 	  break;
				 case priority:
				 	  puts(error_msg[0]);
				 	  break;
				 case time:
				 	  switch (argc) {
					  	case 1:
							 shellcmd_time();
							 break;
						case 4:
							 if (argv[1] && argv[2] && argv[3])
							 	set_sysTime(atoi(argv[1]), 
											atoi(argv[2]), 
											atoi(argv[3]));
							 else
							 	 puts(error_msg[1]);
							 break;
						default:
							 puts(error_msg[1]);
						}
				 	  break;
				 case freemem:
				 	  shellcmd_freeMemCheck();
				 	  break;
				 case kdb:
					  if (argc < 2)	
						puts(error_msg[1]);
					  else 
					  	shellcmd_kdb(atoi(argv[1]));
					  break;
				 case quit:
				 	  shellcmd_quitShell();
					  return 1;
				 case taskname:
				 	  // start a stopped task that's already created
					  cmd = 0;
					  for (i=0; i<MAXTASKS; i++) {
					  	  if (if_task_exists(i) < 0)	 // non-existent
						  	 continue;
						  else {	  			  		 // does exist
						  	  if (!strcmp(argv[0], get_task_name(i))) {
							  	 set_task_state(i, PENDING);		 // matches
								 INTR_OFF();
								 cmd = 1;
								 }
							   }
						  if (cmd)
						  	 break;
						  }
						if (!cmd) {
						   puts(error_msg[1]); 
			   		 	   putchar('\n');
						   }
				  	  break;
				  default:
				  		  puts(error_msg[1]); 
			   		  	  putchar('\n');
				  		  break;
					  
		  		}}	  // end switch/if		
		  
		
		
		  // free the argv array and argv[i] memory
		  for (i=0; i<argc; i++)
		  	  free(argv[i]);
		  free(argv);
		  
		  putchar('\n');
		  
	}  // end while(1)
	
	
	INTR_OFF();
	give_mutex(COM1);
	
	return 0;
	
}	// end shell()





void shellcmd_help(char *argv1) {

	 puts(error_msg[0]);
}



void shellcmd_tcb(signed char tid) {
	
	 // Local Variables
	 unsigned char max, start;
	 
	 if (tid >= MAXTASKS) {
	 	puts(error_msg[2]);
		return;
		}
		
	 if (tid < 0) {
	 	start = 0;
		max = MAXTASKS;
		}	
	 else {
	 	start = tid;
	 	max = tid + 1;
		}
	 
	 puts("\nTask Control Block:");
	 for (tid=start; tid<max; tid++) {
	 
	 // don't print a big empty tcb
	 if (get_task_name(tid) == NULL)
	 	continue;
		
	 puts  ("---------------+-------+----------+-----------+------------+------------+");
	 
	 //INTR_OFF();
	 printf("Name: %8s | ID: %d | State: %d | Prio: %3d | Addr: %4x | Msgs: 0x%2x |\n", 
			get_task_name(tid),
			tid,
	 		get_task_state(tid),
			get_task_priority(tid),
			get_task_address(tid),
			get_task_messages(tid));
	 }		
	 //INTR_ON();
	 
	 puts  ("---------------+-------+----------+-----------+------------+------------+");
	 
	 puts("\nState: 0=idle, 1=pending, 2=running, 3=waiting, 4=stopped");
	 puts("Message: [ bit7 | bit6 | bit5 | bit4 | bit3 | bit2 | prio | state ]"); 
	 
}




void shellcmd_mcb(signed char option) {
	 
	 // Local Variables
	 unsigned char id;
	 

	 
	 // print all	
	 if (option == -1) {
		for (id=0; id<MAXMUTEXES; id++) {
			if (get_mutex_name(id) == NULL)
			   continue;
			shellcmd_mcbPrint(id);
			}
		}	
	 
	 // only print busy mutexes
	 else if (option == -2) {
	 	for (id=0; id<MAXMUTEXES; id++) {
			if (get_mutex_state(id) == BUSY)
			   shellcmd_mcbPrint(id); 
			}
		}
		
	 // print only one
	 else {
	 	  if (option >= MAXMUTEXES) {
	 	  	 puts(error_msg[6]);
			 return;
			 }
		  else  
	 	  	shellcmd_mcbPrint(option);

		}	

	 
	 puts  ("-----------+--------+----------+-----------+------------+");
	 
	 puts("\nState: 0=notbusy, 1=busy");
	 puts("Owner: 0..15=task ID, -1=nobody");

}



void shellcmd_mcbPrint(signed char id) {

	 puts  ("-----------+--------+----------+-----------+------------+");
	 
	 INTR_OFF();
	 printf("Type: %s | ID: %2d | State: %x | Owner: %2d | Waiting: %d |\n",
	 		get_mutex_name(id), 
	 		id,
	 		get_mutex_state(id),
			get_mutex_owner(id),
			get_mutex_queuelen(id));
	INTR_ON();

}



void shellcmd_ticks(void) {
	 
	 INTR_OFF();
	 printf("ticks: %ld\n", system_tick);
	 INTR_ON();
}




void shellcmd_startTask(unsigned char id) {

	 INTR_OFF();
	 if (set_task_state(id, PENDING) == 0)
	  	 printf("task %d started\n", id);
	 INTR_ON();
}




void shellcmd_stopTask(unsigned char id) {

	 if ((set_task_state(id, STOPPED)) == 0) {
	 	give_mutex(COM1);
	  	printf("task %d stopped\n", id);
		}

}




void shellcmd_time(void) {

	// LOCAL VARIABLES
	unsigned long int remainder1, remainder2, time;
	char hours, minutes, seconds;
	int milliseconds;
	 
	
	// get the current time, in milliseconds
	time = get_sysTime();
	
	/* convert to hh:mm:ss.ms
	hours = 			 	   time / mS_PER_HOUR;
	remainder1 =		 	   time % mS_PER_HOUR;
	minutes =  			 remainder1 / mS_PER_MINUTE;
	remainder2 = 		 remainder1 % mS_PER_MINUTE;
	seconds =  			 remainder2 / mS_PER_SECOND;
	milliseconds = 		 remainder2 % mS_PER_SECOND;
	 
	printf(" %d:%d:%d.%d\n", hours, minutes,seconds, milliseconds);      */
								    
}




void shellcmd_freeMemCheck(void) {
	 

	 INTR_OFF();
	 printf("approximate free heap memory: %d bytes\n", get_free_memory());
	 INTR_ON();
	 
}



void shellcmd_kdb(int onoff) {


	kdb_trace = onoff;

}




void shellcmd_quitShell(void) {

	 puts("exiting shell");
	 puts("\n");
	 shellcmd_stopTask(get_task_id());

}

