/*
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * Copyright (c) 1999-2003 Apple Computer, Inc.  All Rights Reserved.
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */


#import "IORegistryController.h"

@implementation IORegistryController

- init {
    if (self = [super init]) {
        int plane;
        
        _rootNode = [[IORegOutlineViewNode alloc] initWithName:@"Root" value:@"Root"];
        _detailRootNode = [[IORegDetailOutlineViewNode alloc] init];
        
        if ([[[NSUserDefaults standardUserDefaults] objectForKey:@"IORegistryPlane"] intValue] == 1) {
            plane = kIOService_Plane;
        } else {
            plane = kIOUSB_Plane;
        }
        _infoGatherer = [[IORegInfoGatherer alloc] initWithListener:self rootNode:_rootNode plane:plane];
    }
    return self;
}

- (void)dealloc {
    [_rootNode release];
    [_detailRootNode release];
    [_infoGatherer release];
    [super dealloc];
}

- (void)awakeFromNib {
    if ([[NSUserDefaults standardUserDefaults] boolForKey:@"IORegistryDontAutoRefresh"] == YES) {
        [RefreshCheckBox setState:NSOffState];
//enable/disable button        [RefreshButton setEnabled:YES];
    } else {
        [RefreshCheckBox setState:NSOnState];
//enable/disable button        [RefreshButton setEnabled:NO];
    }
    
    if ([[[NSUserDefaults standardUserDefaults] objectForKey:@"IORegistryPlane"] intValue] == 1) {
        [PlanePopUpButton selectItemAtIndex:1];
    } else {
        [PlanePopUpButton selectItemAtIndex:0];
    }
    
    [IORegOutputOV setTarget:self];
    [IORegOutputOV setAction:@selector(ioregItemSingleClicked:)];
    [IORegOutputOV setDoubleAction:@selector(ioregItemDoubleClicked:)];
    
    [IORegDetailedOutput setTarget:IORegDetailedOutput];
    [IORegDetailedOutput setDoubleAction:@selector(itemDoubleClicked)];
    
    [self expandOutlineViewItems];
}

- (IBAction)Refresh:(id)sender {
    [_detailRootNode removeAllChildren];
    [IORegDetailedOutput reloadData];
    
    [_infoGatherer refreshData:YES];
}

- (IBAction)SaveOutput:(id)sender {
    NSSavePanel *sp = [NSSavePanel savePanel];
    int result;
    
    [sp setRequiredFileType:@"txt"];
    result = [sp runModalForDirectory:NSHomeDirectory() file:@"IORegistry"];
    if (result == NSOKButton) {
        NSString *finalString = [_rootNode stringRepresentationOfValues:0];

        if (![finalString writeToFile:[sp filename] atomically:YES])
            NSBeep();
    }
}

- (IBAction)ToggleAutoRefresh:(id)sender {
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    
    if ([sender state] == NSOffState) {
        [defaults setBool:YES forKey:@"IORegistryDontAutoRefresh"];
//enable/disable button        [RefreshButton setEnabled:YES];
    } else {
        [defaults setBool:NO forKey:@"IORegistryDontAutoRefresh"];
//enable/disable button        [RefreshButton setEnabled:NO];
        [self Refresh:self];
    }
}

- (IBAction)TogglePlane:(id)sender {
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    
    if ([sender indexOfSelectedItem] == 0) {
        [defaults setObject:[NSNumber numberWithInt:0] forKey:@"IORegistryPlane"];
        [_infoGatherer setPlane:0];
    } else if ([sender indexOfSelectedItem] == 1) {
        [defaults setObject:[NSNumber numberWithInt:1] forKey:@"IORegistryPlane"];
        [_infoGatherer setPlane:1];
    }
    
    [self Refresh:self];
}

- (void)ioRegInfoGathererInformationDidChange:(IORegInfoGatherer *)aGatherer {
    [IORegOutputOV reloadData];
    [self expandOutlineViewItems];
}

- (void)expandOutlineViewItems {
    NSEnumerator *enumerator;
    id thisNode;
    
    enumerator = [[_rootNode children] objectEnumerator];
    while (thisNode = [enumerator nextObject]) {
        if ([PlanePopUpButton indexOfSelectedItem] == 0) {
            [IORegOutputOV expandItem:thisNode expandChildren:YES];
        } else {
            NSEnumerator *childEnumerator = [[thisNode children] objectEnumerator];
            id thisChild;
            
            [IORegOutputOV expandItem:thisNode];
            
            while(thisChild = [childEnumerator nextObject]) {
                [IORegOutputOV expandItem:thisChild];
            }
        }
    }
}

- (id)outlineView:(NSOutlineView *)ov child:(int)index ofItem:(id)item
{
    if (ov == IORegOutputOV) {
        if (item == nil) {
            return [_rootNode childAtIndex:index];
        }
        return [item childAtIndex:index];
    } else if (ov = IORegDetailedOutput) {
        if (item == nil) {
            return [_detailRootNode childAtIndex:index];
        }
        return [item childAtIndex:index];
    }
    
    return nil;
}

- (BOOL)outlineView:(NSOutlineView *)ov isItemExpandable:(id)item
{
    return [item isExpandable];
}

- (int)outlineView:(NSOutlineView *)ov numberOfChildrenOfItem:(id)item
{
    if (ov == IORegOutputOV) {
        if (item == nil) {
            return [_rootNode childrenCount];
        }
        return [item childrenCount];
    } else if (ov = IORegDetailedOutput) {
        if (item == nil) {
            return [_detailRootNode childrenCount];
        }
        return [item childrenCount];
    }
    
    return 0;
}

- (id)outlineView:(NSOutlineView *)ov objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
{   
    if (ov == IORegOutputOV) {
        return [item value];
    } else if (ov = IORegDetailedOutput) {
        if ([[tableColumn identifier] intValue] == 0) {
            return [item name];
        } else if ([[tableColumn identifier] intValue] == 1) {
            return [(IORegDetailOutlineViewNode *)item type];
        } else if ([[tableColumn identifier] intValue] == 2) {
            return [item value];
        }
    }
    return nil;
    
}

- (void)outlineViewSelectionDidChange:(NSNotification *)aNotification {
    if ([aNotification object] == IORegOutputOV) {
        [_detailRootNode removeAllChildren];
        [IORegDetailedOutput reloadData];
    }
}

- (void)ioregItemSingleClicked:(id)sender {
        if ([IORegDetailedOutputDrawer state] == NSDrawerOpenState) {
            IORegOutlineViewNode *item = [sender itemAtRow:[sender selectedRow]];
            if ([item representedDevice] != NULL) {
                NSMutableDictionary *propertiesDict;

                if (IORegistryEntryCreateCFProperties( [item representedDevice], (CFMutableDictionaryRef *)&propertiesDict, kCFAllocatorDefault, 0 ) == 0) {
                    [_detailRootNode removeAllChildren];
                    [self populateNode:_detailRootNode withContentsOfDictionary:propertiesDict];
                    [IORegDetailedOutput reloadData];
                    CFRelease(propertiesDict);
                }
            }
        }
}

- (void)ioregItemDoubleClicked:(id)sender {
    [sender itemDoubleClicked];
}

- (void)populateNode:(IORegDetailOutlineViewNode *)node withKey:(NSString *)key value:(id)value {
    IORegDetailOutlineViewNode *   aNewNode;
    
    if (CFGetTypeID(value) == CFStringGetTypeID()) {
        aNewNode = [[IORegDetailOutlineViewNode alloc] initWithName:(NSString *)key value:(NSString *)value];
        [aNewNode setType:@"String"];
        [node addChild:aNewNode];
        [aNewNode release];
    } else if (CFGetTypeID(value) == CFNumberGetTypeID()) {
        switch (CFNumberGetType((CFNumberRef)value)) {
            case kCFNumberSInt32Type:
                aNewNode = [[IORegDetailOutlineViewNode alloc] initWithName:(NSString *)key value:[NSString stringWithFormat:@"%@ (0x%x)",[value description], [value longValue]]];
                break;
            case kCFNumberSInt64Type:
                aNewNode = [[IORegDetailOutlineViewNode alloc] initWithName:(NSString *)key value:[NSString stringWithFormat:@"%@ (0x%x%x)",[value description], [value longLongValue],[value longValue]]];
                break;
            default:
                 aNewNode = [[IORegDetailOutlineViewNode alloc] initWithName:(NSString *)key value:[NSString stringWithFormat:@"%@ (0x%x)",[value description], [value intValue]]];
        }
        [aNewNode setType:@"Number"];
        [node addChild:aNewNode];
        [aNewNode release];
    } else if (CFGetTypeID(value) == CFDataGetTypeID()) {
        aNewNode = [[IORegDetailOutlineViewNode alloc] initWithName:(NSString *)key value:[value description]];
        [node addChild:aNewNode];
        [aNewNode release];
    } else if (CFGetTypeID(value) == CFDictionaryGetTypeID()) {
        aNewNode = [[IORegDetailOutlineViewNode alloc] initWithName:(NSString *)key value:@""];
        [aNewNode setType:@"Dictionary"];
        [self populateNode:aNewNode withContentsOfDictionary:value];
        [node addChild:aNewNode];
        [aNewNode release];
    } else if (CFGetTypeID(value) == CFArrayGetTypeID()) {
        aNewNode = [[IORegDetailOutlineViewNode alloc] initWithName:(NSString *)key value:@""];
        [aNewNode setType:@"Array"];
        [self populateNode:aNewNode withContentsOfArray:value];
        [node addChild:aNewNode];
        [aNewNode release];
    }
}

- (void)populateNode:(IORegDetailOutlineViewNode *)node withContentsOfDictionary:(NSDictionary *)dictionary {
    NSMutableArray *    keys = [[dictionary allKeys] mutableCopy];
    NSEnumerator *      enumerator;
    NSString *          key;

    [keys sortUsingSelector:@selector(caseInsensitiveCompare:)];
    enumerator = [keys objectEnumerator];
    
    while (key = [enumerator nextObject]) {
        [self populateNode:node withKey:key value:[dictionary objectForKey:key]];
    }
    
    [keys release];
}

- (void)populateNode:(IORegDetailOutlineViewNode *)node withContentsOfArray:(NSArray *)array {
    NSEnumerator *      enumerator = [array objectEnumerator];
    id                  value;
    int                 counter = 0;
    
    while (value = [enumerator nextObject]) {
        [self populateNode:node withKey:[NSString stringWithFormat:@"%d",counter] value:value];
        counter++;
    }
}

@end
