//
//  WBToolbarManager.mm
//  MySQLWorkbench
//
//  Created by Alfredo Kojima on 14/Oct/08.
//  Copyright 2008 Sun Microsystems Inc. All rights reserved.
//

#import "WBToolbarManager.h"
#import "WBMiniToolbar.h"
#import "GRTIconCache.h"
#import "MCPPUtilities.h"

#include "incremental_list_updater.h"


@interface WBToolbarItem : NSToolbarItem
{
  wb::WBToolbarItem *_item;
}

- (id)initWithItemIdentifier:(NSString*)identifier
                  sourceItem:(const wb::WBToolbarItem&)item;
- (const wb::WBToolbarItem&)sourceItem;
- (void)update:(const wb::WBToolbarItem&)sourceItem;
@end



@interface WBToolbarItemButton : NSButton
{
  WBToolbarItem* mWBToolbarItem;
}
- (void) setWBToolbarItem: (WBToolbarItem*) WBToolbarItem;
- (const wb::WBToolbarItem&)sourceItem;
@end

@implementation WBToolbarItemButton
- (void) setWBToolbarItem: (WBToolbarItem*) wbToolbarItem;
{
  mWBToolbarItem = wbToolbarItem;
}
- (const wb::WBToolbarItem&)sourceItem;
{
  return [mWBToolbarItem sourceItem];
}
@end


@interface WBSearchField : NSSearchField
{
  WBToolbarItem* mWBToolbarItem;
}
- (void) setWBToolbarItem: (WBToolbarItem*) WBToolbarItem;
- (const wb::WBToolbarItem&)sourceItem;
@end

@implementation WBSearchField
- (void) setWBToolbarItem: (WBToolbarItem*) wbToolbarItem;
{
  mWBToolbarItem = wbToolbarItem;
}
- (const wb::WBToolbarItem&)sourceItem;
{
  return [mWBToolbarItem sourceItem];
}
@end




@implementation WBToolbarItem

- (NSString*)description
{
  return [NSString stringWithFormat: @"%@::%@", [super description], [NSString stringWithCPPString: _item->name]];
}

- (NSButton*) makeButtonWithIconImage: (NSImage*) image;
{
  //	NSImage* bg = [NSImage imageNamed: @"tiny_toolbar_backgound_mac"];
  //	NSRect r = NSZeroRect;
  //	r.size = [bg size];
  //	NSImage *newImage = [[[NSImage alloc] initWithSize: r.size] autorelease];
  //	
  //	// Create button image by superimposing the icon onto a gradient backgound.
  //	[newImage lockFocus];
  //	// Draw the background image onto the new image.
  //	[bg drawAtPoint: NSMakePoint(0, 0)
  //				 fromRect: r
  //				operation: NSCompositeCopy
  //				 fraction: 1];
  //	r.size = [image size];
  //	// Draw the icon image onto the new image.
  //	[image drawAtPoint: NSMakePoint(10, 4)
  //						fromRect: r
  //					 operation: NSCompositeSourceOver
  //						fraction: 1];
  //	[newImage unlockFocus];
  //
  //	// Create the button.
  //	r = NSZeroRect;
  //	r.size = [newImage size];
  //	r = NSInsetRect(r, 0, -1);
  //	WBToolbarItemButton* button = [[[WBToolbarItemButton alloc] initWithFrame: r] autorelease];
  //	[button setBezelStyle: NSTexturedRoundedBezelStyle];
  //	[button setBordered: NO];
  //	[button setButtonType: NSMomentaryChangeButton];
  //	[button setImage: newImage];
  //	[button setAction: [self action]];
  //	[button setTarget: [self target]];
  //	[button setWBToolbarItem: self];
  
  // Create the button.
  NSRect r = NSMakeRect(0, 0, 31, 24);
  r = NSInsetRect(r, 0, -1);
  WBToolbarItemButton* button = [[[WBToolbarItemButton alloc] initWithFrame: r] autorelease];
  [button setBezelStyle: NSTexturedRoundedBezelStyle];
  [button setImage: image];
  [button setAction: [self action]];
  [button setTarget: [self target]];
  [button setWBToolbarItem: self];
  
  return button;
}



- (id)initWithItemIdentifier:(NSString*)identifier
                  sourceItem:(const wb::WBToolbarItem&)item
{
  self = [super initWithItemIdentifier:identifier];
  if (self != nil)
  {
    _item= new wb::WBToolbarItem(item);
    
    if (item.type == wb::ToolbarSearch)
    {
      WBSearchField *field = [[[WBSearchField alloc] initWithFrame: NSZeroRect] autorelease];
      [field sizeToFit];
      [field setFrame: NSMakeRect(0, 0, 180, NSHeight([field frame]))];
      [field setWBToolbarItem: self];
      [self setView: field];
    }
    else
    {
      if (item.icon != 0)
      {
        // check if there's a mac version of the icon
        std::string file= bec::replace_string(bec::IconManager::get_instance()->get_icon_file(item.icon), ".png", "_mac.png");
        NSImage *image= [NSImage imageNamed: [NSString stringWithCString:file.c_str()]];
        if (!image) {
          image= [[GRTIconCache sharedIconCache] imageForIconId:item.icon];
          [self setImage: image];
        }
        else {
          NSButton* button = [self makeButtonWithIconImage: image];
          [self setView: button];
        }
      }
    }
    if (!item.caption.empty())
      [self setLabel:[NSString stringWithUTF8String:item.caption.c_str()]];
    [self setToolTip:[NSString stringWithUTF8String:item.tooltip.c_str()]];
    [self setEnabled:item.enabled];
  }
  return self;
}

- (void) dealloc
{
  delete _item;
  [super dealloc];
}


- (void)update:(const wb::WBToolbarItem&)sourceItem
{
  if (sourceItem.type == wb::ToolbarAction)
  {
    if (sourceItem.icon != 0)
    {
      std::string file= bec::replace_string(bec::IconManager::get_instance()->get_icon_file(sourceItem.icon), ".png", "_mac.png");
      NSImage *image= [NSImage imageNamed: [NSString stringWithCString:file.c_str()]];
      if (!image) {
        image= [[GRTIconCache sharedIconCache] imageForIconId:sourceItem.icon];
        [self setImage: image];
      }
      else {
        NSButton* button = [self makeButtonWithIconImage: image];
        [self setView: button];
      }
    }
  }
  if (!sourceItem.caption.empty())
    [self setLabel:[NSString stringWithUTF8String:sourceItem.caption.c_str()]];
  [self setToolTip:[NSString stringWithUTF8String:sourceItem.tooltip.c_str()]];
  [self setEnabled:sourceItem.enabled];  
}


- (const wb::WBToolbarItem&)sourceItem
{
  return *_item;
}

@end



@implementation WBToolbarManager


class IncrementalToolbarRebuilder 
: public bec::IncrementalListUpdater<NSInteger,NSToolbarItem*,std::vector<wb::WBToolbarItem>::iterator>
{
  WBToolbarManager *_manager;
  NSToolbar *_toolbar;
  NSUInteger _dest_count;
  std::vector<wb::WBToolbarItem> _items;
  std::map<std::string, NSToolbarItem*> _separators;
public:
  NSSearchField *searchField;
  
public:
  IncrementalToolbarRebuilder(WBToolbarManager *manager, NSToolbar *toolbar, const std::vector<wb::WBToolbarItem> &items)
  : _manager(manager), _toolbar(toolbar)
  {
    std::vector<wb::WBToolbarItem> right_items;
    std::list<std::string> separator_ids;
    
    searchField= 0;
    
    // do a little pre-processing on the item list so that it's sorted in relation to right aligned items
    // we insert a special "expander" item of type Label to indicate the expander space item to separate
    // the right aligned items
    for (std::vector<wb::WBToolbarItem>::const_iterator iter= items.begin(); iter != items.end(); ++iter)
    {
      wb::WBToolbarItem item= *iter;

      // because separators cant have an id, 
      // for each separator in the source definition, we match it to one in the real toolbar
      if (item.type == wb::ToolbarSeparator)
        separator_ids.push_back(item.name);
      
      if (item.name.find("__right") != std::string::npos)
        right_items.insert(right_items.begin(), item);
      else
        _items.push_back(item);
    }
    
    wb::WBToolbarItem separator;
    separator.type= wb::ToolbarSeparator;
    separator.name= [NSToolbarFlexibleSpaceItemIdentifier UTF8String];
    _items.push_back(separator);
    _items.insert(_items.end(), right_items.begin(), right_items.end());
    
    NSArray *baritems= [toolbar items];
    _dest_count= [baritems count];
    if (!separator_ids.empty())
    {
      for (id item in baritems)
      {
        if ([[item itemIdentifier] isEqual: NSToolbarSeparatorItemIdentifier])
        {
          _separators[separator_ids.front()]= item;
          separator_ids.pop_front();
          if (separator_ids.empty())
            break;
        }
      }
    }
  }
  
  virtual dest_iterator get_dest_iterator()
  {
    return 0;
  }
  
  virtual source_iterator get_source_iterator()
  {
    return _items.begin();
  }
  
  virtual dest_iterator increment_dest(dest_iterator &iter)
  {
    return ++iter;
  }
  
  virtual source_iterator increment_source(source_iterator &iter)
  {
    return ++iter;
  }
  
  virtual bool has_more_dest(dest_iterator iter)
  {
    return iter < _dest_count;
  }
  
  virtual bool has_more_source(source_iterator iter)
  {
    return iter != _items.end();
  }
  
  virtual bool items_match(dest_iterator diter, source_iterator siter)
  {
    id item= [[_toolbar items] objectAtIndex:diter];
    if (strcmp(siter->name.c_str(), [[item itemIdentifier] UTF8String]) != 0)
    {
      if (item == _separators[siter->name])
        return true;
      return false;
    }
    return true;
  }
  
  virtual dest_ref get_dest(dest_iterator iter)
  {
    return [[_toolbar items] objectAtIndex:iter];
  }
  
  virtual dest_iterator begin_adding()
  {
    return 0;
  }
  
  
  virtual void end_adding(dest_iterator iter)
  {
    int i, c = [[_toolbar items] count];
    for (i = c - 1; i >= iter; i--) {
      [_toolbar removeItemAtIndex:iter];
    }
  }
  
  virtual dest_iterator add(dest_iterator &dest_pos, dest_ref item)
  {
    [item retain];

    if ([[_toolbar items] count] > dest_pos && [[_toolbar items] objectAtIndex:dest_pos] == item)
    {
      ++dest_pos;
    }
    else
    {
      NSUInteger ix = [[_toolbar items] indexOfObject:item];
      if ( (ix >= 0) && (ix != NSNotFound) ) {
        [_toolbar removeItemAtIndex: ix];
      }
      if (dest_pos <= [[_toolbar items] count]) {
        [_toolbar insertItemWithItemIdentifier:[item itemIdentifier] atIndex:dest_pos++];
      }
    }
    [item release];
  
    WBToolbarItem* tb_item= (WBToolbarItem*) item;
    if ([tb_item respondsToSelector: @selector(sourceItem)])
    {
      if ([tb_item sourceItem].type == wb::ToolbarSearch)
        searchField= (NSSearchField*)[tb_item view];
    }
    
    return dest_pos;
  }
  
  virtual dest_iterator add(dest_iterator &dest_pos, source_iterator source_item)
  {
    NSString *itemIdentifier= nil;
    switch (source_item->type)
    {
      case wb::ToolbarAction:
      case wb::ToolbarSearch:
      {
        WBToolbarItem *item;

        itemIdentifier= [NSString stringWithUTF8String:source_item->name.c_str()];
        item= [[WBToolbarItem alloc] initWithItemIdentifier:itemIdentifier
                                                 sourceItem:*source_item];
        
        [item setTarget:_manager];
        [item setAction:@selector(activateWBToolbarItem:)];
        
        [_manager->_mainToolbarItems setObject:item forKey:itemIdentifier];
        
        if (source_item->type == wb::ToolbarSearch)
          searchField= (NSSearchField*)[item view];

        [item release];
        break;
      }
        
      case wb::ToolbarSeparator:
        if (strcmp(source_item->name.c_str(), [NSToolbarFlexibleSpaceItemIdentifier UTF8String])==0)
          itemIdentifier= NSToolbarFlexibleSpaceItemIdentifier;
        else
          itemIdentifier= NSToolbarSeparatorItemIdentifier;
        break;
        
      case wb::ToolbarToggle:
        break;
        
      case wb::ToolbarCheck:
        break;
        
      case wb::ToolbarRadio:
        break;
        
      case wb::ToolbarLabel:
        break;
        
      case wb::ToolbarDropDown:
        break;
    }
    
    if (itemIdentifier != nil)
    {
      [_toolbar insertItemWithItemIdentifier:itemIdentifier atIndex:dest_pos++];  
    }
    return dest_pos;
  }
  
  virtual void update(dest_ref dest_item, source_iterator source_item)
  {
    if ([dest_item isKindOfClass:[WBToolbarItem class]])
      [(WBToolbarItem*)dest_item update:*source_item];
  }
};


static WBToolbarManager *managerSingleton= nil;

+ (WBToolbarManager*)defaultManager
{
  return managerSingleton;
}


- (id)initWithToolbar:(NSToolbar*)toolbar
            WBContext:(wb::WBContextUI*)wbui
{
  self = [super init];
  if (self != nil) 
  {
    if (managerSingleton == nil)
      managerSingleton= self;
    
    _wbui= wbui;
    _toolbar= toolbar;
    
    _mainToolbarItems= [[NSMutableDictionary alloc] init];
    
    [_toolbar setDelegate:self];
    [_toolbar setSizeMode: NSToolbarSizeModeSmall];
    [_toolbar setDisplayMode: NSToolbarDisplayModeIconOnly];
  }
  return self;
}


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



- (void)refreshMainToolbar
{
  std::vector<wb::WBToolbarItem> items(_wbui->get_command_ui()->get_toolbar_items("main"));
  
  IncrementalToolbarRebuilder rebuilder(self, _toolbar, items);
  
  rebuilder.execute();
  
  _searchField= rebuilder.searchField;
}



- (void)activateWBToolbarItem:(id)sender
{
  [_lastSearchString release];
  if ([sender isKindOfClass:[WBSearchField class]])
  {
    _lastSearchString= [[sender stringValue] copy];
  }
  _wbui->get_command_ui()->activate_command([sender sourceItem].command);
}


- (void)focusSearchField:(id)sender
{
  if (_searchField)
    [[_searchField window] makeFirstResponder: _searchField];
  else
    NSBeep();
}


- (NSString*)lastSearchString
{
  return _lastSearchString;
}


- (NSArray *)toolbarAllowedItemIdentifiers: (NSToolbar *)toolbar 
{
  return [NSArray arrayWithObjects:
          NSToolbarFlexibleSpaceItemIdentifier,
          NSToolbarSpaceItemIdentifier,
          NSToolbarSeparatorItemIdentifier, nil];
}

- (NSToolbarItem *)toolbar:(NSToolbar *)toolbar
     itemForItemIdentifier:(NSString *)itemIdentifier
 willBeInsertedIntoToolbar:(BOOL)flag
{
  return [_mainToolbarItems objectForKey:itemIdentifier];
}


- (void)toolButtonClicked:(id)sender
{
  _wbui->get_command_ui()->activate_command([[sender title] UTF8String]);
}

- (void) refreshToolsToolbar: (NSView*) toolsToolbar
{
  std::vector<wb::WBToolbarItem> items(_wbui->get_command_ui()->get_toolbar_items("tools"));
  
  for (id sv in [[toolsToolbar subviews] reverseObjectEnumerator])
    [sv removeFromSuperview];
  
  NSRect frame;
  CGFloat buttonSize= NSWidth([toolsToolbar frame]);
  
  frame.origin.y= NSHeight([toolsToolbar frame]);
  frame.origin.x= 0.0;
  frame.size.width= buttonSize;
  frame.size.height= buttonSize;
  
  for (std::vector<wb::WBToolbarItem>::const_iterator item= items.begin();
       item != items.end(); ++item)
  {
    if (item->type == wb::ToolbarSeparator)
    {
      frame.origin.y-= 2;
      
      NSBox *sep= [[[NSBox alloc] initWithFrame:NSMakeRect(frame.origin.x+3, frame.origin.y, buttonSize-5, 2)] autorelease];
      [sep setAutoresizingMask:NSViewMinYMargin];
      [toolsToolbar addSubview:sep];
    }
    else if (item->type == wb::ToolbarRadio)
    {
      frame.origin.y-= buttonSize;
      
      NSButton *button= [[[NSButton alloc] initWithFrame:frame] autorelease];
      [toolsToolbar addSubview: button];
      
      // title is used for storing the performed command
      [button setAutoresizingMask:NSViewMinYMargin];
      [button setTitle:[NSString stringWithUTF8String:item->command.c_str()]];
      [button setToolTip:[NSString stringWithUTF8String:item->tooltip.c_str()]];
      [button setTarget:self];
      [button setAction:@selector(toolButtonClicked:)];
      [button setEnabled:item->enabled];
      [button setState:item->checked?NSOnState:NSOffState];
      if (item->checked)
      {
        [button setBordered:YES];
        [button setBezelStyle:NSTexturedSquareBezelStyle];
      }
      else
        [button setBordered:NO];
      [button setButtonType:NSOnOffButton];
      [button setImage: [[GRTIconCache sharedIconCache] imageForIconId: item->icon]];
    }
  }
}


- (void)miniToolbar:(WBMiniToolbar*)sender
       popupChanged:(NSString*)name
             option:(NSString*)option
              value:(NSString*)value
{  
  _wbui->get_command_ui()->select_dropdown_item([name UTF8String], [option UTF8String], [value UTF8String]);
}


- (BOOL)refreshToolOptionsToolbar:(WBMiniToolbar*)toolbar
{
  std::vector<wb::WBToolbarItem> items(_wbui->get_command_ui()->get_toolbar_items("options"));
  
  if (items.empty())
    return NO;

  [toolbar removeAllItems];
  [toolbar setDelegate: self];
  
  for (std::vector<wb::WBToolbarItem>::const_iterator item= items.begin();
       item != items.end(); ++item)
  {
    switch (item->type)
    {
      case wb::ToolbarLabel:
        [toolbar addLabelWithTitle: [NSString stringWithCPPString: item->command]];
        break;
        
      case wb::ToolbarSeparator:
        [toolbar addSeparator];
        break;
        
//      case wb::ToolbarCheck:
//        NSLog(@"CHECK");
//        break;
        
      case wb::ToolbarDropDown:
      {
        std::string selected;
        std::vector<std::string> options= _wbui->get_command_ui()->get_dropdown_items(item->name, item->command, selected);
        
        if (!options.empty() && options.front()[0] == '#')
        {
          NSMutableArray *colors= [NSMutableArray arrayWithCapacity: options.size()];
          
          for (std::vector<std::string>::const_iterator color= options.begin(); color != options.end(); ++color)
            [colors addObject: [NSColor colorFromHexString: [NSString stringWithCPPString: *color]]];
          
          [toolbar addSelectionPopUpWithColors: colors
                                          name: [NSString stringWithCPPString: item->name]
                                        option: [NSString stringWithCPPString: item->command]
                                  defaultValue: [NSString stringWithCPPString: selected]];
        }
        else
          [toolbar addSelectionPopUpWithItems: MArrayFromStringVector(options)
                                         name: [NSString stringWithCPPString: item->name]
                                       option: [NSString stringWithCPPString: item->command]
                                 defaultValue: [NSString stringWithCPPString: selected]];
        break;
      }
        
      default:
        NSLog(@"unhandled toolbar option type for %s", item->name.c_str());
        break;
    }
  }
  return YES;
}

@end
