
/* AssignmentTracker X http://www.atxproject.net - (c) 2003-2006, Colin Wheeler ( colin@atxproject.net )
 * AXSourceDragController.h Created by Colin Wheeler on March 12, 2006
 * 
 * Contributor(s):
 *	Colin Wheeler
 * 
 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
 * General Public License version 2 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

/* Based off of SourceDragController from http://allusions.sourceforge.net/articles/treeDragPart2.php 
 */

#import "ATXSourceDragController.h"
#import "ATXImageAndTextCell.h"

#ifndef VERBOSE_MODE
#define VERBOSE_MODE
#endif

@implementation ATXSourceDragController

- (void)awakeFromNib {	
	
	nc = [NSNotificationCenter defaultCenter];
	
	dragType = [NSArray arrayWithObjects: 	@"factorialDragType", nil];
	
	[ dragType retain ]; 
	
	[ treeTable registerForDraggedTypes:dragType ];
	NSSortDescriptor* sortDesc = [[NSSortDescriptor alloc] initWithKey:@"position" ascending:YES];
	[groupTreeControl setSortDescriptors:[NSArray arrayWithObject: sortDesc]];
	[ sortDesc release ];
	
	//
	[treeTable setDelegate:self];
	[treeTable setUsesGradientSelection:YES];
	//set the background color to the same light blue as iTunes
	[treeTable setBackgroundColor:[NSColor colorWithDeviceRed:(231.0/255.0) green:(237.0/255.0) blue:(248.0/255.0) alpha:1.0]];
	[treeTable setFont:[NSFont fontWithName:@"Helvetica" size:9.0]];
	/* This is only used if you want the blue gradient to cover all rows
	 * vs having a gradient selection for each row */
	//[treeTable setSelectionGradientIsContiguous:YES];
	
	/* Setting the icon and text field cell for the ATXOutlineView
	 * individual icons are now set via the icon attribute in
	 * Core Data entity - (void)outlineView:(NSOutlineView *)olv 
     * willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item 
	 */
	ATXImageAndTextCell *itCell = [[ATXImageAndTextCell alloc] init];
	[tbColumn setDataCell:itCell];
}	


//------------------------------------
#pragma mark NSOutlineView datasource methods -- see NSOutlineViewDataSource
//---------------------------------------------------------------------------	
- (BOOL) outlineView : (NSOutlineView *) outlineView 
					writeItems : (NSArray*) items 
				toPasteboard : (NSPasteboard*) pboard {
	/* This is only temporary to disable drag n drop for now */
	return NO;

	[ pboard declareTypes:dragType owner:self ];		
	// items is an array of _NSArrayControllerTreeNode see http://theocacao.com/document.page/130 for more info
	draggedNode = [ items objectAtIndex:0 ];
	
	return YES;	
}




- (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(int)index {
	
	_NSArrayControllerTreeNode* parentNode = item;
	_NSArrayControllerTreeNode* siblingNode;
	_NSControllerTreeProxy* proxy = [ groupTreeControl arrangedObjects ];
		
	NSManagedObject* draggedGroup = [ draggedNode observedObject ];
			 
	BOOL draggingDown = NO;
	BOOL isRootLevelDrag = NO;
	
	// ----------------------
	// Setup comparison paths
	// -------------------------
	NSIndexPath* draggedPath = [ draggedNode indexPath ];
	NSIndexPath* siblingPath =  [ NSIndexPath indexPathWithIndex:  index  ];
	if ( parentNode == NULL ) {		
		isRootLevelDrag = YES;
	} else {
		// A non-root drag - the index value is relative to this parent's children
		siblingPath = [ [ parentNode indexPath ] indexPathByAddingIndex: index ];
	}
	
	// ----------------------
	// Compare paths - modify sibling path for down drags, exit for redundant drags
	// -----------------------------------------------------------------------------	
	switch ( [ draggedPath compare:siblingPath] ) {
		case NSOrderedAscending:  // reset path for down dragging
			if ( isRootLevelDrag ) {
				siblingPath = [ NSIndexPath indexPathWithIndex: index  - 1];							 
			} else {
			  siblingPath = [ [ parentNode indexPath ] indexPathByAddingIndex: index - 1 ]; 
			}
			draggingDown = YES;
			break;
		 
		case NSOrderedSame:
			return NO;
			break;				 
	}
		
	siblingNode = [ proxy nodeAtIndexPath:siblingPath ];	
	
	// ------------------------------------------------------------	
	// SPECIAL CASE: Dragging to the bottom
	// ------------------------------------------------------------
	// - K								 - K							- C								 - C
	// - - U							 - - C     OR     - U								 - F
	// - - C     ====>     - - F				    - F								 - K
	// - - F               - U              - K								 - U
	// ------------------------------------------------------------ 
	if ( isRootLevelDrag  && siblingNode == NULL ) {		
		draggingDown = YES;
		siblingPath = [ NSIndexPath indexPathWithIndex: [ proxy count ] - 1 ];			
		siblingNode = [ proxy nodeAtIndexPath:siblingPath ] ;
	}
		
	// ------------------------------------------------------------	
	// Give the dragged item a postion relative to it's new sibling
	// ------------------------------------------------------------	
	 NSManagedObject* sibling = [ siblingNode observedObject ];	 
	 NSNumber* bystanderPosition = [ sibling valueForKey:@"position"];
	 int newPos =   ( draggingDown ? [ bystanderPosition intValue ]  + 1 : [ bystanderPosition intValue ]  - 1 );
	 [draggedGroup setValue:[ NSNumber numberWithInt:newPos ] forKey:@"position"];	

	// ----------------------------------------------------------------------------------------------
	// Set the new parent for the dragged item, resort the position attributes and refresh the tree
	// ----------------------------------------------------------------------------------------------	 
	[ draggedGroup setValue:[ parentNode observedObject ] forKey:@"parent" ];
	[ self resortGroups:[draggedGroup managedObjectContext] forParent:[ parentNode observedObject ] ];			
	[ groupTreeControl rearrangeObjects ];  
	return YES;
}






- (NSArray* ) getSubGroups:(NSManagedObjectContext*)objectContext forParent:(NSManagedObject*)parent {
	NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
	NSEntityDescription *entity = [NSEntityDescription entityForName:@"group" inManagedObjectContext:objectContext];
	
	[request setEntity:entity];
	NSSortDescriptor* aSortDesc = [[NSSortDescriptor alloc] initWithKey:@"position" ascending:YES];
	[request setSortDescriptors:[NSArray arrayWithObject: aSortDesc] ];
	[aSortDesc release];
	
	NSPredicate* validationPredicate = [NSPredicate predicateWithFormat:@"parent == %@", parent ];
	
	[ request setPredicate:validationPredicate ];
	
	NSError *error = nil;  // TODO - check the error bozo
	return [objectContext executeFetchRequest:request error:&error];	
}




- (void) resortGroups:(NSManagedObjectContext*)objectContext forParent:(NSManagedObject*)parent {
	
	NSArray *array = [ self getSubGroups:objectContext forParent:parent ];
	
	// Reset the indexes...
	NSEnumerator *enumerator = [array objectEnumerator];
	NSManagedObject* anObject;
	int index = 0;
	while (anObject = [enumerator nextObject]) {
		// Multiply index by 10 to make dragging code easier to implement ;) ....
    [anObject setValue:[ NSNumber numberWithInt:(index * INTERVAL ) ] forKey:@"position"];	  
		index++;
	}	
	
	
}

- (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(int)index {
	return  NSDragOperationGeneric;	
}


// This method gets called by the framework but the values from bindings are used instead
- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item {	
	return NULL;
}

/* 
The following are implemented as stubs because they are required when 
 implementing an NSOutlineViewDataSource. Because we use bindings on the
 table column these methods are never called. The NSLog statements have been
 included to prove that these methods are not called.
 */
- (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {
	//NSLog(@"numberOfChildrenOfItem");
	QuietLog(@"numberOfChildrenOfItem");
	return 1;
}

- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item {
	//NSLog(@"isItemExpandable");
	QuietLog(@"isItemExpandable");
	return NO;
}

- (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item {
	//NSLog(@"child of Item");
	QuietLog(@"child of Item");
	return NULL;
}

//more custom extentsions
- (void)outlineView:(NSOutlineView *)olv willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
{	
	// If the row is selected but isn't being edited and the current drawing isn't being used to create a drag image,
	// colour the text white; otherwise, colour it black
	int rowIndex = [olv rowForItem:item];
	NSColor *fontColor = ( [[olv selectedRowIndexes] containsIndex:rowIndex] && ([olv editedRow] != rowIndex) && (![[(ATXOutlineView *)olv draggedRows] containsIndex:rowIndex]) ) ? [NSColor whiteColor] : [NSColor blackColor];
	[cell setTextColor:fontColor];
	[cell setImage:[NSImage imageNamed:[[item observedObject] valueForKey:@"icon"]]];
	/* ADD CODE HERE TO SET ICONS FOR SPECIFIC ROWS/CELLS!!! */
}

- (float)outlineView:(NSOutlineView *)outlineView heightOfRowByItem:(id)item
{
	/*if([outlineView rowForItem:item] == 2 || [outlineView rowForItem:item] == 4) {
		return 30.0;
	} */
	return 17.0;
}

- (void)outlineViewSelectionDidChange:(NSNotification *)notification
{
	//TODO: Post message to AXPresentationManager or MyDocument of the change in selection
#ifdef VERBOSE_MODE
	//NSLog(@"[ATXSourceDragController] Selected Item with Title: %@", [[groupTreeControl selection] valueForKey:@"name"] );
	QuietLog(@"[ATXSourceDragController] Selected Item with Title: %@", [[groupTreeControl selection] valueForKey:@"name"] );
#endif
	[nc postNotificationName:@"SOURCE_LIST_SELECTION_CHANGED"
					  object:nil];
}

/////////////
//EXPERIMENTAL CODE
////////////

//- (id)outlineView:(NSOutlineView *)ov uniqueValueForObservedObject:(id)object
//{
//	return [object position];
//}

@end
