//
//  FSAViewAssociationController.m
//  F-Script Anywhere
//
//  Created by Nicholas Riley on Wed Jul 17 2002.
//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
//

/*

 F-Script Anywhere 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; either version 2 of the License, or
 (at your option) any later version.

 F-Script Anywhere 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 F-Script Anywhere; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*/

#import "FSAViewAssociationController.h"
#import "FSAController.h"
#import "FSAnywhere.h"
#import <FScript/FSInterpreter.h>
#import <FScript/System.h>

// XXX workaround for lack of identifier validation; should go away when F-Script adds (promised) direct support for this
@interface Compiler
+ (BOOL)isValidIndentifier:(NSString *)str;
@end

@implementation FSAViewAssociationController

- (id)initWithFSAController:(FSAController *)fsa;
{
    self = [super initWithWindowNibName: @"FSAViewAssociationPanel"];

    if (self != nil) {
        interpreter = [[[fsa interpreterView] interpreter] retain];
        system = [fsa system];
        [self window];
        [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(controlTextDidChange:) name: NSControlTextDidChangeNotification object: variableNameField];
        [[captureButton cell] setShowsStateBy: NSContentsCellMask | NSChangeGrayCellMask];
        [self update: nil];
    }
    return self;
}

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

- (IBAction)update:(id)sender;
{
    NSString *variableName = [variableNameField stringValue];
    BOOL canAssignToVariable = NO;
    [browseButton setEnabled: selectedView != nil];
    [statusField setStringValue: @""];
    if ([variableName length] != 0) {
        if (![Compiler isValidIndentifier: variableName]) {
            [statusField setStringValue: @"Invalid name: contains spaces, punctuation or non-ASCII characters"];
        } else if (selectedView != nil) {
            [statusField setStringValue: @"Click ÒAssociateÓ to assign to this variable"];
            canAssignToVariable = YES;
        }
    }
    [associateButton setEnabled: canAssignToVariable];
    [variableNameField setEnabled: [captureButton state] == NSOffState];
}

- (void)stopCapturing;
{
    FSALog(@"stopping capture");
    [captureButton setState: NSOffState];
    [statusField setStringValue: @""];
    [self update: nil];
    [variableNameField becomeFirstResponder];
}

- (void)_addElementToMenu:(id)element;
{
    NSMenuItem *item;
    if (element == nil) return;
    item = [viewHierarchyMenu addItemWithTitle: [@"  "
                stringByAppendingString: NSStringFromClass([element class])]
                                        action: @selector(elementSelected:)
                                 keyEquivalent: @""];
    [item setTarget: self];
    [item setRepresentedObject: element];
}

- (void)captureOneView;
{
    NSEvent *event;
    NSView *view, *superView = nil, *contentView;
    NSWindow *eventWindow;

    FSALog(@"capturing one");
    [captureButton setState: NSOnState];

    event = [NSApp nextEventMatchingMask: NSLeftMouseDownMask | NSRightMouseDownMask | NSKeyUpMask
                               untilDate: [NSDate distantFuture]
                                  inMode: NSEventTrackingRunLoopMode
                                 dequeue: YES];
    if ([event type] == NSKeyUp) {
        [self stopCapturing];
        return;
    }
    [viewHierarchyMenu release]; viewHierarchyMenu = nil;
    viewHierarchyMenu = [[NSMenu alloc] initWithTitle: @""];
    NS_DURING
        eventWindow = [event window];
        contentView = [eventWindow contentView];
        view = [[contentView superview] hitTest: [event locationInWindow]];
        if (view == captureButton) {
            [self stopCapturing];
            NS_VOIDRETURN;
        }
        [viewHierarchyMenu addItemWithTitle: @"View" action: nil keyEquivalent: @""];
        [self _addElementToMenu: view];
        superView = view;
        do {
            superView = [superView superview];
            if (superView == nil) break;
            [self _addElementToMenu: superView];
        } while (superView != contentView);
        [viewHierarchyMenu addItem: [NSMenuItem separatorItem]];
        [viewHierarchyMenu addItemWithTitle: @"Window" action: nil keyEquivalent: @""];
        [self _addElementToMenu: eventWindow];
    NS_HANDLER
        [descriptionField setStringValue:
            [NSString stringWithFormat: @"Çan exception occurred: %@È", localException]];
    NS_ENDHANDLER
    [NSMenu popUpContextMenu: viewHierarchyMenu withEvent: event forView: view];
    if ([captureButton state] == NSOnState)
        [self captureOneView];
}

- (IBAction)captureView:(id)sender
{
    [statusField setStringValue: @"Click inside one of this applicationÕs windows to select."];
    [selectedView release]; selectedView = nil;
    [self update: nil];
    [self captureOneView];
}

- (void)elementSelected:(NSMenuItem *)sender;
{
    NSView *element = [sender representedObject];
    FSALog(@"element selected: %@", sender);
    NS_DURING
        [descriptionField setStringValue: [element description]];
        [selectedView release];
        selectedView = [element retain];
    NS_HANDLER
        [descriptionField setStringValue:
            [NSString stringWithFormat: @"Çan exception occurred: %@È", localException]];
    NS_ENDHANDLER
    [viewHierarchyMenu release]; viewHierarchyMenu = nil;
    [self captureOneView];
}

- (IBAction)defineVariable:(id)sender;
{
#warning this should change when F-Script supports a public API for identifier validation
    NS_DURING
        NSString *variableName = [variableNameField stringValue];
        [statusField setStringValue: @"AssociatingÉ"];
        [interpreter setObject: selectedView forIdentifier: variableName];
        [statusField setStringValue: [NSString stringWithFormat: @"Assigned variable Ò%@Ó", variableName]];
    NS_HANDLER
        [statusField setStringValue: [NSString stringWithFormat: @"Assocation failed: %@", localException]];
    NS_ENDHANDLER
}

- (IBAction)viewInObjectBrowser:(id)sender;
{
    FSALog(@"system: %@", system);
    [system browse: selectedView];
    [statusField setStringValue: @"Opened object browser"];
}

- (void)controlTextDidChange:(NSNotification *)notification;
{
    [self update: nil];
}

@end
