/*
 ** AqMetaNode
 ** 
 ** CHANGES:
 ** 0.22 (pozytron) Added Unknown Artist/Album                                                                            ** are still broken).
 */

#import "AqMetaNode.h"
#import "AqMetaNode(Drawing).h"
#import "AqMetaTableView.h"


/* NSMutableArray */

    @implementation NSMutableArray (FastRemove)

        - (void) fastRemoveObjectIdenticalTo: (id) theObject; {
            int index = [self indexOfObjectIdenticalTo: theObject];
            if (index != NSNotFound) [self removeObjectAtIndex: index];
        }

    @end


/* MetaNode */

    @implementation AqMetaNode
    
        - (id) initWithName: (id) theName; {
            self = [self init];
            name = [theName retain];
            return self;
        }
        
        - (id) init; {
            files[0] = [[NSMutableArray alloc] init];
            files[1] = [[NSMutableArray alloc] init];
            sfiles[0] = [[NSMutableSet alloc] init];
            sfiles[1] = [[NSMutableSet alloc] init];
            return [super init];
        }
    
        - (void) dealloc; {
            //NSLog(@"AqMetaNode dealloc");
            [files[0] release];
            [files[1] release];
            [sfiles[0] release];
            [sfiles[1] release];
            [nodes release];
            [primary release];
            [secondary release];
            [name release];
            [super dealloc];
        }

        - (id) copyWithZone: (id) theZone; {
            return [self retain];
        }
        
        - (NSComparisonResult) caseInsensitiveCompare: (id) otherObject; {
            return [name caseInsensitiveCompare: [otherObject name]];
        }
        
        - (void) showFile: (id) theFile; {
            if (![sfiles[0] containsObject: theFile]) {
                [files[0] addObject: theFile];
                [sfiles[0] addObject: theFile];
            }
            if ([sfiles[1] containsObject: theFile]) {
                [files[1] fastRemoveObjectIdenticalTo: theFile];
                [sfiles[1] removeObject: theFile];
            }
        }
        
        - (void) hideFile: (id) theFile; {
            if (![sfiles[1] containsObject: theFile]) {
                [files[1] addObject: theFile];
                [sfiles[1] addObject: theFile];
            }
            if ([sfiles[0] containsObject: theFile]) {
                [files[0] fastRemoveObjectIdenticalTo: theFile];
                [sfiles[0] removeObject: theFile];
            }
        }
        
        - (void) handleFile: (AqRemoteFile*) theFile isVisible: (BOOL) visible; {
            if (visible) {
                [self showFile: theFile];
            } else {
                [self hideFile: theFile];
            }
            [secondary release];
            secondary = nil;
            [metaView setNeedsDisplay: YES];
        }

        - (void) sort; {
        }
    
        - (id) nodes; {
            return nil;
        }
        
        - (id) files; {
            return files[0];
        }
        
        - (id) hiddenFiles; {
            return files[1];
        }
        
        - (id) name; {
            return name; 
        }

        - (int) hiddenCount; {
            return [files[1] count];
        }

        - (int) visibleCount; {
            return [files[0] count];
        }

        - (int) totalCount; {
            return [files[0] count] + [files[1] count];
        }
    
    @end


/* Root */

    @implementation AqRootNode
    
        #define ALLTYPES NSLocalizedStringFromTable(@"All Types", @"MetaBrowser", @"")
        #define MUSIC NSLocalizedStringFromTable(@"Music", @"MetaBrowser", @"")
        #define PICTURES NSLocalizedStringFromTable(@"Pictures", @"MetaBrowser", @"")
        #define MOVIES NSLocalizedStringFromTable(@"Movies", @"MetaBrowser", @"")
        #define DOCUMENTS NSLocalizedStringFromTable(@"Text", @"MetaBrowser", @"")
        #define FILES NSLocalizedStringFromTable(@"Files", @"MetaBrowser", @"")
        
        - (id) init; 
        {
            self = [super init];

            allTypesNode = [[AqAllTypesNode alloc] initWithName: ALLTYPES parent: self];
            musicNode     = [[AqMusicNode alloc] initWithName: MUSIC];
            imageNode     = [[AqImageNode alloc] initWithName: PICTURES];
            videoNode     = [[AqVideoNode alloc] initWithName: MOVIES];
            documentsNode = [[AqDocumentsNode alloc] initWithName: DOCUMENTS];
            filesNode     = [[AqFilesNode alloc] initWithName: FILES];

            nodes = [[NSArray arrayWithObjects: allTypesNode, musicNode, imageNode, videoNode, documentsNode, filesNode, nil] retain];

            return self;
        }
        
        - (void) dealloc; {
            [allTypesNode release];
            [musicNode release];
            [imageNode release];
            [videoNode release];
            [documentsNode release];
            [filesNode release];
            [super dealloc];
        }

        - (id) nodes; {
            return nodes;
        }
    
        - (void) handleFile: (AqRemoteFile*) theFile isVisible: (BOOL) visible; {
            switch([theFile mediaType]) {
                case kAqMusic:
                    [musicNode handleFile: theFile isVisible: visible];
                    break;
                    
                case kAqVideo:
                    [videoNode handleFile: theFile isVisible: visible];
                    break;
                    
                case kAqImage:
                    [imageNode handleFile: theFile isVisible: visible];
                    break;
                    
                case kAqDocument:
                    [documentsNode handleFile: theFile isVisible: visible];
                    break;

                case kAqFile:
                    [filesNode handleFile: theFile isVisible: visible];
                    break;
            }
            [allTypesNode handleFile: theFile isVisible: visible];
        }

        - (int) hiddenCount; {
            return [allTypesNode hiddenCount];
        }

        - (int) visibleCount; {
            return [allTypesNode visibleCount];
        }

        - (int) totalCount; {
            return [allTypesNode totalCount];
        }

        - (id) allTypesNode; {
            return allTypesNode;
        }
            
    @end
    
    
    @implementation AqAllTypesNode
    
        - (id) initWithName: (id) theName parent: (id) theParent; {
            self = [self initWithName: theName];
            parent = theParent;
            musicNode = [[AqMusicNode alloc] init];
            return self;
        }

        - (void) dealloc; {
            [musicNode release];
            [super dealloc];
        }
        
        - (void) handleFile: (id) theFile isVisible: (BOOL) visible; {
            [super handleFile: theFile isVisible: visible];
            [musicNode handleFile: theFile isVisible: visible];
        }
        
        - (id) nodes; {
            return [musicNode nodes];
        }
        
        - (void) sort; {
            [((AqMusicNode*)musicNode)->nodes sortUsingSelector: @selector(caseInsensitiveCompare:)];
        }
    
    @end


/* Music */

    @implementation AqSortingNode

        - (id) init; {
            nodes = [[NSMutableArray alloc] init];
            nameToNode = [[NSMutableDictionary alloc] init];
            return [super init];
        }

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

        - (id) nodes; {
            id result = [[[NSMutableArray alloc] init] autorelease];
            [result addObject: allFilesNode];
            [result addObjectsFromArray: nodes];
            //int i, count = [nodes count];
            //for (i=0; i<count; i++) {
            //    id node = [nodes objectAtIndex: i];
            //    if ([node visibleCount]) {
            //        [result addObject: node];
            //    }
            //}
            return result;
        }
        
        - (BOOL) string: (id) a isCloseToString: (id) b; {
            a = [a lowercaseString];
            b = [b lowercaseString];
            if ([a isEqualToString: b])	return YES;
            if ([a hasPrefix: b]) return YES;
            if ([b hasPrefix: a]) return YES;
            return NO;
        }
        
        - (void) sort; {
            if(metaView) [nodes sortUsingSelector: @selector(caseInsensitiveCompare:)];
        }

    @end


    @implementation AqMusicNode
        
        #define ALLARTISTS NSLocalizedStringFromTable(@"All Artists", @"MetaBrowser", @"")
        #define UNKNOWNARTIST NSLocalizedStringFromTable(@"Unknown Artist", @"MetaBrowser", @"")
        
        - (id) init; {
            allFilesNode = [[AqAllArtistsNode alloc] initWithName: ALLARTISTS];
            [self setImage: [AqMetaNode musicImage]];
            return [super init];
        }

        #define METANAME theFile->artist
        #define METAKEY  theFile->artistlc
        #define SUBNODE  AqArtistNode
        
        - (void) handleFile: (AqRemoteFile*) theFile isVisible: (BOOL) visible; 
        {
            [super handleFile: theFile isVisible: visible];
            [allFilesNode handleFile: theFile isVisible: visible];
            
            id node;
            if (METANAME) {
                id key = METAKEY;
                
                if (!(node = [nameToNode objectForKey: key])) {
                    node = [[[SUBNODE alloc] initWithName: METANAME] autorelease];
                    [nameToNode setObject: node forKey: key];
                    [nodes addObject: node];
                    [metaView nodeWasAdded];
                }

                [node handleFile: theFile isVisible: visible];
                
                        //[metaView nodeWasAdded];
                //if (visible) {
                //    if ([node visibleCount] == 1) {
                //        /* node was just created */
                //        [metaView nodeWasAdded];
                //    }
                //} else {
                //    if ([node visibleCount] == 0) {
                        /* node visible count just dropped to zero */
                //        [metaView nodeWasAdded];
                //    }
                //}
            } else if ([theFile mediaType] == kAqMusic) {
                if (!(node = [nameToNode objectForKey: @"UnknownArtist"])) {
                    node = [[[SUBNODE alloc] initWithName: UNKNOWNARTIST] autorelease];
                    [nameToNode setObject: node forKey: @"UnknownArtist"];
                    [nodes addObject: node];
                    [metaView nodeWasAdded];
                }
                [node handleFile: theFile isVisible: visible];
            }
        }
        
        #undef METANAME
        #undef METAKEY
        #undef SUBNODE
        
    @end
    
    
    @implementation AqArtistNode
    
        #define ALLALBUMS NSLocalizedStringFromTable(@"All Albums", @"MetaBrowser", @"")
        #define UNKNOWNALBUM NSLocalizedStringFromTable(@"Unknown Album", @"MetaBrowser", @"")

        - (id) init; {
            allFilesNode = [[AqMetaNode alloc] initWithName: ALLALBUMS];
            [self setImage: [AqMetaNode artistImage]];
            return [super init];
        }
        
        #define METANAME theFile->album
        #define METAKEY  theFile->albumlc
        #define SUBNODE  AqAlbumNode

        - (void) handleFile: (AqRemoteFile*) theFile isVisible: (BOOL) visible; 
        {
            [super handleFile: theFile isVisible: visible];
            [allFilesNode handleFile: theFile isVisible: visible];
            
            id node;
            if (METANAME) {
                id key = METAKEY;
                
                if (!(node = [nameToNode objectForKey: key])) {
                    node = [[[SUBNODE alloc] initWithName: METANAME] autorelease];
                    [nameToNode setObject: node forKey: key];
                    [nodes addObject: node];
                    [metaView nodeWasAdded];
                }

                [node handleFile: theFile isVisible: visible];
            } else if ([theFile mediaType] == kAqMusic) {
                if (!(node = [nameToNode objectForKey: @"UnknownAlbum"])) {
                    node = [[[SUBNODE alloc] initWithName: UNKNOWNALBUM] autorelease];
                    [nameToNode setObject: node forKey: @"UnknownAlbum"];
                    [nodes addObject: node];
                    [metaView nodeWasAdded];
                }
                [node handleFile: theFile isVisible: visible];
            }
        }

        #undef METANAME
        #undef METAKEY
        #undef SUBNODE

    @end
    
    
    @implementation AqAllArtistsNode
        
        - (id) init; {
            [super init];
            [self setImage: nil];
            return self;
        }
        
    @end
    
    
    @implementation AqAlbumNode
    
        - (id) init; {
            [self setImage: [AqMetaNode albumImage]];
            return [super init];
        }
    
    @end


/* Misc */

    @implementation AqImageNode
    
        - (id) init; {
            [self setImage: [AqMetaNode imageImage]];
            return [super init];
        }
        
    @end
    
    
    @implementation AqVideoNode
        
        - (id) init; {
            [self setImage: [AqMetaNode videoImage]];
            return [super init];
        }

    @end


    @implementation AqDocumentsNode
        
        - (id) init; {
            [self setImage: [AqMetaNode documentsImage]];
            return [super init];
        }

    @end
    
    
    @implementation AqFilesNode
        
        - (id) init; {
            [self setImage: [AqMetaNode filesImage]];
            return [super init];
        }
    
    @end