/* All Rights reserved */

#import "LedgerModel.h"

@implementation LedgerModel

- (void) dealloc
{  
	RELEASE(records);  
	RELEASE(filteredRecords); 
	RELEASE(defaultDate);
	RELEASE(dates);
	RELEASE(categories);  
	[super dealloc];
}

- (id) init
{  
	if( (self = [super init]) )
		{  
			records = [NSMutableArray new];  
			filteredRecords = [NSMutableArray new];
			total = 0; 
			new = -1;
			dates = [NSMutableArray new];
			categories = [NSMutableArray new];  
			defaultDate = RETAIN([NSCalendarDate calendarDate]);  
			return self;
		}
	return nil;
}


// Properties methods
//********************


- (NSMutableArray *) records
{
	return records;
}

- (void) setRecords: (NSMutableArray *) someRecords
{
	if(someRecords)
		{
			ASSIGN(records, someRecords);
			[self _computeTotal];
			[self _updateDates];
			[self _updateCategories];
			[[NSNotificationCenter defaultCenter] postNotificationName: @"Total Changed" object: nil];
		}
}

- (NSCalendarDate *) defaultDate
{
	return defaultDate;
}

- (void) setDefaultDate: (NSCalendarDate *) date
{
	if(date)
		{
			ASSIGN(defaultDate, date);
		}
}

- (NSNumber *) total
{
	return [NSNumber numberWithFloat: total];
}

- (NSNumber *) average;
{
    return [NSNumber numberWithFloat: average];
}

- (NSArray *) dates
{
	return [NSArray arrayWithArray: dates];
}

- (NSArray *) categories;
{
	return [NSArray arrayWithArray: categories];
}


- (void) filterRecords: (NSDictionary *)theFilter
{
	NSMutableArray *result = [NSMutableArray new];
	NSString *date, *dateTo, *dateFrom;
	id tempCategories = [NSArray new];
	int x = 0;
	int i;
	
	dateFrom = [NSString string];
	dateTo = [NSString string];

	// start with an empty array, and add in all the records whose date falls within the filter
	// if fromDate or toDate is empty add everything

	if( [[theFilter objectForKey: @"dateFrom"] isEqualToString: @"NONE"] ||
		[[theFilter objectForKey: @"dateTo"] isEqualToString: @"NONE"] )
		{
			for( i=0; i < [records count]; i++)
				[result addObject: [NSNumber numberWithInt: i]];
		}
	else
		{
			dateFrom = [NSString stringWithString: [[NSCalendarDate dateWithString: [theFilter objectForKey: @"dateFrom"] calendarFormat: @"%d.%m.%Y"] 
			                                  	     descriptionWithCalendarFormat: @"%Y%m%d"]];	
	
			dateTo = [NSString stringWithString: [[NSCalendarDate dateWithString: [theFilter objectForKey: @"dateTo"] calendarFormat: @"%d.%m.%Y"]
		   		                                   descriptionWithCalendarFormat: @"%Y%m%d"]];
		
			for( i=0; i < [records count]; i++ )
				{
					date = [[records objectAtIndex: i] objectForKey: @"dateSortable"];
			
					if( [[[records objectAtIndex: i] objectForKey: @"forcedisplay"] isEqualToString: @"YES"] )
						{
							[result addObject: [NSNumber numberWithInt: i]];
						}
					else if( ([dateFrom compare: date] == NSOrderedSame || [dateFrom compare: date] == NSOrderedAscending) &&
			  		([dateTo compare: date] == NSOrderedSame || [dateTo compare: date] == NSOrderedDescending) )
			  		{
			  			[result addObject: [NSNumber numberWithInt: i]];
			  		}
				}
		}
			
	// if the filter is not NONE, remove from the array all the records 
	// whose category is excluded

	if( [[theFilter objectForKey: @"categories"] isEqual: @"NONE"] )
		{
			// don't remove any categories
		}
  else if( (tempCategories = [NSArray arrayWithArray: [theFilter objectForKey: @"categories"]]) )
		{
			for( i = 0; i < [result count]; i++ )
				{
					x = [[result objectAtIndex: i] intValue];
					
					if( ![tempCategories containsObject: [[records objectAtIndex: x] objectForKey: @"category"]] &&
							![[[records objectAtIndex: x] objectForKey: @"forcedisplay"] isEqualToString: @"YES"] )
						{
							[result removeObjectAtIndex: i];
							i--;
						}
				}
		}
		
	// remove from the array all items that don't match the filtered item
	// or if the as yet non-existant 'exclude' switch box, is checked, remove
	// only the items that match.
	
	// not implemented: FIXME 
	
	ASSIGN(filteredRecords, result);
	[self _computeTotal];			
  [[NSNotificationCenter defaultCenter] postNotificationName: @"Total Changed" object: nil];
}


// Editing methods
//*****************


- (void) addRecord: (NSMutableDictionary *) record
{
	if(record)
		{
			[record setObject: @"YES" forKey: @"forcedisplay"];
			[records addObject: record];
			[self _sortRecords];
			[self _updateDates];
			new = [records indexOfObject: record];
		}
}

- (int) newRecord
{
	int i;
	
	for( i=0; i < [filteredRecords count]; i++ )
		{
			if( new == [[filteredRecords objectAtIndex: i] intValue] )
				break;
		}
		

	return i;	
}

- (void) clearNewRecord
{
	// [[records objectAtIndex: anIndex] removeObjectForKey: @"new"];
	new = -1;
}


// NSTableViewDataSource protocol methods
//****************************************


- (int) numberOfRowsInTableView: (NSTableView *) view
{
	return [filteredRecords count];
}

- (id)            tableView: (NSTableView *) view
  objectValueForTableColumn: (NSTableColumn *) column      
  											row: (int) row
{
	int x;

	x = [[filteredRecords objectAtIndex: row] intValue];
	return [[records objectAtIndex: x] objectForKey: [column identifier]];
}

- (void) tableView: (NSTableView *) view       
	  setObjectValue: (id) object       
	  forTableColumn: (NSTableColumn *) column       
	             row: (int) row
{
	int x = 0;
	x = [[filteredRecords objectAtIndex: row] intValue];
	
	[[records objectAtIndex: x] setObject: object forKey: [column identifier]];

	if( [[column identifier] isEqualToString: @"amount"] )
		{
			if( new == x && 
				[[records objectAtIndex: x] objectForKey: @"category"] &&
				[[records objectAtIndex: x] objectForKey: @"item"] )
				{
					[self clearNewRecord];
				}
			[self _computeTotal];	
			[[NSNotificationCenter defaultCenter] postNotificationName: @"Total Changed" object: nil];
		}
		
	if( [[column identifier] isEqualToString: @"category"] )
		{
			[self _updateCategories];
			
			if( [[[records objectAtIndex: x] objectForKey: @"forcedisplay"] isEqualToString: @"YES"] )
				[[records objectAtIndex: x] removeObjectForKey: @"forcedisplay"];
		}
}


// NSTableViewDataSource drag and drop methods
//*********************************************

// disabled for now ...


// - (NSDragOperation) tableView: (NSTableView *) view
//                 validateDrop: (id <NSDraggingInfo>) info
//                  proposedRow: (int) row
//        proposedDropOperation: (NSTableViewDropOperation) operation
//{
//  if (row > [records count])
//    return NSDragOperationNone;
//
//  if (nil == [info draggingSource]) // From other application
//    {
//      return NSDragOperationNone;
//    }
//  //else if (tableView == [info draggingSource]) // From self
//  //  {
//  //    return NSDragOperationNone;
//  //  }
//  else 
//    {
//      [view setDropRow: row dropOperation: NSTableViewDropAbove];
//      return NSDragOperationCopy;
//    }
//}
//
//- (BOOL) tableView: (NSTableView *) view
//        acceptDrop: (id <NSDraggingInfo>) info
//               row: (int) row
//     dropOperation: (NSTableViewDropOperation) operation
//{
//  NSPasteboard *pboard = [info draggingPasteboard];
//  NSData *data = [pboard dataForType: @"NSGeneralPboardType"];
//
//  if (row > [records count])
//    return NO;
//
//  if (nil == [info draggingSource]) // From other application
//    {
//      return NO;
//    }
//  //else if (tableView == [info draggingSource]) // From self
//  // {
//  //    return NO;
//  //  }
//  else
//    {
//      id object = [NSUnarchiver unarchiveObjectWithData: data];
//     [records insertObject: object atIndex: row];
//      //[tableView reloadData];
//      //[self updateChangeCount: NSChangeDone];
//
//      return YES;
//    }
//  return NO;
//}
//
//- (BOOL) tableView: (NSTableView *) view
//         writeRows: (NSArray *) rows
//      toPasteboard: (NSPasteboard *) pboard
//{
//  id object = [records objectAtIndex: [[rows lastObject] intValue]];
//  NSData *data = [NSArchiver archivedDataWithRootObject: object];
//
//  [pboard declareTypes: [NSArray arrayWithObject: @"NSGeneralPboardType"]
//                 owner: nil];
//  [pboard setData: data forType: @"NSGeneralPboardType"];
//  return YES;
//}


// Private methods and functions
//*******************************


- (void) _sortRecords
{
	NSMutableArray *newArray;
	newArray = [NSMutableArray arrayWithArray: [records sortedArrayUsingFunction: _recordSort context: NULL]];
	ASSIGN(records, newArray);
}

- (void) _computeTotal
{
	// NSDecimalNumber *tempAverage;
	id temp;
	int i;
	int x = 0;
 	float value = 0;
 	
 	for( i=0; i < [filteredRecords count]; i++ )
 		{
 			x = [[filteredRecords objectAtIndex: i] intValue];
 			if( (temp = [[records objectAtIndex: x] objectForKey: @"amount"]) )
 				{
 					value = value + [temp floatValue];
 				}
 		}
 	total = value;
	if ([dates count] >= 1)
  	average = total/[dates count];
  else
  	average = 0;
 	//tempAverage = [NSDecimalNumber numberWithFloat: average];
  //tempAverage = [tempAverage decimalNumberByRoundingAccordingToBehavior: [NSDecimalNumber defaultBehavior]];
  //average = [tempAverage floatValue]; 
}

- (void) _updateDates
{
	int i;
	
	dates = [[NSMutableArray alloc] initWithCapacity: 1];
	
	for( i=0; i<[records count]; i++ )
		{
			if( ![dates containsObject: [[records objectAtIndex: i] objectForKey: @"date"]] )
				{
					[dates addObject: [[records objectAtIndex: i] objectForKey: @"date"]];
				}
		}

	[[NSNotificationCenter defaultCenter] postNotificationName: @"Dates Changed" object: dates];			
}

- (void) _updateCategories;
{
	int i;  
	
	categories = [[NSMutableArray alloc] initWithCapacity: 1];  
	
	for( i=0; i<[records count]; i++ )    
		{      
			if( ![categories containsObject: [[records objectAtIndex: i] objectForKey: @"category"]] &&
				[[records objectAtIndex: i] objectForKey: @"category"] != nil &&
				![[[records objectAtIndex: i] objectForKey: @"category"] isEqualToString: @""] )        
				{          
					[categories addObject: [[records objectAtIndex: i] objectForKey: @"category"]];        
				}    
		}
	
	if( categories )
		[[NSNotificationCenter defaultCenter] postNotificationName: @"Categories Changed" object: categories];
}

int _recordSort(id rec1, id rec2, void *context)
{
	NSString *s1 = (NSString *)[rec1 objectForKey: @"dateSortable"];
	NSString *s2 = (NSString *)[rec2 objectForKey: @"dateSortable"];
	
	if ( !s1 || !s2 )
		{
			NSLog(@"we're missing some data to sort by");
			return NSOrderedSame;
		}
	return [s1 compare: s2];
}

@end
