#import "TextToBrailleController.h"

@implementation TextToBrailleController
- (void) awakeFromNib
{
	[NSApp setDelegate: self];
	/* For loading the previous text
	 * that has been saved */
	[self loadDataFromDisk:[self inputFileName]];
	[self loadDataFromDisk:[self outputFileName]];
	if ([self inputString] != NULL)
	{
		[inputText setString:[self inputString]];
		[asciiDisplay setString:[self output]];
		// Set display font to Braille
		NSFont *font = [NSFont fontWithName:@"Braille" size:0.0];
		[brailleOutput setFont:font];
		[brailleOutput setString:[self output]];
	}
}

- (id)init
{
	NSLog(@"Initialising...");
	[super init];
	[inputText setDelegate:self];
	[asciiDisplay setDelegate:self];
	[self setInputFileName:@"inputFile.txt"];
	[self setOutputFileName:@"output.brf"];
	return self;
}

/* Method used to decode data to be loaded. */
- (id) initWithCoder:(NSCoder *) coder
{
	if (self = [super init])
	{
		[self setInputString:[coder decodeObjectForKey:@"input"]];
		[self setOutput:[coder decodeObjectForKey:@"output"]];
	}
	return self;
}

/* Method to encode data to be saved. */
- (void) encodeWithCoder: (NSCoder *) coder
{
	[coder encodeObject:inputString forKey:@"input"];
	[coder encodeObject:output forKey:@"output"];
}

/* Method to set the complete path of the file to be written. */
- (NSString *) pathForDataFile:(NSString *) fileName
{
	NSFileManager *fileManager = [NSFileManager defaultManager];
	
	NSString *folder = @"~/try";
	folder = [folder stringByExpandingTildeInPath];
	
	// Create a folder if it has not exist yet.
	if ([fileManager fileExistsAtPath: folder] == NO)
	{
		[fileManager createDirectoryAtPath: folder attributes: nil];
	}
	// Combine the folder location and the file name to get the complete path.
	return [folder stringByAppendingPathComponent:fileName];
}

/* Method to save or archive a string object with specified file name */
- (void) saveDataToDisk:(NSString *) stringObject withFileName:(NSString *) aFile
{
	NSString *path = [self pathForDataFile:aFile];
	//NSData *stringData;
	
	// Save the string object as dictionary or data.
	NSMutableDictionary *rootObject;
	rootObject = [NSMutableDictionary dictionary];
	
	if ([stringObject isEqualToString:[self inputString]])
	{
		[rootObject setValue:stringObject forKey:@"input"];
	}
	
	else if ([stringObject isEqualToString:[self output]])
	{
		[rootObject setValue:stringObject forKey:@"output"];
	}
	[NSKeyedArchiver archiveRootObject: rootObject toFile: path];
	
	//[NSKeyedArchiver archiveRootObject: [inputText string] toFile: path];
	
	//stringData = [NSKeyedArchiver archivedDataWithRootObject:stringObject];
	//[stringData writeToFile:path atomically:YES];
}

/* Method to load or unarchive data from specified file. This method works.
* It is proven that the file is stored in the corespond variables.
* For example, input text is loaded and stored in variable inputString */
- (void) loadDataFromDisk:(NSString *) fileName
{
	NSString *path = [self pathForDataFile:fileName];
	NSDictionary *rootObject;
	
	rootObject = [NSKeyedUnarchiver unarchiveObjectWithFile: path];
	
	// Save the unarchived data to relevant instance variable and print it out.
	if ([fileName isEqualToString:[self inputFileName]])
	{
		[self setInputString: [rootObject valueForKey:@"input"]];
		NSLog(@"input variable %@",[self inputString]);
	}
	else if ([fileName isEqualToString:[self outputFileName]])
	{
		[self setOutput: [rootObject valueForKey:@"output"]];
		NSLog(@"output variable %@",[self output]);
	}
}

/* Accessor method to change the inputFileName (mutator method in Java) */
- (void) setInputFileName:(NSString *) anInputFileName
{
	[anInputFileName retain];
	[inputFileName release];
	inputFileName = anInputFileName;
}

/* Accessor method to get inputFileName */
- (NSString *) inputFileName
{
	return inputFileName;
}

/* Accessor method to change the outputFileName (mutator method in Java) */ 
- (void) setOutputFileName:(NSString *) anOutputFileName
{
	[anOutputFileName retain];
	[outputFileName release];
	outputFileName = anOutputFileName;
}

/* Accessor method to get outputFileName */
- (NSString *) outputFileName
{
	return outputFileName;
}

/* Accessor method to change the inputString (mutator method in Java) */
- (void) setInputString:(NSString *) anInput
{
	[anInput retain];
	[inputString release];
	inputString = anInput;
}

/* Accessor method to get inputString */
- (NSString *) inputString
{
	return inputString;
}

/* Accessor method to change the output variable string (mutator method in Java) */
- (void) setOutput:(NSString *) anOutput
{
	[anOutput retain];
	[output release];
	output = anOutput;
}

/* Accessor method to get output variable string */
- (NSString *) output
{
	return output;
}

/* A delegate method. It saves the input and output string to relevant file when
* the application is terminated. */
- (void) applicationWillTerminate: (NSNotification *) note
{
	NSString *inFile = @"~/try/inputFile.text";
	NSString *outFile = @"~/try/output.brf";
	[self saveDataToDisk:[self inputString] withFileName:inFile];
	[self saveDataToDisk:[self output] withFileName:outFile];
}

/* A delegate method. This method is is executed when the text in NSTextView
* object has changed. Text in inputText NSTextView is translated everytime
* inputText NSTextView receives a space character and then it displays
* the translated result as Braille in Braille output NSTextView and 
* ASCII output the ASCII output NSTextView. */
- (void) textDidChange:(NSNotification *)aNotification
{
	// Local variables
	char input_string[INPUT_SIZE] = "\0";
	char out_string[OUTPUT_SIZE] = "\0";
		
	BOOL isLonger = YES;
	NSString *words;
	NSString *aSubString;
	NSString *tempString;
	NSString *wordTemp = @"";
	int start_index = 0;
	NSRange range;
	//NSString *prevWords;
	//prevWords = [self output];
	
	[self setInputString:[inputText string]];
	
	/* If there is text in inputText NSTextView; This is
	 * to avoid error when the first word is typed in NSTextView
	 * but it is deleted before the space character */
	if ([[self inputString] length] != 0)
	{
		// Get the last character of the sentence
		unichar inputCharacter = [[self inputString] characterAtIndex:[[self inputString] length]-1];
		
		/* If the notification applies to inputText NSTextView;
		* This condition to ensure that Grade 2 Braille translation
		* only applies to string in inputText NSTextView while
		* asciiDisplay NSTextView is editable and it is direct display
		* as Braille. */
		if([aNotification object] == inputText)
		{
			// Everytime space character is detected
			if (inputCharacter == ' ')
			{
				words = [inputText string];
				while (isLonger)
				{
					range.location = start_index;
					if (range.location + 200 > [words length])
					{
						range.length = [words length] - range.location;
						isLonger = NO;
					}					
					else
					{
						range.length = 200;	
					}
					aSubString = [words substringWithRange:range];
					int end = [self findLastSpace:aSubString];
					range.length = end;
					start_index += end;
					aSubString = [words substringWithRange:range];
					strcpy(input_string,[aSubString cStringUsingEncoding:NSUTF8StringEncoding]);
					strcpy(out_string,[brailleTranslator doTranslation:input_string]);
					//printf("The translated result is %s\n",out_string);
					const char *wordConstChar = out_string;
					tempString = [NSString stringWithCString:wordConstChar encoding:NSUTF8StringEncoding];
					wordTemp = [wordTemp stringByAppendingString:tempString];
					wordTemp = [wordTemp stringByAppendingString:@" "];
					[self setOutput:wordTemp];
				};
				/* Convert the string object to C char array then pass it as argument to translator
				* object. The result of the translation is a C char array. This C char array is
				* then converted back to string object to be displayed as Braille in Braille output
				* NSTextView and ASCII code in asciiDisplay NSTextView. */
				[asciiDisplay setString:output];
			}
		}
		
		else if ([aNotification object] == asciiDisplay)
		{
			[self setOutput:[asciiDisplay string]];
		}
	}
	
	// Set display font to Braille
	NSFont *font = [NSFont fontWithName:@"Braille" size:0.0];
	[brailleOutput setFont:font];
	
	// Display what was translated as Braille
	[brailleOutput setString:[asciiDisplay string]];
	
	// archive string data to disk to relevant file
	[self saveDataToDisk:[self inputString] withFileName:inputFileName];
	[self saveDataToDisk:[self output] withFileName:outputFileName];
}
-(int) findLastSpace:(NSString *) aString
{
	int anIndex;
	int i;
	for (i = 0; i < [aString length]; i++)
	{
		if ([aString characterAtIndex:i] == ' ')
		{
			anIndex = i;
		}
	
	}
	return anIndex;
}

- (IBAction)clearInput:(id)sender
{
	[inputText setString:@""];
}

- (IBAction)saveFile:(id)sender
{
	NSRange brailleRange;
	NSData *brailleData;
	
	// Save the input text and ascii output to a file.
	NSString *pathInputFile = @"/inputFile.txt";
	NSString *pathOutputFile = @"/outputFile.brf";
	NSString *brailleOutputFile = @"/braille.rtf";
	
	[[inputText string] writeToFile:pathInputFile atomically:YES];
	[[asciiDisplay string] writeToFile:pathOutputFile atomically:YES];
	
	brailleRange.location = 0;
	brailleRange.length = [[brailleOutput string] length]-1;
	brailleData = [brailleOutput RTFFromRange:brailleRange];
	[brailleData writeToFile:brailleOutputFile atomically:YES];
}

- (IBAction)translateToBraille:(id)sender
{
	// Local variables
	NSString *newLine = @"\n";
	NSString *tab = @"\t";
	NSString *space = @" ";
	NSArray *paragraphArray;
	NSArray *tabArray;
	NSArray *wordArray;
	NSString *wordTemp;
	NSString *outputString = @"";
	
	unsigned paragraphArrayIndex;
	unsigned tabArrayIndex;
	unsigned indx;
	
	// Initialise array of chars.
	char input_string[INPUT_SIZE] = "\0";
	char out_string[OUTPUT_SIZE] = "\0";
	char input_string_copy[WORD_SIZE] = "\0";
	char next_word[WORD_SIZE] = "\0";
	
	[self setInputString:[inputText string]];
	// Break down string object into an array of paragraph if there are multiple paragraphs.
	paragraphArray = [[inputText string] componentsSeparatedByString:newLine];
	
	for (paragraphArrayIndex = 0; paragraphArrayIndex < [paragraphArray count]; paragraphArrayIndex++)
	{
		// Break down string object into an array of tab section if there are multiple tab sections.
		tabArray = [[paragraphArray objectAtIndex:paragraphArrayIndex] componentsSeparatedByString:tab];
		
		for (tabArrayIndex = 0; tabArrayIndex < [tabArray count]; tabArrayIndex++)
		{
			// Break sentences into an array of words.
			wordArray = [[tabArray objectAtIndex:tabArrayIndex] componentsSeparatedByString:space];
			
			indx = 0;
			
			while (indx < [wordArray count])  // for each word
			{
				// convert to C string
				strcpy(input_string,[[wordArray objectAtIndex:indx] cStringUsingEncoding:NSUTF8StringEncoding]);
				
				// Check if the word is word required single word contraction and not the last word of a sentence.
				if (((strcmp(input_string,"to") == 0) || (strcmp(input_string,"by") == 0) || (strcmp(input_string,"into") == 0) || (strcmp(input_string,"To") == 0) || (strcmp(input_string,"By") == 0) || (strcmp(input_string,"Into") == 0)) && (indx+1 < [wordArray count]))
				{
					// If single word contraction and not end of sentence, combine the words.
					strcpy(input_string_copy,input_string);
					strcpy(next_word,[[wordArray objectAtIndex:indx+1] cStringUsingEncoding:NSUTF8StringEncoding]);
					strcat(input_string," ");
					strcat(input_string,next_word);
					indx++;		// increment the index to avoid same word to be translated.
				}
				printf("The value of input_string is: %s\n",input_string);
				strcpy(out_string,[brailleTranslator doTranslation:input_string]);  // translate the word(s).
				const char *wordConstChar = out_string;
				// Convert back to NSString object
				wordTemp = [NSString stringWithCString:wordConstChar encoding:NSUTF8StringEncoding];
				outputString = [outputString stringByAppendingString:wordTemp]; // Combine words
				outputString = [outputString stringByAppendingString:space]; // Separated by space
				indx++;  // go to the next word.
			}
			if ( tabArrayIndex < [tabArray count]-1) // append the tab except after the last part
			{
				outputString = [outputString stringByAppendingString:tab];
			}
		}
		// append the tab except after the last part
		if (paragraphArrayIndex < [paragraphArray count]-1)
		{
			outputString = [outputString stringByAppendingString:newLine];
		}
	}
	
	//NSLog(@"The complete translated string in NSString object is %@",outputString);
	
	[asciiDisplay setString:outputString];  // Display the ASCII code to be sent to embosser.
	[self setOutput:outputString];
		
	// Set display font to Braille
	NSFont *font = [NSFont fontWithName:@"Braille" size:0.0];
	[brailleOutput setFont:font];	
	
	// Display what was typed translated as Braille
	[brailleOutput setString:[asciiDisplay string]];
	
	// Save or archive data of string to relevant file using NSCoder
	[self saveDataToDisk:[self inputString] withFileName:inputFileName];
	[self saveDataToDisk:[self output] withFileName:outputFileName];
}

/*- (IBAction)saveFileAs:(id)sender
{
	NSArray *fileTypes = 
	NSSavePanel *panel = [NSSavePanel savePanel];
	[panel setAllowedFileTypes:
	[panel beginSheetForDirectory:nil
							 file:nil
				   modalForWindow:[self window]
					modalDelegate:self
				   didEndSelector:@selector(didEnd:returnCode:contextInfo:)
					  contextInfo:NULL];
}

- (void)didEnd:(NSSavePanel *)sheet
	returnCode:(int)code
   contextInfo:(void *)contextInfo
{
	NSData *data;
	if (code == NSOKButton)
	{
		[data writeToFile:[sheet fileName] atomically:YES];
	}
}*/

- (void) dealloc
{
	NSLog(@"Dealloc...");
	[super dealloc];
}
@end