// Copyright (c) 2002, Peter Bentley
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
// 
//   Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 
//   Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 
//   The name Peter Bentley may not be used to endorse or promote
// products derived from this software without specific prior
// written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// $Id: OmdTocCtlr.m,v 1.21 2002/11/24 19:31:30 pete Exp $

#import "OmdTocCtlr.h"
#import "OmdToc.h"
#import "OmdTrackInfo.h"
#import "OmdGroupInfo.h"
#import "OmdUnit.h"
#import "omd.h"

CVSID( "$Id: OmdTocCtlr.m,v 1.21 2002/11/24 19:31:30 pete Exp $" );

// Constants for the node types we allow in the outline view
NSString *kOmdTitleNode = @"Title Node";
NSString *kOmdGroupNode = @"Group Node";
NSString *kOmdTrackNode = @"Track Node";


@implementation OmdTocCtlr

- (void) refresh
{
    [children release];
    children = nil;
    [toc release];
    toc = [[omdUnit toc] retain];
    [outline reloadData];
}

- (void) dealloc
{
    [children release];
    [toc release];
    [currTitle release];
    [super dealloc];
}




- (void) updateView
{
    [outline setAutoresizesOutlineColumn: NO];
    if( toc )
    {
        slog( LOG_DEBUG, "Updating TOC - %d tracks", [toc numTracks] );
        [self saveCurrentTitle: [toc title]];
    }
    else
    {
        slog( LOG_INFO, "No TOC" );
    }
}

- (void) saveCurrentTitle: (NSString *) newTitle
{
    [currTitle autorelease];
    currTitle = [newTitle retain];
}



// Data Source methods

- (int) outlineView: (NSOutlineView *) outlineView 
    numberOfChildrenOfItem: (id) item 
{
    slog( LOG_TRACE, "Get number of children for %p", item );
    return (item == nil) ? 1 : [item numberOfChildren];
}

- (BOOL) outlineView: (NSOutlineView *) outlineView 
    isItemExpandable: (id) item 
{
    slog( LOG_TRACE, "Get isexpandable for %p", item );
    if( item == nil )
    {
        return YES;
    }
    //if( (expanded == NO) )
    //{
    //    slog( LOG_INFO, "Sending expand" );
    //    [outline expandItem: self expandChildren: NO];
    //    // expanded = YES;
    //}
    return ([item numberOfChildren] != -1);
}

- (id) outlineView: (NSOutlineView *) outlineView 
    child: (int) index 
    ofItem: (id) item 
{
    slog( LOG_TRACE, "Get child %d of item %p", index, item );
    if( item == nil )
    {
        return self;
    }
    else
    {
        //if( (expanded == NO) )
        //{
        //    slog( LOG_INFO, "Sending expand" );
        //    [outline expandItem: self expandChildren: NO];
        //    expanded = YES;
        //}
        return [item childAtIndex: index];
    }
}

- (id) outlineView: (NSOutlineView *) outlineView 
    objectValueForTableColumn: (NSTableColumn *) tableColumn 
    byItem: (id) item 
{
    id retval;
//    slog( LOG_INFO, "Column %p, item %p", tableColumn, item );
    retval = (item == nil) ? @"" : [item valueForKey: [tableColumn identifier]];
    return retval;
}

- (void) outlineView: (NSOutlineView *) outlineView 
    setObjectValue: (id) object 
    forTableColumn: (NSTableColumn *) tableColumn 
    byItem: (id) item
{
    [item takeValue: object forKey: [tableColumn identifier]];
}

// Outline view data

- (void) findChildren
{
    int 		g, track;
    OmdGroupInfo	*grp;
    
    if( children )
    {
        [children release];
    }

    // Arbitrary initial capacity...
    children = [[NSMutableArray alloc] initWithCapacity: 8];
    
    track = 0;
    for( g = 0; g < [toc numGroups]; g++ )
    {
        // Get the group info
        grp = [toc groupInfo: g];

        // Add all tracks before this group
        while( track < [grp first] )
        {
            [children addObject: [toc trackInfo: track]];
            track++;
        }
        // Then add the group
        [children addObject: grp];
        // And move the track pointer to the track after
        track = [grp last] + 1;
    }
    // Add any remaining tracks
    for( ; track < [toc numTracks]; track++ )
    {
        [children addObject: [toc trackInfo: track]];
    }
}


- (int) numberOfChildren
{
    if( children == nil )
    {
        [self findChildren];
    }
    return [children count];
}

- (id) childAtIndex: (int) index
{
    return [children objectAtIndex: index];
}

- (int) id
{
    return 0;
}

- (NSString *) idString
{
    return @"";
}

- (NSString *) groupString
{
    return @"";
}

- (NSString *) titleString
{
    //slog( LOG_INFO, "TocCtlr titlestring" );
    //if( (expanded == NO) )
    //{
    //    slog( LOG_INFO, "Sending expand 2" );
    //    [outline expandItem: self expandChildren: NO];
    //    expanded = YES;
    //}
    return currTitle;
}

- (void) setTitleString: (NSString *) str
{
    if( ! [str isEqualToString: currTitle])
    {
        slog( LOG_INFO, "New disc title: %s", [str lossyCString]);
        [toc setDiscTitle: str];
        [self updateView];
    }
}

- (NSString *) lengthString
{
    return @"";
}

- (NSString *) codecString
{
    return @"";
}

- (NSString *) rateString
{
    return @"";
}

- (NSString *) lockedString
{
    return @"";
}

- (NSString *) nodeType
{
    return kOmdTitleNode;
}


- (IBAction) test: (id) sender
{
    // [outline expandItem: self expandChildren: NO];
    if( [outline numberOfSelectedRows] )
    {
        int i;
        NSEnumerator *enumer = [outline selectedRowEnumerator];
        NSMutableArray *targets = [[NSMutableArray alloc] initWithCapacity: 5];
        NSNumber *row;
        id	t;
        
        [targets autorelease];
        while( row = [enumer nextObject] )
        {
            [targets addObject: [outline itemAtRow: [row intValue]]];
        }

        enumer = [targets objectEnumerator];
        i = 0;
        while( t = [enumer nextObject] )
        {
            slog( LOG_INFO, "Target %d, type %s, title %s", 
            i++, [[t nodeType] lossyCString], [[t titleString] lossyCString] );
        }

    }
    else
    {
        slog( LOG_INFO, "No rows selected" );
    }
}


- (NSMutableArray *) outlineTargets
{
    NSMutableArray *targets = nil;
    
    // [outline expandItem: self expandChildren: NO];
    if( [outline numberOfSelectedRows] )
    {
        // int i;
        NSEnumerator *enumer = [outline selectedRowEnumerator];
        NSNumber *row;
        // id	t;
        
        targets = [[NSMutableArray alloc] initWithCapacity: 5];
        [targets autorelease];
        while( row = [enumer nextObject] )
        {
            [targets addObject: [outline itemAtRow: [row intValue]]];
        }
    }
    return targets;
}

- (BOOL) canCreateGroupFrom: (NSArray *) targets
{
    NSEnumerator	*enumer;
    id			targ;

    if( ! targets )
    {
        slog( LOG_DEBUG, "No targets" );
        return NO;
    }
    enumer = [targets objectEnumerator];
    while( targ = [enumer nextObject] )
    {
        if( [targ nodeType] != kOmdTrackNode )
        {
            slog( LOG_DEBUG, "Not a track..." );
            return NO;
        }
        OmdTrackInfo *track = targ;
        if( [track group] != -1 )
        {
            slog( LOG_DEBUG, "Track already in group..." );
            return NO;
        }
    }
    slog( LOG_DEBUG, "Group OK" );
    
    return YES;
}



- (BOOL) canDeleteTargets: (NSArray *) targets
{
    NSEnumerator	*enumer;
    id			targ;

    if( ! targets )
    {
        slog( LOG_DEBUG, "No targets" );
        return NO;
    }
    enumer = [targets objectEnumerator];
    while( targ = [enumer nextObject] )
    {
        if( [targ nodeType] == kOmdTitleNode )
        {
            slog( LOG_DEBUG, "Can't delete title node..." );
            return NO;
        }
    }
    slog( LOG_DEBUG, "Nuke 'em" );
    
    return YES;
}




- (IBAction) createGroup: (id) sender
{
    NSArray		*targets = [self outlineTargets];
    NSEnumerator	*enumer;
    OmdTrackInfo	*track;
    int			first, last, num;
    
    if( targets && [self canCreateGroupFrom: targets] )
    {
        enumer = [targets objectEnumerator];
        track = [enumer nextObject];
        first = last = [track id];
        
        while( track = [enumer nextObject] )
        {
            num = [track id];
            if( num != last + 1 )
            {
                slog( LOG_ERROR, "Non contiguous group!" );
                return;
            }
            last = num;
        }
        slog( LOG_INFO, "Creating new group from %d to %d", first, last );
        if( [toc createGroup: @"Untitled Group" from: first - 1 to: last - 1] )
        {
            slog( LOG_INFO, "createGroup: failed" );
        }
        [self refresh];
    }
}


- (IBAction) deleteThings: (id) sender
{
    NSArray		*targets = [self outlineTargets];
    NSEnumerator	*enumer;
    id			node;
    NSString		*type;
    int			trkid, lasttrack = 0, offset = 0;
    
    if( targets && [self canDeleteTargets: targets] )
    {
        enumer = [targets objectEnumerator];

        while( node = [enumer nextObject] )
        {
            type = [node nodeType];
            
            // XXX Cheesey.  Do this with proper polymorphism some time
            if( type == kOmdTrackNode )
            {
                OmdTrackInfo *track = node;
                trkid = [track id];
                
                if( lasttrack && (trkid <= lasttrack ))
                {
                    slog( LOG_ERROR, "Unexpected track order in delete" );
                    break;
                }
                
                slog( LOG_INFO, "Delete track %d", trkid - offset );
                if( [toc deleteTrack: trkid - (offset + 1)] < 0 )
                {
                    break;
                }
                
                offset++;
                lasttrack = trkid;
            }
            else if ( type == kOmdGroupNode )
            {
                OmdGroupInfo *group = node;
                slog( LOG_INFO, "Delete group %d", [group id] );
                [toc deleteGroup: [group id] - 1 ];
            }
            else
            {
                slog( LOG_ERROR, "Tried to delete something we shouldn't!" );
            }
        }
        [self refresh];
    }
}

- (BOOL) validateMenuItem: (id <NSMenuItem>) menuItem
{
    NSString *selectorString = NSStringFromSelector([menuItem action]);
    slog( LOG_DEBUG, "TocController validateMenuItem: %s", 
                    [selectorString lossyCString] );
    
    if( [menuItem action] == @selector( createGroup: ))
    {
        slog( LOG_DEBUG, "Check create group" );
        return [self canCreateGroupFrom: [self outlineTargets]];
    }
    else if( [menuItem action] == @selector( deleteThings: ))
    {
        slog( LOG_DEBUG, "Check Delete stuff" );
        return [self canDeleteTargets: [self outlineTargets]];
    }

    // Default
    return YES;
}




// Delegate methods

//- (BOOL) outlineView: (NSOutlineView *) outlineView 
//    shouldEditTableColumn: (NSTableColumn *) tableColumn 
//    item: (id) item 
//{
//    return NO;
//}


@end
