#import "DraggableSource.h"

@implementation DraggableSource

NSString *kPrivateRowPBType = @"MyRowListPasteboardType";

// Register the table to respond to drops of the private pasteboard type.

- (void)awakeFromNib
{
	[oTable registerForDraggedTypes:[NSArray arrayWithObjects:kPrivateRowPBType,nil]];
}

/*" Populate the pasteboard.  This is invoked directly just before a dragging operation; we also call this code to copy selected rows to the clipboard.  This puts three flavors of data on the clipboard:  NSStringPboardType (basic string), in the format of newlines separating each column and two newlines separating each row; NSTabularTextPboardType (tabular data) with tab characters separating each column and newlines separating each row; and kPrivateRowPBType, just an array of row numbers, used for dragging within the table for reordering.
"*/

- (BOOL)tableView:(NSTableView *)inTableView writeRows:(NSArray*)inRows toPasteboard:(NSPasteboard*)inPasteboard
{
	NSData *archivedRowData = [NSArchiver archivedDataWithRootObject:inRows];

	NSArray *tableColumns = [inTableView tableColumns];
	NSMutableString *tabsBuf = [NSMutableString string];
	NSMutableString *textBuf = [NSMutableString string];
	NSEnumerator *rowEnum = [inRows objectEnumerator];
	NSNumber *rowNumber;

	while (nil != (rowNumber = [rowEnum nextObject]) )
	{
		int row = [rowNumber intValue];
		NSEnumerator *colEnum = [tableColumns objectEnumerator];
		NSTableColumn *col;
		while (nil != (col = [colEnum nextObject]) )
		{
			id columnValue
			= [self tableView:inTableView objectValueForTableColumn:col row:row];
			NSString *columnString = @"";
			if (nil != columnValue)
			{
				columnString = [columnValue description];
			}
			[tabsBuf appendFormat:@"%@\t",columnString];
			if (![columnString isEqualToString:@""])
			{
				[textBuf appendFormat:@"%@\n",columnString];
			}
		}
		// delete the last tab.  (But don't delete the last CR)
		if ([tabsBuf length])
		{
			[tabsBuf deleteCharactersInRange:NSMakeRange([tabsBuf length]-1, 1)];
		}
		// Append newlines to both tabular and newline data
		[tabsBuf appendString:@"\n"];
		[textBuf appendString:@"\n"];
	}

	// Delete the final newlines from the text and tabs buf.
	if ([tabsBuf length])
	{
		[tabsBuf deleteCharactersInRange:NSMakeRange([tabsBuf length]-1, 1)];
	}
	if ([textBuf length])
	{
		[textBuf deleteCharactersInRange:NSMakeRange([textBuf length]-1, 1)];
	}

	// Set up the pasteboard
	[inPasteboard declareTypes:[NSArray arrayWithObjects:NSTabularTextPboardType, NSStringPboardType, kPrivateRowPBType, nil] owner:nil];

	// Put the data into the pasteboard for our various types
	[inPasteboard setString:[NSString stringWithString:textBuf]
										 forType:NSStringPboardType];
	[inPasteboard setString:[NSString stringWithString:tabsBuf]
										 forType:NSTabularTextPboardType];
	[inPasteboard setData: archivedRowData forType: kPrivateRowPBType];

	return YES;
}

/*"	Validate a drop; we check to see if our private row number type is on the pasteboard, and we only allow NSTableViewDropAbove, since we only want to move rows between other rows.
"*/

- (NSDragOperation)tableView:(NSTableView*)inTableView validateDrop:(id <NSDraggingInfo>)inInfo proposedRow:(int)inRow proposedDropOperation:(NSTableViewDropOperation)inOperation
{
	// Look for our private type for reordering rows.
	NSString *type = [[inInfo draggingPasteboard] availableTypeFromArray:[NSArray arrayWithObjects:kPrivateRowPBType, nil]];
	
	if (inOperation == NSTableViewDropAbove && [type isEqualToString:kPrivateRowPBType])
	{
		return NSDragOperationMove;
	}
	return NSDragOperationNone;
}

// 	Called when the mouse is released over an table view that previously decided to allow a drop. The data source should incorporate the data from the dragging pasteboard at this time.

- (BOOL)tableView:(NSTableView*)inTableView acceptDrop:(id <NSDraggingInfo>)inInfo row:(int)inRow dropOperation:(NSTableViewDropOperation)inOperation
{
	// Look for our private type for reordering rows.
	NSString *type = [[inInfo draggingPasteboard] availableTypeFromArray:[NSArray arrayWithObjects:kPrivateRowPBType, nil]];

	if ([type isEqualToString:kPrivateRowPBType])
	{
		NSData *archivedRowData = [[inInfo draggingPasteboard] dataForType:kPrivateRowPBType];
		NSArray *rows = [NSUnarchiver unarchiveObjectWithData:archivedRowData];
		NSMutableArray *movedRows = [NSMutableArray arrayWithCapacity:[rows count]];
		NSEnumerator *theEnum = [rows objectEnumerator];
		id theRowNumber;

		// First collect up all the selected rows, then put null where it was in the array
		while (nil != (theRowNumber = [theEnum nextObject]) )
		{
			int row = [theRowNumber intValue];
			[movedRows addObject:[oData objectAtIndex:row]];
			[oData replaceObjectAtIndex:row withObject:[NSNull null]];
		}

		// Then insert these data rows into the array
		[oData replaceObjectsInRange:NSMakeRange(inRow, 0) withObjectsFromArray:movedRows];

		// Now, remove the NSNull placeholders
		[oData removeObjectIdenticalTo:[NSNull null]];

		// And refresh the table.  (Ideally, we should turn off any column highlighting)
		[inTableView deselectAll:nil];
		[inTableView reloadData];

	}
    return YES;
}


@end
