/*
 * Copyright (c) 2010 David McIntosh <dmcintosh@df12.net>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#import "GanttController.h"

#define DEBUG_OUTLINEVIEW NO
#define DEBUG_GANTTVIEW YES

@implementation GanttController

- (id)init
{
    self = [super init];
    if (self)
    {
        tasks = [[GanttModel alloc] init];
        toolbarItems = [[NSMutableDictionary alloc] init];
        nextTag = 0;
    }
    return self;
}

- (NSString *) windowNibName
{
  return @"Window";
}

- (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError
{
    return [NSKeyedArchiver archivedDataWithRootObject:tasks];
}

- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName 
    error:(NSError **)outError
{
    ASSIGN(tasks, [NSKeyedUnarchiver unarchiveObjectWithData:data]);
    return YES;
}

- (void)awakeFromGSMarkup
{
    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];

    NSWindow  *aWindow = [_ganttView window];
    NSToolbar *toolbar = [[NSToolbar alloc] 
        initWithIdentifier:@"DocumentToolbar"];

    [toolbar setDelegate:self];
        
    [aWindow setToolbar:toolbar];
    AUTORELEASE(toolbar);

    [_tableView setDataSource:self];
    [_tableView setAllowsMultipleSelection:YES];

    [nc addObserver:self
        selector:@selector(outlineViewSelectionDidChange:)
        name:NSOutlineViewSelectionDidChangeNotification
        object:_tableView];
    [nc addObserver:self
        selector:@selector(outlineViewItemWillCollapse:)
        name:NSOutlineViewItemWillCollapseNotification
        object:_tableView];
}

- (NSInteger)rootTasks
{
    return [tasks count];
}

- (NSEnumerator *)rootTaskEnumerator
{
    return [tasks rootTaskEnumerator];
}

- (GanttTask *)taskAtIndex:(NSInteger)index
{
    return [tasks taskAtIndex:index];
}

-(NSDate *)earliestStartDate
{
    return [tasks earliestStartDate];
}

-(NSDate *)latestEndDate
{
    return [tasks latestEndDate];
}

- (void)setGanttView:(GanttView *)ganttView
{
    ASSIGN(_ganttView, ganttView);
}

- (GanttView *)ganttView
{
    return _ganttView;
}

- (void)setTableView:(id)aTableView
{
    ASSIGN(_tableView, aTableView);
}

- (id)tableView
{
    return _tableView;
}

- (GanttModel *)tasks
{
    return tasks;
}

//
// toolbar delegate methods
//
- (NSToolbarItem *)toolbar:(NSToolbar *)toolbar 
    itemForItemIdentifier:(NSString *)itemIdentifier 
    willBeInsertedIntoToolbar:(BOOL)flag
{
    if (![toolbarItems objectForKey:itemIdentifier])
    {
        NSToolbarItem *anItem = [[NSToolbarItem alloc] 
            initWithItemIdentifier:itemIdentifier];
    
        [anItem setLabel:itemIdentifier];
        if ([itemIdentifier isEqualToString:@"addNewTask"])
        {
            [anItem setAction:@selector(addNewTask:)];
            [anItem setLabel:@"Add Task"];
            [anItem setImage:[NSImage imageNamed:@"Add"]];
        }
        else if ([itemIdentifier isEqualToString:@"removeTask"])
        {
            [anItem setAction:@selector(removeTask:)];
            [anItem setLabel:@"Remove Task"];
            [anItem setImage:[NSImage imageNamed:@"Remove"]];
        } 
        else if ([itemIdentifier isEqualToString:@"addChildToSelectedTask"])
        {
            [anItem setAction:@selector(addChildToSelectedTask:)];
            [anItem setLabel:@"Add Child"];
            [anItem setImage:[NSImage imageNamed:@"AddChild"]];
        }
        else if ([itemIdentifier isEqualToString:@"aggregate"])
        {
            [anItem setAction:@selector(aggregate:)];
            [anItem setLabel:@"Aggregate"];
            [anItem setImage:[NSImage imageNamed:@"Aggregate"]];
        }
        [toolbarItems setObject:anItem forKey:itemIdentifier];
        AUTORELEASE(anItem);
    }
    return [toolbarItems objectForKey:itemIdentifier];
}

- (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar*)toolbar
{
    return [NSArray arrayWithObjects:
        @"addNewTask", @"removeTask", @"aggregate", @"addChildToSelectedTask", nil];
}

- (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar*)toolbar
{
    return [NSArray arrayWithObjects:
        @"addNewTask", @"removeTask", @"aggregate", @"addChildToSelectedTask", nil];
}

- (NSArray *)toolbarSelectableItemIdentifiers:(NSToolbar *)toolbar
{
    return [NSArray arrayWithObjects:
        @"addNewTask", @"removeTask", @"aggregate", @"addChildToSelectedTask", nil];
}

- (BOOL)validateToolbarItem:(NSToolbarItem *)theItem
{
    if ([[theItem itemIdentifier] isEqualToString:@"removeTask"])
        return ([[_ganttView selectedTags] count] > 0);
    if ([[theItem itemIdentifier] isEqualToString:@"addChildToSelectedTask"])
        return ([[_ganttView selectedTags] count] == 1);
    return YES;
}

//
// GanttView delegate methods
//
- (void)userUpdatedCompletion:(id)sender
{
    GanttCell *theCell = [sender  selectedCell];
    float completion   = [theCell completion];
    NSInteger tag      = [sender  selectedTag];
    if (DEBUG_GANTTVIEW)
        UKLog(@"Called for tag %d completion %f", tag, completion);
    GanttTask *task = [tasks taskForTag:tag];
    [task setCompletion:completion];
    [_tableView reloadItem:task reloadChildren:NO];
    [[NSNotificationCenter defaultCenter] 
        postNotificationName:@"GanttViewSelectionDidChange" 
        object:_ganttView userInfo:nil];
}

- (void)userUpdatedStartDate:(id)sender
{
    GanttCell *theCell = [sender  selectedCell];
    NSDate *startDate  = [theCell startDate];
    NSInteger tag      = [sender  selectedTag];
    if (DEBUG_GANTTVIEW)
        UKLog(@"Called for tag %d start date %@", tag, startDate);
    [[tasks taskForTag:tag] setStartDate:startDate];
    [[NSNotificationCenter defaultCenter] 
        postNotificationName:@"GanttViewSelectionDidChange" 
        object:_ganttView userInfo:nil];
}

- (void)userUpdatedEndDate:(id)sender
{
    GanttCell *theCell = [sender  selectedCell];
    NSDate *endDate    = [theCell endDate];
    NSInteger tag      = [sender  selectedTag];
    if (DEBUG_GANTTVIEW)
        UKLog(@"Called for tag %d end date %@", tag, endDate);
    [[tasks taskForTag:tag] setEndDate:endDate];
    [[NSNotificationCenter defaultCenter] 
        postNotificationName:@"GanttViewSelectionDidChange" 
        object:_ganttView userInfo:nil];
}

- (void)outlineViewSelectionDidChange:(NSNotification *)note
{
    NSIndexSet *selection = [[note object] selectedRowIndexes];
    if (DEBUG_OUTLINEVIEW)
        UKLog(@"Selection: %@", selection);
    [_ganttView selectRowIndexes:selection byExtendingSelection:NO];
    
}

- (void)selectionDidChange:(id)sender
{
    NSIndexSet *selection = [sender selectedRowIndexes];
    [_tableView selectRowIndexes:selection byExtendingSelection:NO];
}

- (void)outlineViewItemWillCollapse:(id)sender
{
    if (DEBUG_OUTLINEVIEW)
        UKLog(@"Called");
}

//
// Action methods
//
- (void)zoomIn:(id)sender
{
    GanttZoomFactor zoomFactor = [_ganttView zoomFactor];
    if (GanttZoomWide == zoomFactor)
        [_ganttView setZoomFactor:GanttZoomMedium];
    else if (GanttZoomMedium == zoomFactor)
        [_ganttView setZoomFactor:GanttZoomClose];
}

- (void)zoomOut:(id)sender
{
    GanttZoomFactor zoomFactor = [_ganttView zoomFactor];
    if (GanttZoomClose == zoomFactor)
        [_ganttView setZoomFactor:GanttZoomMedium];
    else if (GanttZoomMedium == zoomFactor)
        [_ganttView setZoomFactor:GanttZoomWide];
}

- (GanttTask *)newTask
{
    return [GanttTask taskWithTag:nextTag++
        startDate:[[NSDate dateWithTimeIntervalSinceNow:0]
            dateByRoundingToNearest:GanttDay]
        endDate:[[NSDate dateWithTimeIntervalSinceNow:60*60*24*1]
            dateByRoundingToNearest:GanttDay]
        completion:0.5];
}

- (void)addNewTask:(id)sender
{
    [tasks addTask:[self newTask]];
    [_tableView reloadItem:nil reloadChildren:YES];
    [_ganttView setNeedsDisplay:YES];
    [[_ganttView window] invalidateCursorRectsForView:_ganttView];
}

- (void)removeTask:(id)sender
{
    NSNumber *tag;
    NSEnumerator *e = [[_ganttView selectedTags] objectEnumerator];
    while (( tag = [e nextObject] ))
        [tasks removeTaskWithTag:[tag intValue]];
    [_ganttView clearSelection];
    NSWindow *window = [_ganttView window];
    [window invalidateCursorRectsForView:_ganttView];
    [[window toolbar] validateVisibleItems];
    [_ganttView setNeedsDisplay:YES];
    [_tableView reloadItem:nil reloadChildren:YES];
}

- (void)addChildToSelectedTask:(id)sender
{
    NSInteger tag = [_ganttView selectedTag];
    [tasks addChild:[self newTask] toTaskWithTag:tag];
    [_tableView reloadItem:nil reloadChildren:YES];
    [_ganttView collapseAll:self];
    [_ganttView setNeedsDisplay:YES];
}

//
// Data Source methods
//
- (NSInteger)outlineView:(NSOutlineView *)outlineView 
    numberOfChildrenOfItem:(id)item 
{
    if (DEBUG_OUTLINEVIEW)
        UKLog(@"Called for view: %@ item: %@ (%d)", 
            outlineView, item, [(GanttTask *)item tag]);
    if (nil == item)
        return [tasks count];
    else
        return [(GanttTask *)item childCount];
}

- (BOOL)outlineView:(NSOutlineView *)outlineView 
    isItemExpandable:(id)item 
{
    if (DEBUG_OUTLINEVIEW)
        UKLog(@"Called for view: %@ item: %@ (%d)", 
            outlineView, item, [(GanttTask *)item tag]);
    return ([(GanttTask *)item childCount] > 0);
}

- (id)outlineView:(NSOutlineView *)outlineView 
    child:(NSInteger)index ofItem:(id)item 
{
    if (DEBUG_OUTLINEVIEW)
        UKLog(@"Called for view: %@ item: %@ index: %d", 
            outlineView, item, index);

    // nil signifies root item
    if (nil == item) 
        return [tasks taskAtIndex:index];
    else
        return [(GanttTask *)item childAtIndex:index];
}

- (id)outlineView:(NSOutlineView *)outlineView
    objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item 
{
    if (DEBUG_OUTLINEVIEW)
        UKLog(@"Called for view: %@ item: %@ (%d)", 
            outlineView, item, [(GanttTask *)item tag]);

    if (nil == item) return nil;

    if ([[tableColumn identifier] isEqualToString:@"name"])
        return [item name];

    if ([[tableColumn identifier] isEqualToString:@"completion"])
    {
        NSCell *cell = [tableColumn dataCell];
        id f = [cell formatter];
        if (nil == f)
        {
            f = [[NSNumberFormatter alloc] init];
            [f setFormat:@"0;0;-0"];
            [cell setFormatter:AUTORELEASE(f)];
        }
        return [NSNumber numberWithFloat:[(GanttTask *)item completion]*100];
    }

    return nil;
}

- (void)dealloc
{
    [[_ganttView window] setToolbar:nil];
    DESTROY(tasks);
    DESTROY(toolbarItems);
    DESTROY(_ganttView);
    DESTROY(_tableView);
    [super dealloc];
}

@end
