//
//  Controller.m
//  HR-WorkLoad
//
//  Created by Julian on 1/22/10.
//  Copyright 2010 Julian Yu-Chung Chen. All rights reserved.
//

#import "Controller.h"
#import <CorePlot/CorePlot.h>
#import "YCPlotDataSource.h"

@implementation Controller

-(void)dealloc 
{
	[dataObjects release];
    [graph release];
    [plotColors release];
	[xLabels release];
	
    [super dealloc];
}

	// Generate default data (sine wave: y = 3 * sin( x / 5.0 ) + 3;
-(NSMutableArray *) generateDefaultData:(int) size
{
	NSMutableArray *newData = [NSMutableArray array];
	NSUInteger x;
	for ( x = 0; x <= size; ++x ) {
		id y = [NSDecimalNumber numberWithFloat:( 3 * sin( x / 5.0 ) + 3 )];
		
		[newData addObject:
		  [NSDictionary dictionaryWithObjectsAndKeys:
		  [NSDecimalNumber numberWithFloat:x], [NSNumber numberWithInt:CPScatterPlotFieldX], 
										    y, [NSNumber numberWithInt:CPScatterPlotFieldY], 
		  nil]];
	}
	
	return newData;
}

	// Update and refresh plot
-(void)updateData:(NSMutableArray *) inputArray
{	
	if(dataObjects != nil) {
		[dataObjects release];
	}
	
	dataObjects = [inputArray retain];
}

-(void) initPlotElements
{
		// Assign brush detection delegate
	hostView.brushDelegate = self;

		// Create graph from theme
    graph = [[CPXYGraph alloc] initWithFrame:CGRectZero];
	CPTheme *theme = [CPTheme themeNamed:kCPStocksTheme];
		// CPTheme *theme = [CPTheme themeNamed:kCPDarkGradientTheme];
	[graph applyTheme:theme];
	
	    // Graph padding
    graph.paddingLeft   = 50.0;
    graph.paddingTop    = 10.0;
    graph.paddingRight  = 10.0;
    graph.paddingBottom = 80.0;
	
	hostView.hostedLayer = graph;
    
		// Setup scatter plot space
    CPXYPlotSpace *plotSpace = (CPXYPlotSpace *)graph.defaultPlotSpace;
	plotSpace.xRange = [CPPlotRange plotRangeWithLocation:CPDecimalFromFloat(0.0) length:CPDecimalFromFloat(10.0f)];
	plotSpace.yRange = [CPPlotRange plotRangeWithLocation:CPDecimalFromFloat(0.0) length:CPDecimalFromFloat(10.0f)];
	
		// Axes
	CPXYAxisSet *axisSet = (CPXYAxisSet *)graph.axisSet;
    CPXYAxis *x = axisSet.xAxis;
	x.majorIntervalLength = CPDecimalFromFloat(1.0f);	
		// FIXME x.constantCoordinateValue = CPDecimalFromString(@"0.0");
	x.minorTicksPerInterval = 0;
	x.labelRotation = M_PI * 0.25;
	x.title = @"Nurses";
	double halfRange = [[NSDecimalNumber decimalNumberWithDecimal:plotSpace.xRange.length] doubleValue] / 2.0;
	x.titleLocation = CPDecimalFromDouble(halfRange);
		
    CPXYAxis *y = axisSet.yAxis;
    y.majorIntervalLength = CPDecimalFromFloat(1.0f);
    y.minorTicksPerInterval = 0;
	
		// init plot colors array
	NSMutableArray *colors = [NSMutableArray array];
	[colors addObject:[CPColor greenColor]];
	[colors addObject:[CPColor grayColor]];
	[colors addObject:[CPColor redColor]];
	[colors addObject:[CPColor cyanColor]];
	[colors addObject:[CPColor yellowColor]];
	[colors addObject:[CPColor magentaColor]];
	[colors addObject:[CPColor brownColor]];
	[colors addObject:[CPColor whiteColor]];
	plotColors = [colors retain];
}

-(void)awakeFromNib
{
    [super awakeFromNib];
	
		// Update current time	
	NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
	[dateFormatter setDateStyle:NSDateFormatterMediumStyle];
	[dateFormatter setTimeStyle:NSDateFormatterMediumStyle];

	NSDate *now = [NSDate date];
	NSString *formattedDateString = [dateFormatter stringFromDate:now];	
	[currentTime setStringValue:formattedDateString];
	 
		// Prepare basic graph elements
	[self initPlotElements];	
}

#pragma mark -
#pragma mark IBAction implementation
- (IBAction) openInputCSVFile:(id)sender
{
	NSOpenPanel *panel = [NSOpenPanel openPanel];
	[panel setRequiredFileType:@"csv"];
	[panel setAllowsOtherFileTypes:NO];
	[panel setTreatsFilePackagesAsDirectories:YES];
	
	[panel
	 beginSheetForDirectory:nil
	 file:nil
	 modalForWindow:[self window]
	 modalDelegate:self
	 didEndSelector:@selector(openCSVDidEnd:returnCode:contextInfo:)
	 contextInfo:nil];
}

/* How to use threading to avoid locked UI.		 
 // Fill out the information needed to save
 NSDictionary * info = [NSDictionary dictionaryWithObjectsAndKeys:[width objectValue], kWidthKey, [height objectValue], kHeightKey, [panel URL], kLocationKey, nil];
 // And dispatch the operation to a background thread to avoid locking up the UI.
 // detachNewThreadSelector:toTarget:withObject: will retain it's parameters so we don't have to.
 // Equivalent to [self saveTo:info]
 [NSThread detachNewThreadSelector:@selector(saveTo:) toTarget:self withObject:info];
 */

	// Build a NSMutableArray of YCPlotDataSource
-(NSMutableArray *) openCSVFileFromURL:(NSURL *) url
{	
		// TODO: This might take some time if it's a big file. 
		//       Use the NSThread class above to put it in separate thread
	NSError *error;
	NSString *content = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error];
	
	if (content == nil) {
		[[NSAlert alertWithError:error] runModal];
		
		return nil;
	} else {
		NSArray *lines = [content componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
		int numOfCols = [[[lines objectAtIndex:0] componentsSeparatedByString:@","] count];
					
		NSMutableArray *dataCollections = [NSMutableArray array];		
		for ( int i = 0; i < (numOfCols - 1); ++i ) {
			NSMutableArray *data = [NSMutableArray array];
			[dataCollections addObject:data];
		}
		
		xLabels = [[NSMutableArray alloc] init];
		float min;
		float max;
		for ( int x = 1; x < [lines count]; ++x ) {	// Skip the 1st line for labels
			NSArray  *toks = [[lines objectAtIndex:x] componentsSeparatedByString:@","];
			if ([toks count] < numOfCols) {
				continue;
			}

			NSString *nurseId = [[toks objectAtIndex:0] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
			[xLabels addObject:nurseId];
			
			id xVal = [NSDecimalNumber numberWithFloat:x];						
			for( int y = 1; y < numOfCols; ++y ) { // 1st column is nurse ID, not a number
				id yVal = [NSDecimalNumber decimalNumberWithString:[toks objectAtIndex:y]];
		
				[[dataCollections objectAtIndex:(y - 1)] addObject:
					[NSDictionary dictionaryWithObjectsAndKeys:
						xVal, [NSNumber numberWithInt:CPScatterPlotFieldX], 
						yVal, [NSNumber numberWithInt:CPScatterPlotFieldY], 
						nil]];				
				
				float _y = [[toks objectAtIndex:y] floatValue];
				if(x == 1) {
					min = _y;
					max = _y;
				} else {
					if(_y > max) max = _y;
					if(_y < min) min = _y;						
				}								
			}						
		}
		
			// reset y-axis min/max
		CPXYPlotSpace *plotSpace = (CPXYPlotSpace *)graph.defaultPlotSpace;
		plotSpace.xRange = [CPPlotRange plotRangeWithLocation:CPDecimalFromFloat(0.0) length:CPDecimalFromFloat([lines count] - 1)];
		plotSpace.yRange = [CPPlotRange plotRangeWithLocation:CPDecimalFromFloat(min) length:CPDecimalFromFloat(max - min)];
		
			// reset axes ticks
		CPXYAxisSet *axisSet = (CPXYAxisSet *)graph.axisSet;
		CPXYAxis *x = axisSet.xAxis;
		x.majorIntervalLength = CPDecimalFromFloat(1.0f);	
			// FIXME x.constantCoordinateValue = CPDecimalFromString(@"0.0");
		x.minorTicksPerInterval = 0;		
			
			// Customize x-axis labels
		double halfRange = [[NSDecimalNumber decimalNumberWithDecimal:plotSpace.xRange.length] doubleValue] / 2.0;
		x.titleLocation = CPDecimalFromDouble(halfRange);
		x.titleOffset = 40;

			// Customized axis labels
		x.labelingPolicy = CPAxisLabelingPolicyNone;
		
		NSMutableArray *customTickLocations = [NSMutableArray array];
		NSMutableArray *customTickLabels    = [NSMutableArray array];
		
		for (int i = 1; i <= [xLabels count]; ++i) {
				// for ticks
			[customTickLocations addObject:[NSNumber numberWithInt:i]];			
			CPAxisLabel *l = [[CPAxisLabel alloc] initWithText:[xLabels objectAtIndex:(i - 1)] textStyle:x.labelTextStyle];

			l.tickLocation = [[customTickLocations objectAtIndex:(i - 1)] decimalValue];
			[l setRotation:(M_PI * 0.25)];
		    [l setOffset:7];
			[customTickLabels addObject:l];
			
			[l release];
		}		
		x.majorTickLocations = [NSSet setWithArray:customTickLocations];
		x.axisLabels = [NSSet setWithArray:customTickLabels];
		
		CPXYAxis *y = axisSet.yAxis;
		y.majorIntervalLength = CPDecimalFromFloat((max - min)/10.0f);
		y.minorTicksPerInterval = 0;
						
		NSMutableArray *dataSources = [NSMutableArray array];
		for(int i = 0; i < [dataCollections count]; ++i) {
			[dataSources addObject:[YCPlotDataSource initWithArray:[dataCollections objectAtIndex:i]]];
		}
		
			// Add the URL to "Recent Files" menu
		[[NSDocumentController sharedDocumentController] noteNewRecentDocumentURL:url];
		
		return dataSources;
	}	
}

- (void) loadCSVFrom:(NSURL *)url
{
	NSMutableArray *data = [self openCSVFileFromURL:url];
	[self updateData:data];
	
	[graph reloadData];
	[toggles setEnabled:YES];
}

-(void)openCSVDidEnd:(NSOpenPanel*)panel returnCode:(int)returnCode contextInfo:(void*)contextInfo
{
	if(returnCode == NSOKButton)
	{
		[self loadCSVFrom:[panel URL]];
	}
}

- (void) scaleAxes
{
	float min = 0.0f;
	float max = 1.0f;
	
		// Get min and max value of y from current visible plots.
	int num = [[graph allPlots] count];

	for(int i = 0; i < num; ++i) {
		CPScatterPlot *plot = (CPScatterPlot *)[graph plotAtIndex:i];
		YCPlotDataSource *dataSrc = (YCPlotDataSource *) [plot dataSource];
		double *minmax = [dataSrc getMinMax];		
		
		if (i == 0) {
			min = minmax[0];
			max = minmax[1];
		} else {
			if(min > minmax[0]) {
				min = minmax[0];
			}
			
			if(max < minmax[1]) {
				max = minmax[1];
			}
		}
		
		free(minmax);
	}
	
		// reset y-axis min/max
	CPXYPlotSpace *plotSpace = (CPXYPlotSpace *)graph.defaultPlotSpace;
		// plotSpace.xRange = [CPPlotRange plotRangeWithLocation:CPDecimalFromFloat(0.0) length:CPDecimalFromFloat([lines count] - 1)];
	plotSpace.yRange = [CPPlotRange plotRangeWithLocation:CPDecimalFromFloat(min) length:CPDecimalFromFloat(max - min)];
	
		// reset axes ticks
	CPXYAxisSet *axisSet = (CPXYAxisSet *)graph.axisSet;
	CPXYAxis *x = axisSet.xAxis;
	x.majorIntervalLength = CPDecimalFromFloat(1.0f);	
		// FIXME x.constantCoordinateValue = CPDecimalFromString(@"0.0");
	x. minorTicksPerInterval = 0;
	
	CPXYAxis *y = axisSet.yAxis;
	y.majorIntervalLength = CPDecimalFromFloat((max - min)/10.0f);
	y.minorTicksPerInterval = 0;	
}

#pragma mark -
#pragma mark Print and Add Organization actions
- (IBAction) printReport:(id)sender
{
		// TODO: print function
	[[NSAlert alertWithMessageText:@"[TODO] Print Report"
					 defaultButton:@"OK"
				   alternateButton:nil
					   otherButton:nil
		 informativeTextWithFormat:@"Print as graph or table report."]
	 runModal];
}

- (IBAction) addMoreOrganizaions:(id)sender
{
		// TODO: add organization
	[[NSAlert alertWithMessageText:@"[TODO] Add organization"
					 defaultButton:@"OK"
				   alternateButton:nil
					   otherButton:nil
		 informativeTextWithFormat:@"Add more organization from the list."]
	 runModal];	
}

#pragma mark -
#pragma mark Checkbox event action
- (IBAction) togglePlot:(id)sender
{
		// Sender is NSMatrix
	int numOfRows = [sender numberOfRows];
	int row = [sender selectedRow];
	int col = [sender selectedColumn];
	
	int index = col * numOfRows + row;
	NSString *identifier = [NSString stringWithFormat:@"plot%d", index];
	
	id target = [sender selectedCell];	
	if ([target state] == NSOnState) {
			// Create the plot
		CPScatterPlot *dataSourceLinePlot = [[[CPScatterPlot alloc] init] autorelease];
		dataSourceLinePlot.identifier = identifier;
		dataSourceLinePlot.dataLineStyle.lineWidth = 3.f;
		dataSourceLinePlot.dataLineStyle.lineColor = [plotColors objectAtIndex:index];
		
		dataSourceLinePlot.dataSource = (YCPlotDataSource *) [dataObjects objectAtIndex:index];
		[graph addPlot:dataSourceLinePlot];
		
			// Add plot symbols (UI interactions?)
		CPLineStyle *symbolLineStyle = [CPLineStyle lineStyle];
		symbolLineStyle.lineColor = [CPColor blackColor];
		CPPlotSymbol *plotSymbol = [CPPlotSymbol ellipsePlotSymbol];
		plotSymbol.fill = [CPFill fillWithColor:[CPColor orangeColor]];
		plotSymbol.lineStyle = symbolLineStyle;
		plotSymbol.size = CGSizeMake(10.0, 10.0);
		dataSourceLinePlot.plotSymbol = plotSymbol;				
	} else if ([target state] == NSOffState) {
		CPPlot *plot = (CPPlot *) [graph plotWithIdentifier:identifier];
		[graph removePlot:plot];		
	} else {
	}
	
	[self scaleAxes];	
}

#pragma mark -
#pragma mark Implement YCBrushDetectDelegate
- (void) brushFoundDataInPlotNumber:(int)plotNumber 
					withPointNumber:(int)pointNumber 
						 withXValue:(float)xVal 
						   withYVal:(float)yVal
{
		// Lookup all info and format the message properly
	NSMutableString *mesg = [NSMutableString stringWithCapacity:256];
	
	NSString *label = [xLabels objectAtIndex:pointNumber];
	[mesg appendFormat:@"<font face=\"Helvetica\" size=\"12\"><B>%@</B><BR/>\n", label];
	
	for (int i = 0; i < [dataObjects count]; ++i) {
		YCPlotDataSource *dataSource = (YCPlotDataSource *) [dataObjects objectAtIndex:i];

			// Find the proper index to hightlight values...
		double value = [[dataSource getValueAtIndex:pointNumber] doubleValue];
		if (yVal == value) {
			[mesg appendFormat:@"- <font color=\"RED\"><B>%d:</B> %.2f</font><BR/>\n", (i + 1), value];			
		} else {
			[mesg appendFormat:@"- <B>%d:</B> %@<BR/>\n", (i + 1), [dataSource getValueAtIndex:pointNumber]];			
		}
	}
    [mesg appendFormat:@"<BR/>\n</font>\n"];
	
		// Assign NSAttributedString to NSTextField
	NSData *fooData = [NSData dataWithBytes:[mesg UTF8String] length:strlen([mesg UTF8String])];
	NSAttributedString *fooString = [[NSAttributedString alloc] initWithHTML:fooData documentAttributes:nil];
	[status setAttributedStringValue:fooString];
}

@end
