/*
iPhly: Radio Control on the iPhone

Copyright (C) 2010-2011 Ari Krupnik & Associates

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License V2
as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#import "ModelProcessor.h"
#import "NSMutableDictionary+ModelConfig.h"
#import "ModelProcessorChannel.h"
#import "ppm_audio_out.h"


NSString * const MODEL_CONFIG_FILE_EXTENSION = @"aero";
NSString * const MODEL_IMG_FILE_EXTENSION = @"jpg";


@implementation ModelProcessor

+ (NSString *) modelsDir {
	NSString *documentsDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
	return [documentsDir stringByAppendingPathComponent: @"models"];
}

+ (NSArray *) allModelPaths {
	NSMutableArray *modelPaths = [[NSMutableArray alloc] init];
	for (NSString *fileName in [[NSFileManager defaultManager] contentsOfDirectoryAtPath: [ModelProcessor modelsDir] error: NULL]) {
		if ([[fileName pathExtension] isEqualToString:MODEL_CONFIG_FILE_EXTENSION]) {
			[(NSMutableArray *) modelPaths addObject: [[ModelProcessor modelsDir] stringByAppendingPathComponent: fileName]];
		}
	}
	[modelPaths autorelease];
	return modelPaths;
}



- (ModelProcessor *) initWithFilePath: (NSString *) path {
    self = [super init];
    if ( self ) {
		filePath = path;
		[filePath retain];
		data = [[NSMutableDictionary alloc] initWithContentsOfFile: filePath];
		
		if (![data numChannels]) {
			// file doesn't contain valid information
			return nil;
		}
		
		accelerometerSensitivity = [data accelerometerSensitivity];
		
		channels = [[NSMutableArray alloc] init];
		
		for (int i=0; i<[data numChannels]; i++) {
			ModelProcessorChannel *pc = [[ModelProcessorChannel alloc] initWithProcessor: self andOrd:i];
			[channels addObject: pc];
			[pc release];
		}
	}
	return self;
}

- (OSStatus) startTransmission {
	// 8 is a kludge for 2-servo wings and FPV head trackers
	//ppm_audio_out_start([self numChannels]);
	return ppm_audio_out_start(8);
}

- (OSStatus) stopTransmission {
    return ppm_audio_out_stop();
}

- (NSString *) name {
	return [[filePath lastPathComponent] stringByDeletingPathExtension];
}

NSString *pathForNewModelWithName (NSString *newName, NSString **errorMessage) {
	if ([newName length] == 0) {
		*errorMessage = @"Model name cannot be empty";
		return nil;
	}
	if ([newName isEqualToString:@"."] || [newName isEqualToString:@".."]) {
		*errorMessage = @"Model name cannot be '.' or '..'";
		return nil;
	}
	if ([newName rangeOfString:@"/"].location != NSNotFound) {
		*errorMessage = @"Model name cannot contain '/'";
		return nil;
	}
	NSString *newPath = [[[ModelProcessor modelsDir] stringByAppendingPathComponent:newName] stringByAppendingPathExtension:MODEL_CONFIG_FILE_EXTENSION];
	NSFileManager *fm = [NSFileManager defaultManager];
	if ([fm fileExistsAtPath:newPath]) {
		*errorMessage = @"A model with this name already exists";
		return nil;
	}
	return newPath;
}

- (NSString *) rename: (NSString *) newName {
	if ([newName isEqualToString: [self name]]) {
		// return silently if new name is same as old
		return nil;
	}
	NSString *error;
	NSString *newPath = pathForNewModelWithName(newName, &error);
	if (!newPath) {
		return error;
	}
	if ([[NSFileManager defaultManager] moveItemAtPath:filePath toPath:newPath error:NULL]) {
		NSString *oldImgPath = [self imgPath];
		[filePath release];
		filePath = newPath;
		[filePath retain];
		NSString *newImgPath = [self imgPath];
		[[NSFileManager defaultManager] moveItemAtPath:oldImgPath toPath:newImgPath error:NULL];
		return nil;
	} else {
		return @"Cannot rename model";
	}
}
- (NSString *) copy: (NSString *) newName {
	NSString *error;
	NSString *newPath = pathForNewModelWithName(newName, &error);
	if (!newPath) {
		return error;
	}
	if ([[NSFileManager defaultManager] copyItemAtPath:filePath toPath:newPath error:NULL]) {
		return nil;
	} else {
		return @"Cannot create model";
	}
}

- (BOOL) delete {
	return [[NSFileManager defaultManager] removeItemAtPath:filePath error:NULL];
}

- (BOOL) isAirplane {
	return [data isAirplane];
}
- (BOOL) isCar {
	return [data isCar];
}

- (int) numChannels {
	return [channels count];
}
- (ModelProcessorChannel *) channelAtIndex:(int) i {
	return [channels objectAtIndex: i];
}
- (ModelProcessorChannel *) channelByName:(NSString *) name {
	for (ModelProcessorChannel *c in channels) {
		if ([name isEqualToString: [c name]]) {
			return c;
		}
	}
	return nil;
}

- (void) changeOutputOrderForChannel: (int) from to: (int) to {
	ModelProcessorChannel *c = [[channels objectAtIndex:from] retain];
	[channels removeObjectAtIndex:from];
	[channels insertObject:c atIndex:to];
	[c release];
	
	// this is a kludge, having to do this twice. i want to refactor the entire persistance mechanism
	NSMutableArray *persistentChannles = (NSMutableArray *) [data objectForKey: @"channels"];
	id pc = [[persistentChannles objectAtIndex:from] retain];
	[persistentChannles removeObjectAtIndex:from];
	[persistentChannles insertObject:pc atIndex:to];
	[pc release];
	
	int i = 0;
	for (ModelProcessorChannel *c in channels) {
		[c setOrd: i++];
	}
	[self writeToFile];
}

- (void) setAccelerometerSensitivity:(float) n {
	accelerometerSensitivity = n;
	[data setAccelerometerSensitivity:n];
	[self writeToFile];
}

@synthesize filePath;

@synthesize accelerometerSensitivity;


- (NSMutableDictionary *) __channelDataAtIndex:(int) i {
	return [data channel:i];
}

- (BOOL) writeToFile {
	return [data writeToFile: filePath atomically: YES];
}

- (NSString *) imgPath {
	return [[filePath stringByDeletingPathExtension] stringByAppendingPathExtension:MODEL_IMG_FILE_EXTENSION];
}
- (UIImage *) image {
	return [UIImage imageWithContentsOfFile:[self imgPath]];
}
- (void) setImage:(UIImage *) image {
	if (image) {
		[UIImageJPEGRepresentation(image, 0.85) writeToFile:[self imgPath] atomically:YES];
	} else {
		[[NSFileManager defaultManager] removeItemAtPath:[self imgPath] error:NULL];
	}
}

- (void) dealloc {
	[self stopTransmission];
	[channels release];
	[data release];
	
	[filePath release];
	
	[super dealloc];
}

@end