//
//  MFTreeView.mm
//  MySQLWorkbench
//
//  Created by Alfredo Kojima on 11/Feb/09.
//  Copyright 2009 Sun Microsystems Inc. All rights reserved.
//

#import "MFTreeView.h"

#import "MFView.h"
#include "mforms/mforms.h"

@implementation MFTreeViewImpl


- (id)initWithObject:(::mforms::TreeView*)aTreeView
{
  self= [super initWithFrame:NSMakeRect(0, 0, 40, 40)];
  if (self)
  { 
    mOwner= aTreeView;
    mOwner->set_data(self);
    
    [self setHasHorizontalScroller:YES];
    [self setHasVerticalScroller:YES];
    [self setAutohidesScrollers:YES];
    
    NSRect rect;
    rect.origin= NSMakePoint(0, 0);
    rect.size= [NSScrollView contentSizeForFrameSize:[self frame].size hasHorizontalScroller:YES hasVerticalScroller:YES
                                          borderType:NSBezelBorder];
    mOutline= [[[NSOutlineView alloc] initWithFrame: rect] autorelease];
        
    [self setBorderType: NSBezelBorder];
    [self setDocumentView: mOutline];
    [mOutline setColumnAutoresizingStyle: NSTableViewLastColumnOnlyAutoresizingStyle];

    mContents= [[NSMutableDictionary dictionaryWithObject:[NSMutableArray array] forKey:@"children"] retain];
    [mOutline setDataSource: self];
    [mOutline setDelegate: self];
  }
  return self;
}


- (void) dealloc
{
  [mContents release];
  [super dealloc];
}


- (NSSize)minimumSize
{
  return NSMakeSize(40, 50);
}


- (NSInteger)addColumnWithTitle:(NSString*)title type:(mforms::TreeColumnType)type editable:(BOOL)editable
                          width:(int)width
{
  int idx= [[mOutline tableColumns] count];
  
  NSTableColumn *column= [[[NSTableColumn alloc] initWithIdentifier: [NSString stringWithFormat:@"%i",idx]] autorelease];

  [mOutline addTableColumn: column];
  [[column headerCell] setTitle: title];
  [column setResizingMask: NSTableColumnUserResizingMask];
  [[column dataCell] setEditable: editable];
  if (width >= 0)
    [column setWidth: width];

  return idx;
}


- (void)setTag:(NSInteger)tag
{
  [mOutline setTag:tag];
}


- (NSInteger)tag
{
  return [mOutline tag];
}


- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item
{
  if (!item)
    item= mContents;

  return [[item objectForKey:@"children"] objectAtIndex: index];
}


- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
{
  return [[item objectForKey:@"children"] count] > 0;
}


- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
{
  if (!item)
    return [[mContents objectForKey:@"children"] count];
  else
    return [[item objectForKey:@"children"] count];
}


- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
{
  return [item objectForKey: [tableColumn identifier]];
}


- (void)outlineView:(NSOutlineView *)outlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
{
  [item setObject:object forKey: [tableColumn identifier]];
}


- (void)outlineViewSelectionDidChange:(NSNotification *)notification
{
  mOwner->emit_changed();
}


#pragma mark reorder rows drag n drop

- (BOOL)outlineView:(NSOutlineView *)outlineView 
         writeItems:(NSArray *)items
       toPasteboard:(NSPasteboard *)pboard
{
  if ([items count] != 1)
    return NO;
  
  NSMutableDictionary* dict = [NSMutableDictionary dictionary];
  [dict setObject: [items lastObject]
           forKey: @"item"];
  [dict setObject: [NSNumber numberWithInt:[outlineView rowForItem: [items lastObject]]]
           forKey: @"index"];
  
  [pboard declareTypes: [NSArray arrayWithObject: @"com.sun.mysql.workbench.internal.treeview"]
                 owner: self];
  [pboard setPropertyList: dict
                  forType: @"com.sun.mysql.workbench.internal.treeview"];

  return YES;
}


- (NSDragOperation)outlineView:(NSOutlineView *)outlineView
                  validateDrop:(id < NSDraggingInfo >)info
                  proposedItem:(id)item
            proposedChildIndex:(NSInteger)index
{
  NSDragOperation op = NSDragOperationNone;
  
  NSPasteboard* pb = [info draggingPasteboard];
  NSDictionary* dict = [pb propertyListForType: @"com.sun.mysql.workbench.internal.treeview"];
  
  NSAssert( (dict != nil), @"Drag flavour was not found.");
  
  NSInteger oldIndex= [[dict objectForKey: @"index"] integerValue];
  // right now only allow reordering flat lists, not trees 
  // (even with trees we will only allow reordering in the same level)
  if (oldIndex != index && item == nil)
  {
    item= mContents;
    if (index < [[item objectForKey:@"children"] count])
    {
      [outlineView setDropItem: nil dropChildIndex: index];
      op = NSDragOperationMove;
    }
  }

  return op;
}



- (BOOL)outlineView:(NSOutlineView *)outlineView 
         acceptDrop:(id < NSDraggingInfo >)info
               item:(id)item
         childIndex:(NSInteger)index
{
  NSPasteboard* pb = [info draggingPasteboard];

  NSDictionary* dict = [pb propertyListForType: @"com.sun.mysql.workbench.internal.treeview"];
  NSAssert( (dict != nil), @"Drag flavour was not found.");

  NSInteger oldIndex = [[dict objectForKey: @"index"] integerValue];

  NSMutableArray *list= [item objectForKey: @"children"];
  id draggedItem= [list objectAtIndex: oldIndex];

  [list removeObjectAtIndex: oldIndex];
  if (index < oldIndex)
    [list insertObject: draggedItem atIndex: index];
  else
    [list insertObject: draggedItem atIndex: index-1];

  [outlineView reloadData];

  return YES;
}


- (void)enableRowReordering
{
  [mOutline setDraggingSourceOperationMask:NSDragOperationMove forLocal:YES];
  [mOutline setDraggingSourceOperationMask:NSDragOperationNone forLocal:NO];
  [mOutline registerForDraggedTypes:
   [NSArray arrayWithObject: @"com.sun.mysql.workbench.internal.treeview"] ];
}


static bool treeview_create(mforms::TreeView *self, mforms::TreeOptions options)
{
  MFTreeViewImpl *tree= [[[MFTreeViewImpl alloc] initWithObject: self] autorelease];
  
  if (!(options & mforms::TreeShowHeader))
  {
    [tree->mOutline setHeaderView:nil];
  }
  if (options & mforms::TreeAllowReorderRows)
  {
    [tree enableRowReordering];
  }
  
  return true;
}

static int treeview_add_column(mforms::TreeView *self, mforms::TreeColumnType type, const std::string &name, int width, bool editable)
{
  MFTreeViewImpl *tree= self->get_data();
  if (tree)
  {
    return [tree addColumnWithTitle:wrap_nsstring(name) type:type editable:editable width:width];
  }
  return -1;
}


static void treeview_end_columns(mforms::TreeView *self)
{
  MFTreeViewImpl *tree= self->get_data();
  if (tree)
  {
    [tree->mOutline sizeLastColumnToFit];
  }
}


static void treeview_clear_rows(mforms::TreeView *self)
{
  MFTreeViewImpl *tree= self->get_data();
  if (tree)
  {
    [[tree->mContents objectForKey:@"children"] removeAllObjects];
  }
}


static void treeview_delete_row(mforms::TreeView *self, int row)
{
  MFTreeViewImpl *tree= self->get_data();
  if (tree)
  {
    id item= [tree->mOutline itemAtRow: row];
    NSMutableDictionary *node= [tree->mOutline parentForItem: item];
    if (!node)
      node= tree->mContents;
    [[node objectForKey:@"children"] removeObject: item];

    [tree->mOutline reloadData];
  }
}


static int treeview_add_row(mforms::TreeView *self)
{
  MFTreeViewImpl *tree= self->get_data();
  if (tree)
  {
    NSMutableDictionary *child= [NSMutableDictionary dictionary];
    [child setObject:[NSMutableArray array] forKey:@"children"];
    NSMutableArray *array;
    
    array= [tree->mContents objectForKey:@"children"];
    if (!array)
    {
      array= [NSMutableArray array];
      [tree->mContents setObject: array forKey:@"children"];
    }
    [array addObject: child];

    [tree->mOutline reloadData];
    
    return [tree->mOutline rowForItem: child];
  }
  return -1;
}



static int treeview_count(mforms::TreeView *self)
{
  MFTreeViewImpl *tree= self->get_data();
  if (tree)
  {
    return [tree->mOutline numberOfRows];
  }
  return 0;
}


static void treeview_set_string(mforms::TreeView *self, int row, int column, const std::string &value)
{
  MFTreeViewImpl *tree= self->get_data();
  if (tree)
  {
    id item= [tree->mOutline itemAtRow: row];
    [item setObject: [NSString stringWithUTF8String: value.c_str()]
             forKey: [NSString stringWithFormat:@"%i", column]];
    [tree->mOutline reloadItem: item];
  }
}


static void treeview_set_row_tag(mforms::TreeView *self, int row, const std::string &value)
{
  MFTreeViewImpl *tree= self->get_data();
  if (tree)
  {
    id item= [tree->mOutline itemAtRow: row];
    [item setObject: [NSString stringWithUTF8String: value.c_str()]
             forKey: @"tag"];
  }
}

static void treeview_set_int(mforms::TreeView *self, int row, int column, int value)
{
  MFTreeViewImpl *tree= self->get_data();
  if (tree)
  {
    id item= [tree->mOutline itemAtRow: row];
    [item setObject: [NSString stringWithFormat:@"%i", value]
             forKey: [NSString stringWithFormat:@"%i", column]];
    [tree->mOutline reloadItem: item];
  }
}


static void treeview_set_check(mforms::TreeView *self, int row, int column, bool value)
{
  MFTreeViewImpl *tree= self->get_data();
  if (tree)
  {
    id item= [tree->mOutline itemAtRow: row];
    [item setObject: [NSString stringWithFormat:@"%i", value]
             forKey: [NSString stringWithFormat:@"%i", column]];
    [tree->mOutline reloadItem: item];
  }
}


static std::string treeview_get_string(mforms::TreeView *self, int row, int column)
{
  MFTreeViewImpl *tree= self->get_data();
  if (tree)
  {
    const char *str= [[[tree->mOutline itemAtRow: row] objectForKey: [NSString stringWithFormat:@"%i", column]] UTF8String];
    return str ? str : "";
  }
  return "";
}


static std::string treeview_get_row_tag(mforms::TreeView *self, int row)
{
  MFTreeViewImpl *tree= self->get_data();
  if (tree)
  {
    const char *str= [[[tree->mOutline itemAtRow: row] objectForKey: @"tag"] UTF8String];
    return str ? str : "";
  }
  return "";
}


static int treeview_get_int(mforms::TreeView *self, int row, int column)
{
  MFTreeViewImpl *tree= self->get_data();
  if (tree)
  {
    return [[[tree->mOutline itemAtRow: row] objectForKey: [NSString stringWithFormat:@"%i", column]] integerValue];
  }
  return 0;
}


static bool treeview_get_check(mforms::TreeView *self, int row, int column)
{
  MFTreeViewImpl *tree= self->get_data();
  if (tree)
  {
    return [[[tree->mOutline itemAtRow: row] objectForKey: [NSString stringWithFormat:@"%i", column]] integerValue];
  }
  return false;
}


static int treeview_get_selected(mforms::TreeView *self)
{
  MFTreeViewImpl *tree= self->get_data();
  if (tree)
    return [tree->mOutline selectedRow];
  return -1;
}


static void treeview_set_selected(mforms::TreeView *self, const int idx)
{
  MFTreeViewImpl *tree= self->get_data();
  if (tree)
    return [tree->mOutline selectRow:idx byExtendingSelection:NO];  
}


void cf_treeview_init()
{
  ::mforms::ControlFactory *f = ::mforms::ControlFactory::get_instance();
  
  f->_treeview_impl.create= &treeview_create;
  f->_treeview_impl.add_column= &treeview_add_column;
  f->_treeview_impl.end_columns= &treeview_end_columns;
  
  f->_treeview_impl.clear_rows= &treeview_clear_rows;
  
  f->_treeview_impl.delete_row= &treeview_delete_row;
  f->_treeview_impl.add_row= &treeview_add_row;
  
  f->_treeview_impl.get_selected= &treeview_get_selected;
  f->_treeview_impl.set_selected= &treeview_set_selected;

  f->_treeview_impl.count= &treeview_count;

  f->_treeview_impl.set_row_tag= &treeview_set_row_tag;
  f->_treeview_impl.set_string= &treeview_set_string;
  f->_treeview_impl.set_int= &treeview_set_int;
  f->_treeview_impl.set_check= &treeview_set_check;

  f->_treeview_impl.get_row_tag= &treeview_get_row_tag;
  f->_treeview_impl.get_string= &treeview_get_string;
  f->_treeview_impl.get_int= &treeview_get_int;
  f->_treeview_impl.get_check= &treeview_get_check;
}


@end

