//
//  MyTree.m
//  celestia
//
//  Created by Bob Ippolito on Thu Jun 20 2002.
//  Copyright (C) 2007, Celestia Development Team
//

#import "MyTree.h"
// HACK
#import "CelestiaFavorite.h"
#import <objc/objc-class.h>
@implementation MyVector
-(void)encodeWithCoder:(NSCoder*)coder
{
//    NSLog(@"[MyVector encodeWithCoder:%@]",coder);
    //[super encodeWithCoder:coder];
    [coder encodeObject:_array];
    //[coder encodeValueOfObjCType:@encode(Class) at:&_myClass];
}
-(id)initWithCoder:(NSCoder*)coder
{
//    NSLog(@"[MyVector initWithCoder:%@]",coder);
    //self = [super initWithCoder:coder];
    self = [self init];
    _array = [coder decodeObject];
    //[coder decodeValueOfObjCType:@encode(Class) at:&_myClass];
    return self;
}
-(id)init
{
    self = [super init];
    _array = [NSMutableArray arrayWithCapacity:0];
    _myClass = [NSObject class];
    return self;
}
-(id)initWithClass:(Class)myClass
{
    self = [self init];
    _myClass = myClass;
    return self;
}
-(void)addObject:(id)obj
{
    if (![obj isKindOfClass:_myClass])
        [NSException raise:@"TypeError" format:@"%@ invalid, only %@ allowed",NSStringFromClass([obj class]),NSStringFromClass(_myClass)];
    [_array addObject:obj];
}
-(void)insertObject:(id)obj atIndex:(NSUInteger)idx
{
    if (![obj isKindOfClass:_myClass])
        [NSException raise:@"TypeError" format:@"%@ invalid, only %@ allowed",NSStringFromClass([obj class]),NSStringFromClass(_myClass)];
    [_array insertObject:obj atIndex:idx];
}
-(void)removeLastObject
{
    [_array removeLastObject];
}
-(void)removeObjectAtIndex:(NSUInteger)idx
{
    [_array removeObjectAtIndex:idx];
}
-(void)replaceObjectAtIndex:(NSUInteger)idx withObject:(id)obj
{
    if (![obj isKindOfClass:_myClass])
        [NSException raise:@"TypeError" format:@"%@ invalid, only %@ allowed",NSStringFromClass([obj class]),NSStringFromClass(_myClass)];
    [_array replaceObjectAtIndex:idx withObject:obj];
}
-(NSUInteger)count
{
    return [_array count];
}
-(id)objectAtIndex:(NSUInteger)idx
{
    return [_array objectAtIndex:idx];
}
@end

@implementation MyTree
-(void)encodeWithCoder:(NSCoder*)coder
{
//    NSLog(@"[MyTree encodeWithCoder:%@]",coder);
    //[super encodeWithCoder:coder];
    [coder encodeObject:_nodeValue];
    [coder encodeObject:_children];
}
-(id)initWithCoder:(NSCoder*)coder
{
//    NSLog(@"[MyTree initWithCoder:%@]",coder);
    //self = [super initWithCoder:coder];
    self = [self init];
    _parent = nil;
    _nodeValue = [coder decodeObject];
    _children = [coder decodeObject];
    [_children makeObjectsPerformSelector:@selector(setParent:) withObject:self];
    return self;
}
-(id)init
{
    self = [super init];
    _nodeValue = nil;
    _children = nil;
    _parent = nil;
    return self;
}
-(id)initWithNode:(id)obj parent:(MyTree*)parent
{
    self = [self init];
    _nodeValue = obj;
    _parent = parent;
    _children = nil;
    return self;
}
-(id)initWithNode:(id)obj parent:(MyTree*)parent children:(NSArray*)children
{
    NSEnumerator* enumerator;
    self = [self initWithNode:obj parent:parent];
    _children = [[MyVector alloc] initWithClass:[MyTree class]];
    enumerator = [children objectEnumerator];
    while ((obj = [enumerator nextObject]) != nil)
        [_children addObject:obj];
    return self;
}
-(id)initWithDictionary:(NSDictionary*)dict parent:(MyTree*)parent
{
    NSMutableArray* children = nil;
    NSArray* origArray = nil;
    NSEnumerator* enumerator = nil;
    NSDictionary* childDict = nil;
    id <NSCoding> nodeValue = nil;
    // NSLog(@"[MyTree initWithDictionary:%@ parent:%@]",dict,parent);
    // this part could use some work
    nodeValue = [[CelestiaFavorite alloc] initWithDictionary:[dict objectForKey:@"nodeValue"]];
    // Leaf
    if ((origArray = [dict objectForKey:@"children"]) == nil)
        return [self initWithNode:nodeValue parent:parent];
    children = [[MyVector alloc] initWithClass:[MyTree class]];
    enumerator = [origArray objectEnumerator];
    while ((childDict = [enumerator nextObject]) != nil)
        [children addObject:[[MyTree alloc] initWithDictionary:childDict parent:self]];
    return [self initWithNode:nodeValue parent:parent children:children];
}
-(MyTree*)parent
{
    return _parent;
}
-(MyVector*)children
{
    return _children;
}
-(id)nodeValue
{
    return _nodeValue;
}
-(BOOL)isLeaf
{
    return (([self children] == nil) ? YES : NO);
}
-(void)setNode:(id)obj
{
    _nodeValue = obj;
}
-(void)setParent:(MyTree*)obj
{
    _parent = obj;
}
-(void)setChildren:(NSArray*)children
{
    NSEnumerator *enumerator = nil;
    id obj = nil;
    if (children == nil) {
        _children = nil;
        return;
    }
    enumerator = [children objectEnumerator];
    _children = [[MyVector alloc] initWithClass:[MyTree class]];
    while ((obj = [enumerator nextObject]) != nil)
        [_children addObject:obj];
}
-(NSDictionary*)dictionary
{
    return [NSDictionary dictionaryWithObjectsAndKeys:[[self nodeValue] dictionary],@"nodeValue",[NSNumber numberWithBool:[self isLeaf]],@"isLeaf",[self children],@"children",nil,nil];
}
-(NSDictionary*)recursiveDictionary
{
    NSMutableArray* array;
    NSEnumerator* enumerator;
    MyTree* obj;
    if ([self isLeaf])
        return [self dictionary];
    enumerator = [[self children] objectEnumerator];
    array = [NSMutableArray arrayWithCapacity:[[self children] count]];
    while ((obj = [enumerator nextObject]) != nil)
        [array addObject:[obj recursiveDictionary]];
    return [NSDictionary dictionaryWithObjectsAndKeys:[[self nodeValue] dictionary],@"nodeValue",[NSArray arrayWithArray:array],@"children",nil,nil];
}
-(NSString*)description
{
    return [[self dictionary] description];
}
-(BOOL)isDescendantOfNode:(MyTree*)node
{
    MyTree* parent = self;
    if ([self isEqualTo:node])
        return YES;
    while ((parent = [parent parent]) != nil)
        if ([node isEqualTo:parent])
            return YES;
    return NO;
}
-(BOOL)isDescendantOfNodeInArray:(NSArray*)array
{
    NSEnumerator* enumerator = [array objectEnumerator];
    MyTree* node = nil;
    while ((node = [enumerator nextObject]) != nil)
        if ([self isDescendantOfNode:node] == YES)
            return YES;
    return NO;
}

- (void)insertChild:(MyTree*)child atIndex:(NSInteger)index {
    [[self children] insertObject:child atIndex:index];
    [child setParent: self];
}

- (void)insertChildren:(NSArray*)children atIndex:(NSInteger)index {
    [[self children] insertObjectsFromArray: children atIndex: index];
    [children makeObjectsPerformSelector:@selector(setParent:) withObject:self];
}

- (void)_removeChildrenIdenticalTo:(NSArray*)children {
    MyTree *child;
    NSEnumerator *childEnumerator = [children objectEnumerator];
    [children makeObjectsPerformSelector:@selector(setParent:) withObject:nil];
    while ((child = [childEnumerator nextObject]) != nil) {
        [[self children] removeObjectIdenticalTo:child];
    }
}

- (void)removeChild:(MyTree*)child {
    [[self children] removeObject:child];
/*
    int index = [self indexOfChild: child];
    if (index != NSNotFound) {
        [self _removeChildrenIdenticalTo: [NSArray arrayWithObject: [self childAtIndex:index]]];
    }
*/
}

- (void)removeFromParent {
    [[self parent] removeChild:self];
}

- (NSUInteger)indexOfChild:(MyTree*)child {
    return [[self children] indexOfObject:child];
}

- (NSUInteger)indexOfChildIdenticalTo:(MyTree*)child {
    return [[self children] indexOfObjectIdenticalTo:child];
}

- (NSUInteger)numberOfChildren {
    return [[self children] count];
}

- (MyTree*)firstChild {
    return [[self children] objectAtIndex:0];
}

- (MyTree*)lastChild {
    return [[self children] lastObject];
}

- (MyTree*)childAtIndex:(NSInteger)index {
    return [[self children] objectAtIndex:index];
}

// Returns the minimum nodes from 'allNodes' required to cover the nodes in 'allNodes'.
// This methods returns an array containing nodes from 'allNodes' such that no node in
// the returned array has an ancestor in the returned array.

// There are better ways to compute this, but this implementation should be efficient for our app.
+ (NSArray *) minimumNodeCoverFromNodesInArray: (NSArray *)allNodes {
    NSMutableArray *minimumCover = [NSMutableArray array];
    NSMutableArray *nodeQueue = [NSMutableArray arrayWithArray:allNodes];
    MyTree *node = nil;
    while ([nodeQueue count]) {
        node = [nodeQueue objectAtIndex:0];
        [nodeQueue removeObjectAtIndex:0];
        while ( [node parent] && [nodeQueue containsObjectIdenticalTo:[node parent]] ) {
            [nodeQueue removeObjectIdenticalTo: node];
            node = [node parent];
        }
        if (![node isDescendantOfNodeInArray: minimumCover]) [minimumCover addObject: node];
        [nodeQueue removeObjectIdenticalTo: node];
    }
    return minimumCover;
}

@end
