// FILE:      BrailleTranslator.m
// AUTHOR:    Daniel Lee (unless stated)
// PURPOSE:   To translate text to braille or vice versa.
// REFERENCE: None. 
// COMMENTS:  
// REQUIRES:  BrailleTranslator.m
// Last Mod:  31-07-2006




#import "BrailleTranslator.h"

@implementation BrailleTranslator


// NAME: init
// PURPOSE: To initialise the data
// IMPORTS: nil
// EXPORTS: nil
// AUTHOR: Daniel Lee
// REMARKS: Used to ensure tt the values are initialised properly.
// LAST MODIFIED: 17-05-2006

- (id) init
{
    int listIndex;
    // Initialise the items and wait a function call from another object
	// to reset these values.
	
	for( listIndex = 0; listIndex < RULE_ARRAY_SIZE; listIndex++ )
	{
		list_array[listIndex] = NULL;
	}
    translateToBraille = YES;
    brailleGrade = 0;

	return self;
}


// NAME: changeRules
// PURPOSE: To switch the rules of translation
// IMPORTS: int grade
// EXPORTS: nil
// AUTHOR: Daniel Lee
// REMARKS: 
// LAST MODIFIED: 28-06-2006

- (void) changeRules: (NSString *) grade
{
    int listIndex;
    // Switch the tables to reflect the change
	
	// Initialise the rule array
	for( listIndex = 0; listIndex < RULE_ARRAY_SIZE; listIndex++ )
	{
		list_array[listIndex] = NULL;
	}

    // Find the file for the language and the grade, then construct the rules
	if( translateToBraille )
	{
	    constructRulesToBraille( RULES_PATH );
	}
	else
	{
	    constructRulesFromBraille( RULES_PATH_B );
    }
}


// NAME: translateLine
// PURPOSE: To serve as an interface for the two C functions
// IMPORTS: NSString input, int grade
// EXPORTS: NSString
// AUTHOR: Daniel Lee
// REMARKS:    1) Function calls changeRules
//             2) Need to split the 
// LAST MODIFIED: 31-07-2006

- (NSString *) translateLine: (NSString *) input grade: (NSString *) grade direction: (BOOL) newTranslateToBraille
{
    // returnCharArray needs to be initalised to a set length 
	// inputCharArray is set to the input and cast into a c string
    NSString * returnString = @"";
	char returnCharArray[OUTPUT_SIZE] = "\0";
	char * inputCharArray = (char *) [input cStringUsingEncoding: NSASCIIStringEncoding];
	char * output_String = (char *) &returnCharArray;

    translateToBraille = newTranslateToBraille;

	[self changeRules: grade];    // Change the rules here.

    // Convert string into NSTextStorage and then convert into an array of words
    if( translateToBraille )
    {
        translateLineToBraille( inputCharArray, returnCharArray);
    }
    else
    {
        translateLineFromBraille( returnCharArray, inputCharArray);
    }
    // initWithCString requires a cast to const char* and to include encoding pattern, which is ASCII
    returnString = [NSString stringWithCString: (const char*) output_String encoding: NSASCIIStringEncoding];
    NSLog( @"BrailleTranslator: returnString - %@", returnString);

    return returnString;
}


// NAME: hashFunction
// PURPOSE: To return the hash value to which a rule will be applied.
// IMPORTS: char key
// EXPORTS: int index
// AUTHOR: Shannon Thrower
// REMARKS: This function determines the index of the hash table list_array[] to which a rule should
//          be applied. It compares the first letter of the rule's focus to the letter A and typecasts
//          the result to an integer thus defining the index.
// LAST MODIFIED: 29-03-2006

int HashFunction(char key)
{
	int index = (int)key - (int)'A';

	if(index<0 || index>25)
	{
		index = 26;
	}
	return index;
}


/**************************************

   Methods for forward translation

***************************************/


// NAME: translateLineToBraille
// PURPOSE: To translate from text to braille
// IMPORTS: char print_Line[]
// EXPORTS: char braille_Line[]
// AUTHOR: Shannon Thrower
// REMARKS: This version v8 date 01-11-05
// LAST MODIFIED: 06-04-2006

void translateLineToBraille( char print_line[], char braille_line[] )
{
	int current_state = 1, array_index, match_flag=0, focus_length;
	int focus_match, state_match,
		lctxt_match, rctxt_match; 		// used as flags to represent a match occuring
	char right_of_focus[INPUT_SIZE],	// stores the braille input to the right of the current focus
		left_of_focus[INPUT_SIZE]="\0"; 	// stores the braille input to the left of the current focus
	char current_character = braille_line[0];
	char focus[FOCUSSIZE], temp_focus[FOCUSSIZE], print_line_copy[INPUT_SIZE];
	
	rule_t *current_rule;
	int decision_table [5][6]={{1,1,0,0,0,0},	//row represents the current state
	{1,0,0,0,0,0},	//column represents the current input class
	{1,0,1,0,0,0},	//rule fires if there is a 1 corresponding to
	{1,0,0,1,0,0},	//current state and input class
	{0,0,0,0,1,0}};
	int new_word=1; //flag used to indicate the beginning of a new word
	
	
	//initialise variables
	strcpy(print_line_copy, print_line);
	strcpy(right_of_focus, "\0");
	strcpy(braille_line, "\0");
	
	//remove misc chars. tabs new lines etc
	int i;
	for (i=0; i<strlen(print_line);i++){ //for all char in the input string
		if ((int)print_line[i]<32){			 //delete the chars that are misc ie less than 32
			print_line[i]=' ';//note this line actually converts misc chars to space chars
		}
	}
	
	
	while(print_line[0] !='\0' && print_line[0] !='\n'){
		match_flag=0;
		
		//check if capital signs are needed.
		if(new_word && !isspace(print_line[0])){
			if(isalpha(print_line[0])){
				new_word = 0;
			}
			if(isupper(print_line[0])){
				braille_line = strcat(braille_line, ",");
				if(isupper(print_line[1])){
					braille_line = strcat(braille_line, ",");
				}
			}
		}
		
		//convert the char to upper case for comparison.
		current_character = toupper((int)print_line[0]);
		array_index = (int)current_character-(int)'A';
		if(array_index<0 || array_index>25){
			array_index = 26;
		}
		
		
		//search linked list corresponding to array_index for a match
		current_rule = list_array[array_index];
		
		while(!match_flag && current_rule != NULL){
			strcpy(focus, current_rule->focus); //ie 	focus = current_rule->focus
			focus_length = strlen(focus);
			
			//check the focus for a match irrelevant of case. Note that windows and linux use different
			//comands to do this.
			//returns 0 if the rule's focus matches the braille word (up to the lengh of the focus)
			//focus_match = strnicmp(focus, print_line, focus_length);		//windows function
			focus_match = strncasecmp(focus, print_line, focus_length);	//linux function
			
			if (focus_match == 0){
				// the focus has been matched. Now check the state
				// use the rule's state and current input class to check the decision table
				
				state_match = decision_table[current_state-1][current_rule->input_class-1];
				
				if (state_match){
					// the state has been matched. now check the left context
					//strncpy(left_of_focus,print_line, position_of_focus);
					lctxt_match = CheckLeftCtxt(current_rule->left_context, left_of_focus);
					if (lctxt_match){
						// the left context has been matched. now check the right context
						strcpy(right_of_focus, &print_line[strlen(current_rule->focus)]);
						// ie. right_of_focus = braille_line from the end of the current focus
						rctxt_match = CheckRightCtxt(current_rule->right_context, right_of_focus);
						if (rctxt_match){
							match_flag=1;
						}
					}
				}
			}
			if(!match_flag){
				//if a match has not been found move to the next rule in the linked list
				current_rule = current_rule->next;
			}
		}
		//a rule has been matched OR the end of the linked list has been reached
		
		if (match_flag){
			// a match has been found the rule should fire.
			// update the output, the new state and remove the focus from the input braille word
			
			//update new word flag
			if(!isalpha((int)current_rule->focus[0])){
				new_word=1;
			}
			
			//update the state
			if (current_rule->next_state != 99){
				current_state = current_rule->next_state;
			}
			
			//concatenate the output of the rule to the translated word
			//braille_line = strcat(braille_line, current_rule->output);
			strcpy(braille_line, strcat(braille_line, current_rule->output));
			
			//remove the focus from the input print line and attach it to the left of the focus
			strcpy(print_line, &print_line[focus_length]);
			strcat(left_of_focus, current_rule->focus);
		}else{
			/**/		//commented text may be used for debugging new rules
						//printf("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
						//printf("!!!The translation has exited without a match being found!!!\n");
						//printf("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
						//printf("the print input was:%s.\n", print_line);
						//printf("The right of focus was:%s.\n", right_of_focus);
			
			// Print "ERROR" to the output file
			strcpy(braille_line, strcat(braille_line, "\n\n\nERROR\n\n\n"));
			// remove character from the input
			int i;
			for (i=0; i<=strlen(print_line); i++){
				print_line[i]=print_line[i+1];
			}
		}
	}
}


// NAME: constructRulesToBraille
// PURPOSE: To contruct the rules for text to braille
// IMPORTS: char* location
// EXPORTS: Nil
// AUTHOR: Shannon Thrower 
// REMARKS: 
// LAST MODIFIED: 29-03-2006

void constructRulesToBraille( char* location )
{
	/*variable declarations*/
	char rule[RULESIZE], tempRule[RULESIZE];
	int array_index;
	rule_t *current_rule, *last_rule=NULL, *next_rule;
	char  *pch;
	/*open the rules input file as defined by RULES_PATH so that a linked list of rules can be constructed*/
	FILE *rulesfile;
	rulesfile = fopen(location,"r");
	if (rulesfile == NULL)
		{
			printf("**************************************\n* Cannot open the rules file         *\n* The rules file should be stored at *\n* %s\n**************************************\n",RULES_PATH);
		}
	else
		{
		/*for each rule go through the input file and split the rule into its various components to be stored
		in the rule_t data structure*/
		while(fgets(rule, 1000, rulesfile)!=NULL){
			current_rule = malloc(sizeof(rule_t));
			current_rule->next = NULL;
			/*set the input class of the rule*/
			current_rule->input_class = (int)rule[0]-48;//must subtract 48 for the integer typecast
			/*set the focus parameter of the rule*/
			strcpy(tempRule, rule);
			if(strstr(tempRule, "[]]")){
				strcpy(current_rule->focus, "]");
			} else {
				pch = strtok (tempRule,"[");
				pch = strtok (NULL, "]");
				strcpy(current_rule->focus, pch);
			}

			/*set the output field of the rule*/
			/* the tokenising of the output is complex due to some rule using multiple equal signs
			hence tokensing at an equal sign creates ambiguity as to which of the multiple equal signs
			to tokenise from. the majority of rules that contain multiple equal signs are those with an
			equal sign in the focus and those translation "for" to the equal sign and hence have an equal
			sign in the output. */
			strcpy(tempRule, rule);
			// check if there are multiple equal signs in the rule
			int counta=0;
			int temp=0;
			for ( temp=0; temp<strlen(tempRule); temp++){
				if (tempRule[temp]=='='){
					++counta;
				}
			}
			if(counta==1){
				/* there is only one equal sign in the rule. this should be the majority of rules. the only
				   complication occurs in some rules that may have no output. these rules are used only to
				   change the state of program. eg the number sign. */
				if (!strstr(tempRule, "=\t")){
					// an output exists
					pch = strtok (tempRule,"=");
					pch = strtok (NULL, "\t");
					strcpy(current_rule->output, pch);
				} else {
					// an output does not exist
					strcpy(current_rule->output, "\0");  // note "\0" is the null character
				}
			} else {
				/*the rule contains multiple equal signs.
				remove anything before the first equal sign AFTER the end of the focus ie after ']' */
				int index=0;
				//move index to the end of the focus
				while (tempRule[index] !=']'){
					++index;
				}
				//move index to the fist equal sign
				while (tempRule[index] !='='){
					++index;
				}
				//remove anything before this index
				strcpy(tempRule, &tempRule[index+1]);
				pch = strtok (tempRule, "\t");
				strcpy(current_rule->output, pch);
			}


			/*set the next state field of the rule*/
			if (rule[strlen(rule)-2] == '-'){ 	//if the rule has a next state of - then set the next state field
				current_rule->next_state = 99;  //to 99. then when updating state do not change if the next state is 99
			}else{
				current_rule->next_state = (int)rule[strlen(rule)-2]-48;//must subtract 48 for the integer typecast
			}


			/*if there is no right context the strtok command has some problems hence
			 *some we need to check if "]=" occurs in the rule. ie if there is no right context.*/
			strcpy(tempRule, rule);
			if (!strstr(tempRule, NO_RIGHT_CONTEXT)){
				// a right context exists
				pch = strtok (rule,"]");
				pch = strtok (NULL, "=");
				strcpy(current_rule->right_context, pch);
			}else{
				// a right context does not exist
				strcpy(current_rule->right_context, "\0");  // note "\0" is the null character
			}


			/*set the left context field of the rule*/
			strcpy(tempRule, rule);
			if (!strstr(tempRule, "\t[")){
				pch = strtok (rule,"\t");
				pch = strtok (NULL, "[");
				strcpy(current_rule->left_context, pch);
			}else{
				// a left context does not exist
				strcpy(current_rule->left_context, "\0");
			}


			/*determine the list index to according to the first char of the focus*/
			array_index = HashFunction(current_rule->focus[0]);
			if (list_array[array_index] == NULL){
				//Nothing entered in array yet for this focus
				//Add current record into array
				list_array[array_index] = current_rule;
			}else{ //Array entry contains records, go to the end to add this one
				next_rule = list_array[array_index];
                while (next_rule->next != NULL){
	            	next_rule = next_rule->next;
		        }
				//When we get here nextRecord contains the last entry
				//Now just point it to the new entry
				next_rule->next = current_rule;
			}
		}
	}
	fclose(rulesfile);
}


// NAME: checkLeftCtxt
// PURPOSE: 
// IMPORTS: char left_CTxt[], char left_Of_Focus[]
// EXPORTS: int ctxt_match
// AUTHOR: Shannon Thrower
// REMARKS: 
// LAST MODIFIED: 29-03-2006

int CheckLeftCtxt( char left_ctxt[], char left_of_focus[] )
{
	int ctxt_match = 0,
	char_ctxt_match;	// used as a flag when matching a single character of a rules right context;
	int n;					// used to process each char of a rule's right context
	int lof_len;			// length of the left of focus string
	char left_focus_cpy[INPUT_SIZE];	//function manipulates a COPY of the left of focus string since manipulation
										//of the left of focus string would alter the string being passed in as the argument.
	strcpy(left_focus_cpy, left_of_focus);
	
	//get the length of the left of focus string
	if (strlen(left_focus_cpy) == 0){
		lof_len = 0;
	} else {
		lof_len = strlen(left_focus_cpy)-1;
	}
	
	if(left_ctxt[strlen(left_ctxt)-1] == '\0'){
		//this rule does not have a right context. hence it is a match
		ctxt_match=1;
	}else{ /* a right context exists for the rule, check the braille input
		* left of the focus to see if a match occurs.*/
		
		/* match each right context character of the rule. if any one of the chars
		in the right context does not match then rule can not fire */
		char_ctxt_match = 1;
		for (n=strlen(left_ctxt); n>0; n--){
			/* wild cards "!"= a letter
			"#"= a number
			"~"= a space or punctuation
			" "= a space char only
			"|"= zero or mote capital signs
			"`"= one or more roman numerals
			";"= zero or more letters
			"+"= one or more digits								*/
			int i; //used for looping in some of the case statements
			switch (left_ctxt[n-1]){
				case '!': // a letter
					if(!isalpha((int)left_focus_cpy[lof_len])){
						char_ctxt_match = 0;
					} else {//remove the letter char from input to the right of the focus
							//strcpy(right_of_focus, &right_of_focus[1]);
						left_focus_cpy[lof_len]='\0';
					}
					break;
				case '#': // a number
					if( (left_focus_cpy[lof_len]<48) || (left_focus_cpy[lof_len]>57) ){
						// note these are the asci values of 0 and 9. ie if right_of_focus
						// is not a number then set the match to false.
						char_ctxt_match = 0;
					} else {//remove the white space char from input to the right of the focus
						left_focus_cpy[lof_len]='\0';
					}
					break;
				case '~': // a space or punct char
					/* if the frist character to the right of the focus is not a white space character
					or a punctuation char then set the chat_ctxt_match to false */
					if(isspace((int)left_focus_cpy[lof_len]) || ispunct((int)left_focus_cpy[lof_len]) || (left_focus_cpy[lof_len] == '\0')){
						//remove the space/punct char from the input to the right of the focus
						left_focus_cpy[lof_len]='\0';
					} else {
						char_ctxt_match = 0;
					}
					break;
				case ' ': // a space only
					/* if the frist character to the right of the focus is not a white space character
					then set the chat_ctxt_match to false */
					if(isspace((int)left_focus_cpy[lof_len]) || (left_focus_cpy[lof_len]=='\0')){
						//remove the white space char from input to the right of the focus
						left_focus_cpy[lof_len]='\0';
					} else {
						char_ctxt_match = 0;
					}
					break;
				case '|': //zero or more capital signs
					if(left_focus_cpy[lof_len] == ',' ){
						left_focus_cpy[lof_len]='\0';
					} else {
						char_ctxt_match = 0;
					}
					break;
				case '`': //one or more roman letters (I, V, M, C)
					if (left_focus_cpy[lof_len]!='I' && left_focus_cpy[lof_len]!='V' && left_focus_cpy[lof_len]!='M' && left_focus_cpy[lof_len]!='C'){
						char_ctxt_match = 0;
					} else {//remove the roman letter for the input to the right of the focus
						int i=lof_len;
						while (left_focus_cpy[i]=='I' || left_focus_cpy[i]=='V' || left_focus_cpy[i]=='M' || left_focus_cpy[i]=='C'){
							left_focus_cpy[i]='\0';
							--i;
						}
					}
					break;
				case ';': //zero or more letters
					/* do not change the value of char_ctxt_match since there will always be
					zero or more. the vale of char_ctxt_match must be preserved.*/
					//if there are punctuation characters remove them from the right of focus
					i=lof_len;
					while (isalpha((int)left_focus_cpy[i])){//is ture if char is an alpha/letter char.
						left_focus_cpy[i]='\0';
						--i;
					}
						//strcpy(right_of_focus, &right_of_focus[i]);
						break;
				case '+': //one or more digits
					/* note there must be at least one digit. */
					if(!isdigit((int)left_focus_cpy[lof_len])){
						char_ctxt_match = 0;
					} else {//remove the space/punct char from the input to the right of the focus
						int i=lof_len;
						while (isalpha((int)left_focus_cpy[i])){//is ture if char is a punctuation char.
							left_focus_cpy[i]='\0';
							--i;
						}
					}
					break;
				default:
					//left context character is not a wild card. it must be tested as it is. ie the ctxt
					//char must be the next char in the left of focus string.
					if ( toupper((int)left_focus_cpy[lof_len]) == left_ctxt[n-1] ){
						//left context matches rule for a particular char. then remove it.
						left_focus_cpy[lof_len]='\0';
					} else {
						char_ctxt_match = 0;
					}
			}
		}// end FOR statement...for each char of rule's right context
		
		if (char_ctxt_match == 1){
			ctxt_match = 1;
		}else{
			ctxt_match = 0;
			
		}
	}
	return ctxt_match;
}


// NAME: checkRightCtxt
// PURPOSE: 
// IMPORTS: char right_CTxt[], char right_Of_Focus[]
// EXPORTS: int ctxt_match
// AUTHOR: Shannon Thrower
// REMARKS: 
// LAST MODIFIED: 29-03-2006

int CheckRightCtxt( char right_ctxt[], char right_of_focus[] )
{
	int ctxt_match = 0,
	char_ctxt_match;	// used as a flag when matching a single character of a rules right context;
	int n;					// used to process each char of a rule's right context
	
	if(right_ctxt[0] == '\0'){
		//this rule does not have a right context. hence it is a match
		ctxt_match=1;
	}else{ /* a right context exists for the rule, check the braille input
		* right of the focus to see if a match occurs.*/
		
		/* match each right context character of the rule. if any one of the chars
		in the right context does not match then rule can not fire */
		char_ctxt_match = 1;
		for (n=0; n<strlen(right_ctxt); n++){
			/* wild cards "!"= a letter
			"#"= a number
			"~"= a space or punctuation
			" "= a space char only
			"`"= one or more roman numerals
			";"= zero or more letters
			"+"= one or more digits				*/
			
			int i; //used for looping in some of the case statements
			switch (right_ctxt[n]){
				case '!': // a letter
					if(!isalpha((int)right_of_focus[0])){
						char_ctxt_match = 0;
					} else {//remove the letter char from input to the right of the focus
						strcpy(right_of_focus, &right_of_focus[1]);
					}
					break;
				case '#': // a number
					if( (right_of_focus[0]<48) || (right_of_focus[0]>57) ){
						// note these are the asci values of 0 and 9. ie if right_of_focus
						// is not a number then set the match to false.
						char_ctxt_match = 0;
					} else {//remove the white space char from input to the right of the focus
						strcpy(right_of_focus, &right_of_focus[1]);
					}
					break;
				case '~': // a space or punct char
					/* if the frist character to the right of the focus is not a white space character
					or a punctuation char then set the chat_ctxt_match to false */
					if(isspace((int)right_of_focus[0]) || ispunct((int)right_of_focus[0]) || (right_of_focus[0] == '\0')){
						//remove the space/punct char from the input to the right of the focus
						strcpy(right_of_focus, &right_of_focus[1]);
					} else {
						char_ctxt_match = 0;
					}
					break;
				case ' ': // a space only
					/* if the frist character to the right of the focus is not a white space character
					then set the chat_ctxt_match to false */
					if(isspace((int)right_of_focus[0]) || (right_of_focus[0]=='\0')){
						//remove the white space char from input to the right of the focus
						strcpy(right_of_focus, &right_of_focus[1]);
					} else {
						char_ctxt_match = 0;
					}
					break;
				case '`': //one or more roman letters (I, V, M, C)
					if (right_of_focus[0]!='I' && right_of_focus[0]!='V' && right_of_focus[0]!='M' && right_of_focus[0]!='C'){
						char_ctxt_match = 0;
					} else {//remove the roman letter for the input to the right of the focus
						i=0;
						while (right_of_focus[i]=='I' || right_of_focus[i]=='V' || right_of_focus[i]=='M' || right_of_focus[i]=='C'){
							++i;
						}
						strcpy(right_of_focus, &right_of_focus[i]);
					}
					break;
				case ';': //zero or more letters
					/* do not change the value of char_ctxt_match since there will always be
					zero or more. the vale of char_ctxt_match must be preserved.*/
					//if there are punctuation characters remove them from the right of focus
					i=0;
					while (isalpha((int)right_of_focus[i])){//is ture if char is a punctuation char.
						++i;
					}
						strcpy(right_of_focus, &right_of_focus[i]);
					break;
				case '+': //one or more digits
					/* note there must be at least one digit. */
					if(!isdigit((int)right_of_focus[0])){
						char_ctxt_match = 0;
					} else {//remove the space/punct char from the input to the right of the focus
						int i=0;
						while (isalpha((int)right_of_focus[i])){//is ture if char is a punctuation char.
							++i;
						}
						strcpy(right_of_focus, &right_of_focus[i]);
					}
					break;
				default:
					if ( toupper((int)right_of_focus[0]) == right_ctxt[n] ){
						//right context matches rule for a particular char. then remove it.
						strcpy(right_of_focus, &right_of_focus[1]);
					} else {
						char_ctxt_match = 0;
					}
			}
		}// end for statement...for each char of rule's right context
		
		if (char_ctxt_match == 1){
			ctxt_match = 1;
		}else{
			ctxt_match = 0;
			
		}
	}
	return ctxt_match;
}


/**************************************

   Methods for backward translation

***************************************/


// NAME: constructRulesFromBraille
// PURPOSE: To contruct the rules for braille to text
// IMPORTS: char* location
// EXPORTS: Nil
// AUTHOR: Shannon Thrower 
// REMARKS: 
// LAST MODIFIED: 29-03-2006

void constructRulesFromBraille( char* location )
{
	/*variable declarations*/
	char rule[RULESIZE], tempRule[RULESIZE];
	int array_index;
	rule_t *current_rule, *next_rule;
	char  *pch;
	/*open the rules input file as defined by RULES_PATH so that a linked list of rules can be constructed*/
	FILE *rulesfile;
	rulesfile = fopen(location,"r");
	if (rulesfile == NULL)
		{
			printf("**************************************\n* Cannot open the rules file         *\n* The rules file should be stored at *\n* %s\n**************************************\n",RULES_PATH);
		}
	else
		{
		/*for each rule go through the input file and split the rule into its various components to be stored
		in the rule_t data structure*/
		while(fgets(rule, 100, rulesfile)!=NULL){
			//reserve memeory for the new rule
			current_rule = malloc(sizeof(rule_t));
			//initialise the next rule pointer
			current_rule->next = NULL;
			/*set the input class of the rule*/
			current_rule->input_class = (int)rule[0]-48;//must subtract 48 for the integer typecast

			/*set the focus parameter of the rule*/
			strcpy(tempRule, rule);
			if(strstr(tempRule, "[]]")){
				strcpy(current_rule->focus, "]");
			} else {
				pch = strtok (tempRule,"[");
				pch = strtok (NULL, "]");
				strcpy(current_rule->focus, pch);
			}

			/*set the output field of the rule*/
			strcpy(tempRule, rule);
			if (!strstr(tempRule, "=\t")){
				/* an output exists (some may not have an output they
				are used to change the state of the program*/

				/*this section is all very dodgy. basically it finds if there are
				multiple occurences of the equal sign in a rule and if there is
				it replaces the first occurence with an F. then tokenising can
				be applied as normal.*/
				int count=0, index=0;
				int temp=0;
				for (temp=0; temp<strlen(tempRule); temp++){
					if (tempRule[temp]=='='){
						++count;
						if (count==1){
							index=temp;
						}
					}
				}
				if(count==2){
					tempRule[index]='F';
				}
				//now we can tokenise as normal
				pch = strtok (tempRule,"=");
				pch = strtok (NULL, "\t");

				strcpy(current_rule->output, pch);
			}else{
				// an output does not exist. some rules such as the letter sign do not have an output.
				// These are used to change the state of the program.
				strcpy(current_rule->output, "\0");  // note "\0" is the null character
			}


			/*set the next state field of the rule*/
			current_rule->next_state = (int)rule[strlen(rule)-2]-48;//must subtract 48 for the integer typecast
			/*note: the value subtracted from strlen() may be platform dependent.
		   	  when using a file edited in windows the newline char is not included in this strlen count
			  but the newline char is included in a linux strlen count.
			  for windows this value should be 2 but for linux this file should be 3.*/

			/*if there is no right context the strtok command has some problems hence
			 * we need to check if "]=" occurs in the rule. ie if there is no right context.*/
			strcpy(tempRule, rule);
			if (!strstr(tempRule, NO_RIGHT_CONTEXT)){
				// a right context exists
				pch = strtok (tempRule,"]");
				pch = strtok (NULL, "=");
				strcpy(current_rule->right_context, pch);
			}else{
				// a right context does not exist
				strcpy(current_rule->right_context, "\0");  // note "\0" is the null character
			}


			/*determine the list index to according to the first char of the focus*/
			array_index = HashFunction(current_rule->focus[0]);
			if (list_array[array_index] == NULL){
				//Nothing entered in array yet for this focus
				//Add current record into array
				list_array[array_index] = current_rule;
			}else{ //Array entry contains records, go to the end to add this one
				next_rule = list_array[array_index];
                while (next_rule->next != NULL){
	            	next_rule = next_rule->next;
		        }
				//When we get here nextRecord contains the last entry
				//Now just point it to the new entry
				next_rule->next = current_rule;
			}
		}
		printf("\n");
	}
	fclose(rulesfile);
}


// NAME: translateLineFromBraille
// PURPOSE: To translate from braille to text
// IMPORTS: char braille_Line[]
// EXPORTS: char print_Line[]
// AUTHOR: Shannon Thrower
// REMARKS: 
// LAST MODIFIED: 29-03-2006

void translateLineFromBraille( char braille_line[], char print_line[] )
{
	//char print_line[OUTPUT_SIZE];
	int current_state = 1, array_index, match_flag=0, focus_length;
	int focus_match, state_match; // used as flags to represent a match occuring
   	int char_ctxt_match; // used as a flag when matching a single character of a rules right context
	int shift_char=0, shift_word=0; //used as flags to capitalise a char or a word
	int n; //used to process each char of a rule's right context
	char right_of_focus[INPUT_SIZE]; //stores the braille input to the right of the current focus
	char current_character = braille_line[0]; //the first char of the braille input
	char focus[FOCUSSIZE], braille_line_copy[INPUT_SIZE];
	char new_out[OUTSIZE]; //temporarily holds a rules output while capitalizing may be done
	rule_t *current_rule;

	int decision_table [6][7]={{1,1,1,0,0,0,0},
							   {1,0,1,0,0,0,0},	//row represents the current state
							   {1,0,0,1,0,0,0},	//column represents the current input class
							   {1,0,0,0,0,0,1},	//rule fires if there is a 1 corresponding to
							   {1,0,0,0,1,0,0},	//current state and input class
							   {0,0,0,0,0,1,0}};

	//initialise variables
	strcpy(braille_line_copy, braille_line);
	strcpy(right_of_focus, "\0");
	strcpy(print_line, "\0");


	while(braille_line[0] !='\0' && braille_line[0] !='\n'){
		match_flag=0;

		/*ASCII char 12 is the form feed or new page char.
		for now just remove it and ignore it*/
		if((int)braille_line[0]==12){
			strcpy(braille_line, &braille_line[1]);
		}
		//determine which linked list needs to be searched
		current_character = braille_line[0];
		array_index = (int)current_character-(int)'A';
		if(array_index<0 || array_index>25){
			array_index = 26;
		}
		//search linked list corresponding to array_index for a match
		current_rule = list_array[array_index];

		while(!match_flag && current_rule != NULL){
			strcpy(focus, current_rule->focus); //ie 	focus = current_rule->focus
			focus_length = strlen(focus);

			/*strnicmp returns 0 if the rule's focus matches the
			braille word (up to the lengh of the focus)*/
			focus_match = strncasecmp(focus, braille_line, focus_length);	/*linux command*/
			//focus_match = strnicmp(focus, braille_line, focus_length);		/*windows command*/

			if (focus_match == 0){
				// the focus has been matched. Now check the state
				// use the rule's state and current input class to check the decision table
				state_match = decision_table[current_state-1][current_rule->input_class-1];
				if (state_match){
					// the state has been matched. now check the right context
					if(current_rule->right_context[0] == '\0'){
						match_flag=1;
					}else{ /* a right context exists for the rule, check the braille input
							* right of the focus to see if a match occurs.*/
						strcpy(right_of_focus, "\0"); //initialise right_of_focus to NULL
						// ie. right_of_focus = braille_line from the end of the current focus
						strcpy(right_of_focus, &braille_line[strlen(current_rule->focus)]);
						/* match each right context character of the rule. if any one of the chars
						   in the right context does not match then rule can not fire */
						char_ctxt_match = 1;

						for (n=0; n<strlen(current_rule->right_context); n++){
							/* wild cards "!"= one or more of the set "&!(A)."
										  " "= anywhite space char (space \t \n...)
										  "~"= one or more roman letters (I, V, M, C)
										  ":"= zero or more punctuation chars
										  "-"= a space char								*/
							int i=0; // used for counting and removing characters
							switch (current_rule->right_context[n]){
								case '!': // one or more of the set "&!(A)."
									if (right_of_focus[0]=='&' || right_of_focus[0]=='!' || right_of_focus[0]=='(' || right_of_focus[0]=='A' || right_of_focus[0]==')'){
										while (right_of_focus[i]=='&' || right_of_focus[i]=='!' || right_of_focus[i]=='(' || right_of_focus[i]=='A' || right_of_focus[i]==')'){
											++i;
										}
										strcpy(right_of_focus, &right_of_focus[i]);
									} else {
										char_ctxt_match = 0;
									}
									break;
								case ' ': // anywhite space char (space \t \n...)
									/* if the frist character to the right of the focus is not a white space character
									   then set the chat_ctxt_match to false */
									if(isspace((int)right_of_focus[0]) || right_of_focus[0]=='\0'){
										// if there is white space or nothing to the right of the focus
										strcpy(right_of_focus, &right_of_focus[1]);
										// then remove the white space char from input to the right of the focus
									}else{
										char_ctxt_match = 0;
									}
									break;
								case '~': //one or more roman letters (I, V, M, C)
									if (right_of_focus[0]!='I' && right_of_focus[0]!='V' && right_of_focus[0]!='M' && right_of_focus[0]!='C'){
										char_ctxt_match = 0;
									}else{//remove the roman letter for the input to the right of the focus
										while (right_of_focus[i]=='I' || right_of_focus[i]=='V' || right_of_focus[i]=='M' || right_of_focus[i]=='C'){
											++i;
										}
										strcpy(right_of_focus, &right_of_focus[i]);
									}
									break;
								case ':': //zero or more punctuation chars
									/* do not change the value of char_ctxt_match since there will always be
									   zero or more. the vale of char_ctxt_match must be preserved.*/

									//if there are punctuation characters remove them from the right of focus
									while ( ((ispunct((int)right_of_focus[i])|| isdigit((int)right_of_focus[i])))
											 && (right_of_focus[i] != '5')
											 && (right_of_focus[i] != ']')
											 && (right_of_focus[i] != '[')){
										++i;
									}
									strcpy(right_of_focus, &right_of_focus[i]);
									break;
								case '_': //a space char
									//set char_ctxt_match to false if the char immediately right of the focus is not a space
									if(right_of_focus[0] != ' '){
										char_ctxt_match = 0;
									}else{//remove the space char from input to the right of the focus
										strcpy(right_of_focus, &right_of_focus[1]);
									}
									break;
								default: // right context may be a particular letter
									if ( current_rule->right_context[n]>='A' && current_rule->right_context[n]<='Z'){
										// char is an alpha and hence requires a literal match
										if ( right_of_focus[0] == current_rule->right_context[n]){
											//right context matches rule for a particular alpha char
											strcpy(right_of_focus, &right_of_focus[1]);
										} else {
											char_ctxt_match = 0;
										}
									} else {
										char_ctxt_match = 0;
									}
							}
						}// end for statement...for each char of rule's right context

						if (char_ctxt_match == 1){
							// the right context has been matched so set the match flag
							match_flag = 1;
						}
					}
				}
			}
			if(!match_flag){
				//if a match has not been found move to the next rule in the linked list
				current_rule = current_rule->next;
			}
		}
		//a rule has been matched OR the end of the linked list has been reached

 		if (match_flag){
			// a match has been found the rule should fire.
			// update the output, the new state and remove the focus from the input braille word

			//update the state
			current_state = current_rule->next_state;

			//copy the rules output to another string so that it can be capitalised etc
			strcpy(new_out, current_rule->output);

			// if the output is not an alpha letter then we are at the end of a
			// word and must set the shift_word flag to false.
			if(!isalpha(current_rule->output[0])){
				shift_word=0;
			}


			//capitalise the first letter or the whole word depending on the flags
			if(shift_char && isalpha(current_rule->output[0])){
				new_out[0]=new_out[0]-32; //subtract 32 from a letter to capitalise it
				shift_char=0; //set flag to false
			} else if (shift_word){
				//capitalise all letters in the output
				int j=0;
				for (j=0; j<strlen(new_out); j++){ //for all letters in the new output
					if(isalpha(new_out[j])){
						new_out[j]=new_out[j]-32; //subtract 32 from a letter to capitalise it
					}
				}
			}


			//set capitalisation flags or append new output to translated line
			if(!strcmp(current_rule->output, "<SHIFT_CHAR>")){
				shift_char=1;
			}else if(!strcmp(current_rule->output, "<SHIFT_WORD>")){
				shift_word=1;
			}else{
				//concatenate the new output to the translated line
				strcpy(print_line, strcat(print_line, new_out));
			}


			//remove the focus from the input braille word
			int i=0;
			for (i=0; i<=strlen(braille_line); i++){
				braille_line[i]=braille_line[focus_length+i];
			}
		}else{
/**/		//commented text may be used for debugging new rules
			//printf("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
			//printf("!!!The translation has exited without a match being found!!!\n");
			//printf("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
			//printf("the input was:%s.\n", braille_line);
			//printf("the state was:%d.\n", current_state);
			//strcpy(print_line, strcat(print_line, "\n\n\n\n\n!!!!!!!!!!!!!!!\n!!!!!!!!!!!!!!!\n\n\n\n\n"));

			/*append ERROR to the output. this may be changed to append the character which
			is causing the error to the output. ie strcat(print_line, braille_line[0])*/
			strcpy(print_line, strcat(print_line, "\n\n\nERROR\n\n\n"));
			//remove the first character from the input
			int i=0;
			for (i=0; i<=strlen(braille_line); i++){
				braille_line[i]=braille_line[i+1];
			}
		}
	}

	//append a new line char to the end of the line.
	strcpy(print_line, strcat(print_line, "\n"));
}


@end
