//
//  BCParseSOFT.m
//  BioCocoa
//
//  Created by Scott Christley on 10/07/08.
//  Copyright (c) 2003-2009 The BioCocoa Project.
//  All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions
//  are met:
//  1. Redistributions of source code must retain the above copyright
//  notice, this list of conditions and the following disclaimer.
//  2. Redistributions in binary form must reproduce the above copyright
//  notice, this list of conditions and the following disclaimer in the
//  documentation and/or other materials provided with the distribution.
//  3. The name of the author may not be used to endorse or promote products
//  derived from this software without specific prior written permission.
//
//  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
//  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
//  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
//  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
//  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
//  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
//  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
//  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
//  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
//  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#import "BCParseSOFT.h"
#import "BCSeries.h"
#import "BCPlatform.h"
#import "BCSample.h"

@implementation BCParseSOFT

- init
{
	self = [super init];
	if (self) {
		series = [NSMutableArray new];
		platforms = [NSMutableArray new];
		samples = [NSMutableArray new];
	}
	return self;
}

- (void)dealloc
{
	if (series) [series release];
	if (platforms) [platforms release];
	if (samples) [samples release];
	[super dealloc];
}

//
// Parse gene expression data in SOFT format
//
- (BOOL)parseFile: (NSString *)fileName
{
	return [self parseFile: fileName headersOnly: NO];
}

- (BOOL)parseFile: (NSString *)fileName headersOnly: (BOOL)flag
{
	NSError *e;
	NSAutoreleasePool *pool = [NSAutoreleasePool new];
	NSString *s = [NSString stringWithContentsOfFile: fileName encoding: NSUTF8StringEncoding error: &e];
	
	if (!s) {
		printf("Could not open file: %s\n", [fileName UTF8String]);
		printf("%s\n", [[e localizedDescription] UTF8String]);
		//NSRunAlertPanel(@"File Open Error", [e localizedDescription], nil, nil, nil);
		goto failure;
	}
	
	NSArray *a = [s componentsSeparatedByString: @"\n"];
	//printf("%d\n", [a count]);
	
	int i;
	int parseState = 0;
	NSString *seriesName, *platformName, *sampleName;
	BCSeries *aSeries = nil;
	BCPlatform *aPlatform = nil;
	BCSample *aSample = nil;
	NSMutableArray *probeSet = nil, *sampleSet = nil;
	NSArray *probeSetColumns = nil, *sampleSetColumns = nil;
	NSCharacterSet *trimSet = [NSCharacterSet whitespaceAndNewlineCharacterSet];
	for (i = 0; i < [a count]; ++i) {
		//	for (i = 0; i < 10000; ++i) {
		NSString *s = [a objectAtIndex: i];
		if (!s) continue;
		if ([s length] == 0) continue;
		
		//printf("Starting parse of line (state %d):\n", parseState);
		//printf("%s\n", [s UTF8String]);
		
		if ([s characterAtIndex: 0] == '^') {
			
			// Series data
			NSRange r = [s rangeOfString: @"^SERIES"];
			if (r.location != NSNotFound) {
				r = [s rangeOfString: @"="];
				if (r.location == NSNotFound) {
					printf("ERROR");
					goto failure;
				} else {
					++r.location;
					r.length = [s length] - r.location;
					seriesName = [[s substringWithRange: r] stringByTrimmingCharactersInSet: trimSet];
					//printf("series: %s\n", [seriesName UTF8String]);
					
					// check if we already have this series data loaded
					aSeries = [BCSeries seriesWithId: seriesName];
					if (!aSeries) {
						aSeries = [[BCSeries alloc] initWithId: seriesName];
						[BCSeries addSeries: aSeries];
						parseState = 1;
					} else parseState = 0;
				}
				continue;
			}
			
			// Platform data
			r = [s rangeOfString: @"^PLATFORM"];
			if (r.location != NSNotFound) {
				r = [s rangeOfString: @"="];
				if (r.location == NSNotFound) {
					printf("ERROR");
					goto failure;
				} else {
					++r.location;
					r.length = [s length] - r.location;
					platformName = [[s substringWithRange: r] stringByTrimmingCharactersInSet: trimSet];
					//printf("platform: %s\n", [platformName UTF8String]);
					
					// check if we already have this platform loaded
					aPlatform = [BCPlatform platformWithId: platformName];
					if (!aPlatform) {
						aPlatform = [[BCPlatform alloc] initWithId: platformName];
						[BCPlatform addPlatform: aPlatform];
						parseState = 2;
					} else parseState = 0;
				}
				continue;
			}
			
			// Sample data
			r = [s rangeOfString: @"^SAMPLE"];
			if (r.location != NSNotFound) {
				r = [s rangeOfString: @"="];
				if (r.location == NSNotFound) {
					printf("ERROR");
					goto failure;
				} else {
					++r.location;
					r.length = [s length] - r.location;
					sampleName = [[s substringWithRange: r] stringByTrimmingCharactersInSet: trimSet];
					//printf("sample: %s\n", [sampleName UTF8String]);
					
					// check if we already have this platform loaded
					aSample = [BCSample sampleWithId: sampleName];
					if (!aSample) {
						aSample = [[BCSample alloc] initWithId: sampleName];
						[BCSample addSample: aSample];
						parseState = 4;
					} else parseState = 0;
				}
				continue;
			}
		}
		
		switch (parseState) {
				// looking for next section
			case 0: break;
				
				// parsing series data
			case 1: {
				NSRange r1 = [s rangeOfString: @"="];
				NSRange r2;
				r2.length = r1.location - 1;
				r2.location = 1;
				NSString *key = [[s substringWithRange: r2] stringByTrimmingCharactersInSet: trimSet];
				r2.location = r1.location + 1;
				r2.length = [s length] - r2.location;
				NSString *value = [[s substringWithRange: r2] stringByTrimmingCharactersInSet: trimSet];
				//printf("key: %s\n", [key UTF8String]);
				//printf("value: %s\n", [value UTF8String]);
				[aSeries setValue: value forKey: key];
				break;
			}
				
				// parsing platform data
			case 2: {
				// beginning of platform probe set entries
				NSRange r = [s rangeOfString: @"!platform_table_begin"];
				if (r.location != NSNotFound) {
					++i;  // skip the first entry of headers
					if (!flag) {
						probeSet = [NSMutableArray array];
						[aPlatform setValue: probeSet forKey: @"Probe Set"];
						probeSetColumns = [aPlatform valuesForKey: @"Probe Set Columns"];
					}
					parseState = 3;
					continue;
				}
				
				// platform header entries
				if ([s characterAtIndex: 0] == '!') {
					NSRange r1 = [s rangeOfString: @"="];
					NSRange r2;
					r2.length = r1.location - 1;
					r2.location = 1;
					NSString *key = [[s substringWithRange: r2] stringByTrimmingCharactersInSet: trimSet];
					r2.location = r1.location + 1;
					r2.length = [s length] - r2.location;
					NSString *value = [[s substringWithRange: r2] stringByTrimmingCharactersInSet: trimSet];
					//printf("key: %s\n", [key UTF8String]);
					//printf("value: %s\n", [value UTF8String]);
					[aPlatform setValue: value forKey: key];
					continue;
				}
				
				// platform probe set headers
				if ([s characterAtIndex: 0] == '#') {
					NSRange r1 = [s rangeOfString: @"="];
					NSRange r2;
					r2.length = r1.location - 1;
					r2.location = 1;
					NSString *key = [[s substringWithRange: r2] stringByTrimmingCharactersInSet: trimSet];
					r2.location = r1.location + 1;
					r2.length = [s length] - r2.location;
					NSString *value = [[s substringWithRange: r2] stringByTrimmingCharactersInSet: trimSet];
					//printf("key: %s\n", [key UTF8String]);
					//printf("value: %s\n", [value UTF8String]);
					[aPlatform setValue: key forKey: @"Probe Set Columns"];
					[aPlatform setValue: value forKey: @"Probe Set Column Descriptions"];
					continue;
				}
				
				break;
			}
				
				// parsing platform probe set entries
			case 3: {
				// end of platform probe set entries
				NSRange r = [s rangeOfString: @"!platform_table_end"];
				if (r.location != NSNotFound) {
					probeSet = nil;
					probeSetColumns = nil;
					parseState = 0;
					continue;
				}
				
				if (!flag) {
					NSArray *f = [s componentsSeparatedByString: @"\t"];
					if ([f count] != [probeSetColumns count])
						NSLog(@"ERROR: probe set columns %d != %d entries\n", [probeSetColumns count], [f count]);
					[probeSet addObject: f];
					//[probeSet setObject: f forKey: [f objectAtIndex: 0]];
				}
				
				break;
			}
				
				// parsing sample data
			case 4: {
				// beginning of sample data entries
				NSRange r = [s rangeOfString: @"!sample_table_begin"];
				if (r.location != NSNotFound) {
					++i;  // skip the first entry of headers
					if (!flag) {
						sampleSet = [NSMutableArray array];
						[aSample setValue: sampleSet forKey: @"Sample Data"];
						sampleSetColumns = [aSample valuesForKey: @"Sample Data Columns"];
					}
					parseState = 5;
					continue;
				}
				
				// sample header entries
				if ([s characterAtIndex: 0] == '!') {
					NSRange r1 = [s rangeOfString: @"="];
					NSRange r2;
					r2.length = r1.location - 1;
					r2.location = 1;
					NSString *key = [[s substringWithRange: r2] stringByTrimmingCharactersInSet: trimSet];
					r2.location = r1.location + 1;
					r2.length = [s length] - r2.location;
					NSString *value = [[s substringWithRange: r2] stringByTrimmingCharactersInSet: trimSet];
					//printf("key: %s\n", [key UTF8String]);
					//printf("value: %s\n", [value UTF8String]);
					[aSample setValue: value forKey: key];
					continue;
				}
				
				// sample data headers
				if ([s characterAtIndex: 0] == '#') {
					NSRange r1 = [s rangeOfString: @"="];
					NSRange r2;
					r2.length = r1.location - 1;
					r2.location = 1;
					NSString *key = [[s substringWithRange: r2] stringByTrimmingCharactersInSet: trimSet];
					r2.location = r1.location + 1;
					r2.length = [s length] - r2.location;
					NSString *value = [[s substringWithRange: r2] stringByTrimmingCharactersInSet: trimSet];
					//printf("key: %s\n", [key UTF8String]);
					//printf("value: %s\n", [value UTF8String]);
					[aSample setValue: key forKey: @"Sample Data Columns"];
					[aSample setValue: value forKey: @"Sample Data Column Descriptions"];
					continue;
				}
				
				break;
			}
				
				// parsing sample data entries
			case 5: {
				// end of sample data entries
				NSRange r = [s rangeOfString: @"!sample_table_end"];
				if (r.location != NSNotFound) {
					sampleSet = nil;
					sampleSetColumns = nil;
					parseState = 0;
					continue;
				}
				
				if (!flag) {
					NSArray *f = [s componentsSeparatedByString: @"\t"];
					if ([f count] != [sampleSetColumns count])
						NSLog(@"ERROR: sample data columns %d != %d entries\n", [sampleSetColumns count], [f count]);
					[sampleSet addObject: f];
					//NSLog(@"%d\n", [sampleSet count]);
					//[sampleSet setObject: f forKey: [f objectAtIndex: 0]];
				}
				
				break;
			}
				
		}
	}
	
	[pool release];
	return YES;
	
failure:
	[pool release];
	return NO;
}

//
// Retrieve parsed data
//
- (NSArray *)getSeries { return [NSArray arrayWithArray: series]; }
- (NSArray *)getPlatforms { return [NSArray arrayWithArray: platforms]; }
- (NSArray *)getSamples { return [NSArray arrayWithArray: samples]; }

@end
