//
//  GRTShellViewController.m
//  MySQLWorkbench
//
//  Created by Alfredo Kojima on 23/Sep/08.
//  Copyright 2008 Sun Microsystems Inc. All rights reserved.
//

#import "GRTShellViewController.h"
#import "MTextImageCell.h"
#import "MCPPUtilities.h"

#include "sigobjc++.h"
#include "grt/grt_manager.h"

@implementation CommandTextView

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


- (NSRange)rangeForUserCompletion
{
  NSRange range= [super rangeForUserCompletion];
  NSString *text= [self string];
  
  if (!mTokenCharSet)
  {
    mTokenCharSet= [[NSMutableCharacterSet characterSetWithCharactersInString: @"_.[]"] retain];
    [mTokenCharSet formUnionWithCharacterSet: [NSCharacterSet alphanumericCharacterSet]];
  }

  while (range.location > 0 && [mTokenCharSet characterIsMember: [text characterAtIndex:range.location-1]])
  {
    range.location--;
    range.length++;
  }
  return range;
}

@end


@implementation GRTShellViewController


static void handle_ready(const std::string &prompt, GRTShellViewController *shell)
{
  [shell nextShellCommand:[NSString stringWithUTF8String:prompt.c_str()]];
}


static void handle_output(const std::string &output, GRTShellViewController *shell)
{
  [shell write:[NSString stringWithUTF8String:output.c_str()]];
}


- (void)dealloc
{
  bec::ListModel *model= [inspectorDS listModel];
  delete model;
  [[NSNotificationCenter defaultCenter] removeObserver:self];
  [super dealloc];
}


- (NSView*)sidebar
{
  return sidebarView;
}


- (void)setupWithGRTManager:(bec::GRTManager*)grtm
{  
  [NSBundle loadNibNamed:@"GRTShellSidebar" owner:self];
  
  _grtm= grtm;
  _promptPrinted= NO;

  _grtm->get_shell()->set_ready_handler(sigc::bind(sigc::ptr_fun(handle_ready), self));
  _grtm->get_shell()->set_output_handler(sigc::bind(sigc::ptr_fun(handle_output), self));

  [shellText replaceCharactersInRange:NSMakeRange(0,[[shellText textStorage] length]) withString:@""];

  [shellCommand setTextColor:[NSColor whiteColor]];
  [shellCommand setFont:[NSFont fontWithName:@"Monaco" size:10.0]];
  
  if ([shellCommand delegate])
    NSLog(@"shellCommand has an unexpected delegate");
  [shellCommand setDelegate: self];

  [shellText setTextColor:[NSColor whiteColor]];
  [shellText setFont:[NSFont fontWithName:@"Monaco" size:10.0]];
    
  [[NSNotificationCenter defaultCenter] addObserver:self
                                           selector:@selector(outlineSelectionChanged:)
                                               name:NSOutlineViewSelectionDidChangeNotification
                                             object:globalsOutline];
  [[NSNotificationCenter defaultCenter] addObserver:self
                                           selector:@selector(outlineSelectionChanged:)
                                               name:NSOutlineViewSelectionDidChangeNotification
                                             object:classesOutline];
  [[NSNotificationCenter defaultCenter] addObserver:self
                                           selector:@selector(outlineSelectionChanged:)
                                               name:NSOutlineViewSelectionDidChangeNotification
                                             object:modulesOutline];
  
  [globalsDS setTreeModel:_grtm->get_shared_value_tree("/")];
  [classesDS setTreeModel:_grtm->get_shared_structs_tree()];
  [modulesDS setTreeModel:_grtm->get_shared_modules_tree()];
}


- (void)handleGRTMessage:(const grt::Message*)msg
{
  [self writeLine: [NSString stringWithUTF8String: msg->format().c_str()]];
}


- (void)refresh
{
  NSString *requestedPath= [globalsPathCombo stringValue];
  std::string path;
  
  if (!requestedPath || [requestedPath length] == 0 || [requestedPath characterAtIndex: 0] != '/')
    path= "/";
  else
    path= [requestedPath UTF8String];

  _grtm->get_shell()->add_grt_tree_bookmark(path);
  
  _grtm->get_shared_value_tree(path)->refresh();
  _grtm->get_shared_structs_tree()->refresh();
  _grtm->get_shared_modules_tree()->refresh();
  
  std::vector<std::string> bookmarks(_grtm->get_shell()->get_grt_tree_bookmarks());
  [globalsPathCombo removeAllItems];
  [globalsPathCombo addItemsWithObjectValues: MArrayFromStringVector(bookmarks)];
  
  [globalsOutline reloadData];
  [classesOutline reloadData];
  [modulesOutline reloadData];
  
  [globalsOutline expandItem:[globalsOutline itemAtRow:0]];
}


- (void)outlineSelectionChanged:(NSNotification*)notif
{
  if ([notif object] == globalsOutline)
  {
    int row= [globalsOutline selectedRow];
    if (row >= 0)
    {
      bec::ListModel *model= [inspectorDS listModel];
      delete model;
      
      grt::ValueRef value;
      
      value= ((bec::ValueTreeBE*)[globalsDS treeModel])->get_node_value([globalsDS nodeIdForItem:[globalsOutline itemAtRow:row]]);
      
      [inspectorDS setListModel:_grtm->get_new_value_inspector(value, true)];
    }
    else
      [inspectorDS setListModel:0];
    
    std::string path;
    path= ((bec::ValueTreeBE*)[globalsDS treeModel])->get_path_for_node([globalsDS nodeIdForItem:[globalsOutline itemAtRow:row]], true);
    [pathText setStringValue: [NSString stringWithCPPString: path]];
    
    [inspectorTable reloadData];
  }
  else if ([notif object] == classesOutline)
  {
    int row= [classesOutline selectedRow];
    if (row >= 0)
    {
      std::string text= [classesDS treeModel]->get_field_description([classesDS nodeIdForItem:[classesOutline itemAtRow:row]], 0);
      [classText setString: [NSString stringWithCPPString: text]];
    }
    else
      [classText setString: @""];
  }
  else if ([notif object] == modulesOutline)
  {
    int row= [modulesOutline selectedRow];
    if (row >= 0)
    {
      std::string text= [modulesDS treeModel]->get_field_description([modulesDS nodeIdForItem:[modulesOutline itemAtRow:row]], 0);
      [moduleText setString: [NSString stringWithCPPString: text]];
    }
    else
      [moduleText setString: @""];    
  }
}


- (void)nextShellCommand:(NSString*)prompt
{
  float totalWidth= NSWidth([[shellPrompt superview] frame]);
  float promptWidth;
  NSRect commandFrame= [shellCommand frame];
  
  [shellPrompt setStringValue:prompt];
  [shellPrompt sizeToFit];
  
  promptWidth= NSWidth([shellPrompt frame]);

  commandFrame.size.width= totalWidth - promptWidth - 12;
  commandFrame.origin.x= totalWidth - NSWidth(commandFrame);

  [shellCommand setFrame: commandFrame];
}


- (void)writeLine:(NSString*)text
{
  if (_promptPrinted)
  {
    [shellText replaceCharactersInRange:NSMakeRange([[shellText textStorage] length], 0) withString:@"\n"];
    _promptPrinted= NO;
  }
  
  [shellText replaceCharactersInRange:NSMakeRange([[shellText textStorage] length], 0) withString:text];
  [shellText replaceCharactersInRange:NSMakeRange([[shellText textStorage] length], 0) withString:@"\n"];
  
  [shellText scrollRangeToVisible:NSMakeRange([[shellText textStorage] length], 0)];
}


- (void)write:(NSString*)text
{
  if (_promptPrinted)
  {
    [shellText replaceCharactersInRange:NSMakeRange([[shellText textStorage] length], 0) withString:@"\n"];
    _promptPrinted= NO;
  }
  
  [shellText replaceCharactersInRange:NSMakeRange([[shellText textStorage] length], 0) withString:text];
  
  [shellText scrollRangeToVisible:NSMakeRange([[shellText textStorage] length], 0)];
}


- (void)activateCollectionItem:(id)sender
{
  if (sender == globalsPathCombo)
  {
    NSString *path= [globalsPathCombo stringValue];

    if (path && [path length] > 0 && [path characterAtIndex: 0] == '/')
      [self refresh];
  }
}


- (NSArray *)textView:(NSTextView *)textView completions:(NSArray *)words
  forPartialWordRange:(NSRange)charRange indexOfSelectedItem:(NSInteger *)index
{
  NSString *text= [[textView string] substringWithRange: charRange];
  std::string completed;
  std::vector<std::string> choices;
  
  choices= _grtm->get_shell()->complete_line([text UTF8String], completed);
  
  if (!completed.empty() && completed.compare([text UTF8String]) != 0 && choices.empty())
  {
    [textView replaceCharactersInRange:charRange withString: [NSString stringWithCPPString: completed]];
    return nil;
  }
  
  return MArrayFromStringVector(choices);
}


- (BOOL)textView:(NSTextView *)aTextView doCommandBySelector:(SEL)aSelector
{
  if (aTextView == shellCommand)
  {
    NSEvent *theEvent= [NSApp currentEvent];
    
    if (_lastShortcutEventTime != [theEvent timestamp] && [theEvent type] == NSKeyDown)
    {
      NSString *theArrow= [theEvent charactersIgnoringModifiers];
      unichar keyChar= 0;
      
      _lastShortcutEventTime= [theEvent timestamp];
      
      if ([theArrow length] == 1) 
      {
        keyChar= [theArrow characterAtIndex:0];
        if (keyChar == NSUpArrowFunctionKey)
        {
          std::string line;
          
          if (_grtm->get_shell()->previous_history_line([[shellCommand string] UTF8String], line))
          {
            [shellCommand setString: [NSString stringWithCPPString: line]];
            [shellCommand moveToBeginningOfDocument:nil];
            [shellCommand moveToEndOfLine:nil];
          }
          return YES;
        }
        else if (keyChar == NSDownArrowFunctionKey)
        {
          std::string line;
          
          if (_grtm->get_shell()->next_history_line(line))
          {
            [shellCommand setString: [NSString stringWithCPPString: line]];
            [shellCommand moveToBeginningOfDocument:nil];
            [shellCommand moveToEndOfLine:nil];
          }
          return YES;
        }
        else if (keyChar == '\n' || keyChar == '\r') // enter/return
        {
          NSString *command= [[shellCommand string] stringByAppendingString:@"\n"];
          
          _promptPrinted= NO;
          [self write: [NSString stringWithFormat:@"\n%@ %@", [shellPrompt stringValue], [shellCommand string]]];
          _promptPrinted= YES;
          
          _grtm->get_shell()->process_line_async([command UTF8String]);
          
          [shellCommand setString:@""];
          [[shellCommand window] makeFirstResponder: shellCommand];
          
          return YES;
        }
      }
    }
  }
  return NO;
}

@end
