/* 
 * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
 * 
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; version 2 of the License.
 *
 *  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
 */

#import "MFGRTTreeView.h"

#import "MFView.h"
#import <mforms/../cocoa/MFMForms.h>

#import "NSString_extras.h"
#import "GRTTreeDataSource.h"
#import "MTextImageCell.h"

@interface MFormsGRTOutlineView : NSOutlineView
{
@public
  mforms::GRTTreeView *mOwner;
}
@end

@implementation MFormsGRTOutlineView

- (void)rightMouseDown:(NSEvent *)theEvent
{
  int clickedRow = [self rowAtPoint: [self convertPoint: [theEvent locationInWindow] fromView: nil]];
  if (![[self selectedRowIndexes] containsIndex: clickedRow])
    [self selectRowIndexes: [NSIndexSet indexSetWithIndex: clickedRow]
      byExtendingSelection: NO];  
  [super rightMouseDown: theEvent];
}

- (NSMenu*)menuForEvent:(NSEvent *)event
{
  mforms::Menu *menu = mOwner->get_context_menu();
  if (menu)
  {
    (*menu->signal_will_show())();
    return menu->get_data();
  }
  return nil;
}

@end



@implementation MFGRTTreeViewImpl


- (id)initWithObject:(mforms::GRTTreeView*)object options:(mforms::TreeOptions)options
{
  NSRect rect= NSMakeRect(0, 0, 50, 50);
  self= [super initWithFrame:rect];
  if (self)
  {
    mOwner= object;
    mOwner->set_data(self);
    mOutline= [[[MFormsGRTOutlineView alloc] initWithFrame:rect] autorelease];
    ((MFormsGRTOutlineView*)mOutline)->mOwner = object;
    [mOutline setDoubleAction: @selector(rowDoubleClicked:)];
    [mOutline setTarget: self];
    
    [[NSNotificationCenter defaultCenter] addObserver: self
                                             selector: @selector(outlineViewSelectionDidChange:)
                                                 name: NSOutlineViewSelectionDidChangeNotification
                                               object: mOutline];
    
    rect.origin= NSMakePoint(0, 0);
    rect.size= [NSScrollView contentSizeForFrameSize: [self frame].size
                               hasHorizontalScroller: YES
                                 hasVerticalScroller: YES
                                          borderType: NSBezelBorder];
    [self setDocumentView: mOutline];
    [self setHasHorizontalScroller: YES];    
    [self setHasVerticalScroller: YES];    
    
    if (options & mforms::TreeNoBorder)
      [self setBorderType: NSNoBorder];
    else
      [self setBorderType: NSBezelBorder];
    
    if (options & mforms::TreeNoHeader)
      [mOutline setHeaderView:nil];
    
    if (options & mforms::TreeSidebar)
    {
      mSmallFont = YES;
      [mOutline setFocusRingType: NSFocusRingTypeNone];
      [self setAutohidesScrollers: YES];
    }
  }
  return self;
}


- (void)dealloc
{
  [[NSNotificationCenter defaultCenter] removeObserver: self];
  [super dealloc];
}


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


- (void)rowDoubleClicked:(id)sender
{
  bec::NodeId node= [mDataSource nodeIdForItem:[mOutline itemAtRow: [mOutline clickedRow]]];
  
  NSTableColumn *column= [[mOutline tableColumns] objectAtIndex: [mOutline clickedColumn]];

  mOwner->row_activate_callback(node, [[column identifier] integerValue]);
}


- (void)setTreeModel:(bec::TreeModel*)model
{
  [mDataSource autorelease];
  if (model)
  {
    mDataSource= [[GRTTreeDataSource alloc] initWithTreeModel:model];
    [mOutline setDataSource: mDataSource];
    [mOutline setDelegate: mDataSource];
    [mOutline reloadData];
  }
  else
  {
    mDataSource = nil;
    [mOutline setDataSource: nil];
    [mOutline setDelegate: nil];
  }
}


- (void)setWidth:(float)width ofColumn:(int)modelColumn
{
  [[mOutline tableColumnWithIdentifier: [NSString stringWithFormat:@"%i", modelColumn]] setWidth: width];
}


- (int)addColumnWithType:(mforms::GRTTreeColumnType)type modelIndex:(int)modelColumn name:(NSString*)name editable:(BOOL)editable
{
  NSTableColumn *column= nil;
  switch (type)
  {
    case mforms::StringGRTColumnType:
      column= [[[NSTableColumn alloc] initWithIdentifier:[NSString stringWithFormat:@"%i", modelColumn]] autorelease];
      [column setDataCell: [[[NSTextFieldCell alloc] initTextCell:@""] autorelease]];
      break;
    case mforms::IconGRTColumnType:
      
    case mforms::IconStringGRTColumnType:
      column= [[[NSTableColumn alloc] initWithIdentifier:[NSString stringWithFormat:@"%i", modelColumn]] autorelease];
      [column setDataCell: [[[MTextImageCell alloc] initTextCell:@""] autorelease]];
      break;
    case mforms::IntegerGRTColumnType:
      column= [[[NSTableColumn alloc] initWithIdentifier:[NSString stringWithFormat:@"%i", modelColumn]] autorelease];
      [column setDataCell: [[[NSTextFieldCell alloc] initTextCell:@""] autorelease]];
      break;
    case mforms::CheckGRTColumnType:
    {
      NSButtonCell *cell;
      column= [[[NSTableColumn alloc] initWithIdentifier:[NSString stringWithFormat:@"%i", modelColumn]] autorelease];
      [column setDataCell: cell= [[[NSButtonCell alloc] initTextCell:@""] autorelease]];
      [cell setButtonType: NSSwitchButton];
      break;
    }/*
    case mforms::IconGRTColumnType:
      column= [[[NSTableColumn alloc] initWithIdentifier:[NSString stringWithFormat:@"%i", modelColumn]] autorelease];
      [column setDataCell: [[[NSImageCell alloc] initImageCell:nil] autorelease]];
      break;*/
  }
  if (column)
  {
    [[column dataCell] setEditable:editable];
    [[column headerCell] setTitle: name];
    [[column dataCell] setTruncatesLastVisibleLine: YES];
    [mOutline addTableColumn: column];
    if ([[mOutline tableColumns] count] == 1)
      [mOutline setOutlineTableColumn: column];
    [column setResizingMask: NSTableColumnAutoresizingMask|NSTableColumnUserResizingMask];
    
    if (mSmallFont)
      [[column dataCell] setFont: [NSFont systemFontOfSize: [NSFont smallSystemFontSize]]];

    return [[mOutline tableColumns] count] - 1;
  }
  return -1;
}


- (void)refreshNode:(const bec::NodeId&)node
{
  if (node.is_valid())
  {
    [mOutline reloadItem: [mDataSource itemForNodeId:node]];
    [mOutline setNeedsDisplay: YES];
  }
  else
    [mOutline reloadData];
  
}


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


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


- (void) setBackgroundColor:(NSColor *)color
{
  [super setBackgroundColor: color];
  [mOutline setBackgroundColor: color];
}


static bool grttreeview_create(mforms::GRTTreeView *self, mforms::TreeOptions options)
{
  [[[MFGRTTreeViewImpl alloc] initWithObject: self options:options] autorelease];
  return true;
}

static void grttreeview_set_model(mforms::GRTTreeView *self, bec::TreeModel *model)
{
  MFGRTTreeViewImpl* tree = self->get_data();
  
  [tree setTreeModel: model];
}

static void grttreeview_set_allow_multi_select(mforms::GRTTreeView *self, bool flag)
{
  MFGRTTreeViewImpl* tree = self->get_data();
  
  [tree->mOutline setAllowsMultipleSelection: flag];
}

static int grttreeview_add_column(mforms::GRTTreeView *self, mforms::GRTTreeColumnType type, int model_column, const std::string &name)
{
  MFGRTTreeViewImpl* tree = self->get_data();
  
  return [tree addColumnWithType:type modelIndex:model_column name:[NSString stringWithCPPString:name] editable:NO];
}

static int grttreeview_add_column_editable(mforms::GRTTreeView *self, mforms::GRTTreeColumnType type, int model_column, const std::string &name)
{
  MFGRTTreeViewImpl* tree = self->get_data();
  
  return [tree addColumnWithType:type modelIndex:model_column name:[NSString stringWithCPPString:name] editable:YES];
}


static void grttreeview_refresh(mforms::GRTTreeView *self, const bec::NodeId &node)
{
  MFGRTTreeViewImpl* tree = self->get_data();
  
  [tree refreshNode: node];
}

static void grttreeview_set_column_width(mforms::GRTTreeView *self, int model_column, int width)
{
  MFGRTTreeViewImpl* tree = self->get_data();
  
  [tree setWidth: width ofColumn:model_column];
}

static bool grttreeview_get_selected_node(mforms::GRTTreeView *self, bec::NodeId &node)
{
  MFGRTTreeViewImpl* tree = self->get_data();
  NSInteger i= [tree->mOutline selectedRow];

  if (i < 0)
    return false;
  
  node= [tree->mDataSource nodeIdForItem:[tree->mOutline itemAtRow:i]];
  return true;
}

static int grttreeview_get_selection(mforms::GRTTreeView *self, std::vector<bec::NodeId> &nodes)
{
  MFGRTTreeViewImpl* tree = self->get_data();
  NSIndexSet *selection= [tree->mOutline selectedRowIndexes];
  
  for (NSUInteger i= [selection firstIndex]; i != NSNotFound; i= [selection indexGreaterThanIndex:i])
    nodes.push_back([tree->mDataSource nodeIdForItem:[tree->mOutline itemAtRow: i]]);
  
  return nodes.size();
}

static void grttreeview_row_count_changed(mforms::GRTTreeView *self, const bec::NodeId &parent, int old_count)
{
  MFGRTTreeViewImpl* tree = self->get_data();
  //[tree->mOutline noteNumberOfRowsChanged];
  [tree->mOutline performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO];
//  if (old_count < 0 && !parent.is_valid())
//    [tree->mOutline collapseItem: nil collapseChildren: YES];
}

static void expand_to_root(NSOutlineView *outline, GRTTreeDataSource *ds, bec::NodeId node, bool expand)
{
  id item = [ds itemForNodeId: node];
  if (node.depth() > 1)
  {
    expand_to_root(outline, ds, node.parent(), expand);
  }
  
  if (expand)
  {    
    // trigger model/backend expansion to match behaviour in Windows
    [ds outlineViewItemWillExpand: [NSNotification notificationWithName: NSOutlineViewItemWillExpandNotification
                                                                 object: outline
                                                               userInfo: [NSDictionary dictionaryWithObject: item
                                                                                                     forKey: @"NSObject"]]];
    [outline expandItem: item];
  }
  else
  {
    [outline collapseItem: [ds itemForNodeId: node]];
    [ds outlineViewItemDidCollapse: [NSNotification notificationWithName: NSOutlineViewItemDidCollapseNotification
                                                                  object: outline
                                                                userInfo: [NSDictionary dictionaryWithObject: item
                                                                                                      forKey: @"NSObject"]]];
  }  
}

static void grttreeview_set_expanded(mforms::GRTTreeView *self, const bec::NodeId& node, bool expanded)
{
  MFGRTTreeViewImpl* tree = self->get_data();
  
  // to match the behaviour in Windows, we must:
  // trigger the expand/collapse callback from backend (not very nice, because it's inconsistent with the rest of mforms)
  // expand/collapse parent items
  
  expand_to_root(tree->mOutline, tree->mDataSource, node, expanded);
}


void cf_grttreeview_init()
{
  ::mforms::ControlFactory *f = ::mforms::ControlFactory::get_instance();
  
  f->_grttreeview_impl.create= &grttreeview_create;
  f->_grttreeview_impl.set_model= &grttreeview_set_model;
  f->_grttreeview_impl.add_column= &grttreeview_add_column;
  f->_grttreeview_impl.add_column_editable= &grttreeview_add_column_editable;
  f->_grttreeview_impl.refresh= &grttreeview_refresh;
  f->_grttreeview_impl.set_column_width= &grttreeview_set_column_width;
  f->_grttreeview_impl.set_allow_multi_selection= &grttreeview_set_allow_multi_select;
  f->_grttreeview_impl.get_selected_node= &grttreeview_get_selected_node;
  f->_grttreeview_impl.get_selection= &grttreeview_get_selection;
  f->_grttreeview_impl.row_count_changed= &grttreeview_row_count_changed;
  f->_grttreeview_impl.set_expanded= &grttreeview_set_expanded;
}

@end
