/*
SRIconInstaller.m

Author: Makoto Kinoshita

Copyright 2004 The Shiira Project. All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted 
provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions 
  and the following disclaimer.

  2. 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.

THIS SOFTWARE IS PROVIDED BY THE SHIIRA PROJECT ``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 SHIIRA PROJECT 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.
*/

#import "SRDefaultsKey.h"

#import "SRIconInstaller.h"

int  SRIconInstalOK = 0;
int  SRIconInstalNG = -1;

// Icon names
static NSArray* _allIconNames = nil;
static NSArray* _defaultIconNames = nil;

NSString*   SRGoBackIconName = @"SRGoBack";
NSString*   SRGoForward = @"SRGoForward";
NSString*   SRReloadPageIconName = @"SRReloadPage";
NSString*   SRStopLoadingIconName = @"SRStopLoading";
NSString*   SRGoHomeIconName = @"SRGoHome";
NSString*   SRSidebarIconName = @"SRSidebar";
NSString*   SRBookmarkPageIconName = @"SRBookmarkPage";
NSString*   SRNewTabIconName = @"SRNewTab";
NSString*   SRCloseTabIconName = @"SRCloseTab";
NSString*   SRBiggerTextIconName = @"SRBiggerText";
NSString*   SRSmallerTextIconName = @"SRSmallerText";
NSString*   SRCustomizeIconName = @"SRCustomize";
NSString*   SRBrowseDefaultIconName = @"SRBrowseDefault";
NSString*   SRBrowseTabIconName = @"SRBrowseTab";
NSString*   SRBrowseNewTabIconName = @"SRBrowseNewTab";
NSString*   SRBrowseBackTabIconName = @"SRBrowseBackTab";

NSString*   SRGeneralPreferencesIconName = @"SRGeneral";
NSString*   SRAppearancePreferencesIconName = @"SRAppearance";
NSString*   SRBookmarkPreferencesIconName = @"SRBookmarks";
NSString*   SRTabsPreferencesIconName = @"SRTabs";
NSString*   SRSecurityPreferencesIconName = @"SRSecurity";
NSString*   SRAdvancedPreferencesIconName = @"SRAdvanced";
NSString*   SRIconPreferencesIconName = @"SRIcon";
NSString*   SRSidebarPreferencesIconName = @"SRSidebarPref";
NSString*   SRSourcePreferencesIconName = @"SRSource";
NSString*   SRRSSPreferencesIconName = @"SRRSS";

// Setting file names
NSString*   SRIconsDirectory = @"Shiira/Icons";

@interface SRIconInstaller (private)
- (void)_downloadNextFile;
@end

@implementation SRIconInstaller

//--------------------------------------------------------------//
#pragma mark -- Initialize --
//--------------------------------------------------------------//

- (void)_loadDefaultIcon:(int)texture
{
    NSMutableDictionary*    icons;
    icons = [NSMutableDictionary dictionary];
    
    // Get default icon plist
    NSString*       plistPath = nil;
    NSDictionary*   plist;
    switch (texture) {
    case SRIconTextureAqua: {
        plistPath = [[NSBundle mainBundle] pathForResource:@"ShiiraIcons" ofType:@"plist"];
        break;
    }
    case SRIconTextureMetal: {
        plistPath = [[NSBundle mainBundle] pathForResource:@"ShiiraIcons" 
                ofType:@"plist" inDirectory:@"metal"];
        break;
    }
    }
    if (!plistPath) {
        // Could not find plist
        SR_WARNING(@"Could not find plist");
        return;
    }
    plist = [NSDictionary dictionaryWithContentsOfFile:plistPath];
    if (!plist) {
        // Invalid plist
        SR_WARNING(@"Invalid plist");
        return;
    }
    [icons setObject:plist forKey:@"ShiiraIcons"];
    
    // Get icon directory
    NSString*   iconDir;
    iconDir = [plistPath stringByDeletingLastPathComponent];
    
    // Load default icons
    NSEnumerator*   iconNameEnum;
    NSString*       iconName;
    iconNameEnum = [_allIconNames objectEnumerator];
    while (iconName = [iconNameEnum nextObject]) {
        NSImage*    iconImage = nil;
        
        // Get icon image
        NSString*   fileName;
        fileName = [[plist objectForKey:@"icons"] objectForKey:iconName];
        if (fileName) {
            iconImage = [[NSImage alloc] initWithContentsOfFile:
                    [iconDir stringByAppendingPathComponent:fileName]];
        }
        
        // Set it dictionary
        if (iconImage) {
            [icons setObject:iconImage forKey:iconName];
            [iconImage release];
        }
        else {
            // Warning
            SR_WARNING(@"Could not find icon %@ in default", iconName);
        }
    }
    
    // Set icon dictionary root
    switch (texture) {
    case SRIconTextureAqua: {
        [_icons setObject:icons forKey:@"default"];
        break;
    }
    case SRIconTextureMetal: {
        [_icons setObject:icons forKey:@"metalDefault"];
        break;
    }
    }
}

- (void)_loadIconsAtUserLibraryName:(NSString*)iconsDirectory
{
    NSFileManager*  fileMgr;
    fileMgr = [NSFileManager defaultManager];
    
    // Create file path for plist
    NSString* iconsPlistPath;
    iconsPlistPath = [iconsDirectory stringByAppendingPathComponent:@"ShiiraIcons.plist"];
    
    // Check existense
    if (![fileMgr fileExistsAtPath:iconsPlistPath]) {
        // Could not find plist
        SR_WARNING(@"Could not find plist at %@", iconsPlistPath);
        return;
    }
    
    // Load plist
    NSDictionary*   plist;
    plist = [NSDictionary dictionaryWithContentsOfFile:iconsPlistPath];
    if (!plist) {
        // Could not read plist
        SR_WARNING(@"Could not read plist");
        return;
    }
    
    // Check unique name
    NSString*   uniqueName;
    uniqueName = [plist objectForKey:@"uniqueName"];
    if (!uniqueName) {
        // Could not read plist
        SR_WARNING(@"Could not find uniqueName in plist, %@", iconsPlistPath);
        return;
    }
    if ([self iconsOfUniqueName:uniqueName]) {
        // This unique name is already used
        //SR_WARNING(@"This unique name is already used, %@", uniqueName);
        return;
    }

    // Create dictionary for icons
    NSMutableDictionary*    icons;
    icons = [NSMutableDictionary dictionary];
    [icons setObject:plist forKey:@"ShiiraIcons"];
    
    // Load icons
    NSEnumerator*   iconNameEnum;
    NSString*       iconName;
    iconNameEnum = [_allIconNames objectEnumerator];
    while (iconName = [iconNameEnum nextObject]) {
        NSString*   iconFileName = nil;
        NSImage*    iconImage = nil;
        
        // Load icon image from directory
        iconFileName = [[plist objectForKey:@"icons"] objectForKey:iconName];
        if (iconFileName) {
            NSString*   iconPath;
            iconPath = [iconsDirectory stringByAppendingPathComponent:iconFileName];
            iconImage = [[NSImage alloc] initWithContentsOfFile:iconPath];
            [iconImage autorelease];
        }
        
        // If icon directory has no image, user default
        if (!iconImage) {
            iconImage = [[_icons objectForKey:@"default"] objectForKey:iconName];
        }
        if (!iconImage) {
            // Could not find icon
            SR_WARNING(@"Could not find icon %@ for %@", iconName, uniqueName);
        }
        
        // Set it dictionary
        if (iconImage) {
            [icons setObject:iconImage forKey:iconName];
        }
    }
    
    // Set icon dictionary root
    [_icons setObject:icons forKey:[iconsDirectory lastPathComponent]];
}

- (void)_loadIconsAtDirectory:(NSString*)directory
{
    NSFileManager*  fileMgr;
    fileMgr = [NSFileManager defaultManager];
    
    // Check existense
    BOOL    isDir;
    if ([fileMgr fileExistsAtPath:directory isDirectory:&isDir] && isDir) {
        // Get contents of directory
        NSArray*    contents;
        contents = [fileMgr directoryContentsAtPath:directory];
        
        // Enumerate contents
        NSEnumerator*   enumerator;
        NSString*       dirName;
        enumerator = [contents objectEnumerator];
        while (dirName = [enumerator nextObject]) {
            NSString*   path;
            path = [directory stringByAppendingPathComponent:dirName];
            
            // Load icons
            if ([fileMgr fileExistsAtPath:path isDirectory:&isDir] && isDir) {
                [self _loadIconsAtUserLibraryName:path];
            }
        }
    }
}

- (id)init
{
    self = [super init];
    if (!self) {
        return nil;
    }
    
    // Create icon dictionary
    if (!_allIconNames) {
        _allIconNames = [[NSArray arrayWithObjects:
            SRGoBackIconName, SRGoForward, SRReloadPageIconName, SRStopLoadingIconName, 
            SRGoHomeIconName, SRSidebarIconName, SRBookmarkPageIconName, SRNewTabIconName, 
            SRCloseTabIconName, SRBiggerTextIconName, SRSmallerTextIconName, SRCustomizeIconName, 
            SRBrowseDefaultIconName, SRBrowseTabIconName, SRBrowseNewTabIconName, SRBrowseBackTabIconName, 
            
            SRGeneralPreferencesIconName, SRAppearancePreferencesIconName, 
            SRBookmarkPreferencesIconName, SRTabsPreferencesIconName, 
            SRSecurityPreferencesIconName, SRAdvancedPreferencesIconName, 
            SRIconPreferencesIconName, SRSidebarPreferencesIconName, 
            SRSourcePreferencesIconName, SRRSSPreferencesIconName, nil] retain];
        _defaultIconNames = [[NSArray arrayWithObjects:
            SRGoBackIconName, SRGoForward, SRReloadPageIconName, SRStopLoadingIconName, 
            SRGoHomeIconName, nil] retain];
    }
    _icons = [[NSMutableDictionary dictionary] retain];
    
    // Load default icons
    [self _loadDefaultIcon:SRIconTextureAqua];
    [self _loadDefaultIcon:SRIconTextureMetal];
    
    // Load icons in the application bundle
    NSString*       iconsDirectory;
    iconsDirectory = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"icons"];
    [self _loadIconsAtDirectory:iconsDirectory];
    
    // Load icons at ~/Library/Shiira/Icons
    NSArray*	libraryPaths;
    libraryPaths = NSSearchPathForDirectoriesInDomains(
            NSLibraryDirectory, NSUserDomainMask, YES);
    iconsDirectory = [[libraryPaths objectAtIndex:0] 
            stringByAppendingPathComponent:SRIconsDirectory];
    [self _loadIconsAtDirectory:iconsDirectory];
    
    return self;
}

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

+ (NSDictionary*)iconPlistAtPath:(NSString*)path
{
    // Get dictionary at path
    NSDictionary*   plist;
    plist = [NSDictionary dictionaryWithContentsOfFile:path];
    if (!plist) {
        return nil;
    }
    
    // Check 'shiiraVersion'
    if (![plist objectForKey:@"shiiraVersion"]) {
        // Not Shiira icon plist
        SR_WARNING(@"Not Shiira icon plist");
        return nil;
    }
    
    return plist;
}

+ (NSDictionary*)iconPlistOfData:(NSData*)data
{
    // Make data UTF-8 string
    NSString*   string;
    string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    if (!string) {
        // Failed to encode
        SR_WARNING(@"Failed to encode UTF-8");
        return nil;
    }
    
    // Make string dictionary
    id  plist;
    plist = [string propertyList];
    if (!plist) {
        // Failed to make plist
        SR_WARNING(@"Failed to make plist");
        return nil;
    }
    if (![plist isKindOfClass:[NSDictionary class]]) {
        // Not dictionary
        SR_WARNING(@"Not dictionary");
        return nil;
    }
    
    // Check 'shiiraVersion'
    if (![plist objectForKey:@"shiiraVersion"]) {
        // Not Shiira icon plist
        SR_WARNING(@"Not Shiira icon plist");
        return nil;
    }
    
    return plist;
}

- (NSDictionary*)icons
{
    return _icons;
}

- (NSDictionary*)iconsOfName:(NSString*)name
{
    NSDictionary*   icons;
    icons = [_icons objectForKey:name];
    if (!icons) {
        icons = [_icons objectForKey:@"default"];
    }
    
    return icons;
}

- (NSDictionary*)iconsOfUniqueName:(NSString*)uniqueName
{
    NSEnumerator*   enumerator;
    NSDictionary*   icons;
    enumerator = [_icons objectEnumerator];
    while (icons = [enumerator nextObject]) {
        // Get plist
        NSDictionary*   plist;
        plist = [icons objectForKey:@"ShiiraIcons"];
        if (!plist) {
            continue;
        }
        
        // Check unique name
        if ([uniqueName isEqualToString:[plist objectForKey:@"uniqueName"]]) {
            return icons;
        }
    }
    
    return nil;
}

- (NSArray*)iconImagesOfName:(NSString*)name
{
    NSDictionary*   icons;
    icons = [_icons objectForKey:name];
    if (!icons) {
        icons = [_icons objectForKey:@"default"];
    }
    
    // Get only icon image
    NSMutableArray* images;
    NSEnumerator*   enumerator;
    NSString*       iconName;
    images = [NSMutableArray array];
    enumerator = [_allIconNames objectEnumerator];
    while (iconName = [enumerator nextObject]) {
        id  object;
        object = [icons objectForKey:iconName];
        if (object && [object isKindOfClass:[NSImage class]]) {
            [images addObject:object];
        }
    }
    
    return images;
}

- (void)updateIconPopup:(NSPopUpButton*)popup forTexture:(int)texture
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Remove all items
    [popup removeAllItems];
    
    // Create menu
    NSMenu* menu;
    menu = [[NSMenu alloc] init];
    [menu autorelease];
    
    // Add item
    NSEnumerator*   keyEnum;
    NSString*       key;
    NSDictionary*   icons;
    keyEnum = [_icons keyEnumerator];
    while (key = [keyEnum nextObject]) {
        // Get icon dictionary
        icons = [_icons objectForKey:key];
        
        // Create image
        NSImage*    image;
        static int  _imageWidth = 32 * 5;
        static int  _imageHeight = 32;
        image = [[NSImage alloc] initWithSize:NSMakeSize(_imageWidth, _imageHeight)];
        [image autorelease];
        
        // Draw icons
        NSEnumerator*   iconNameEnum;
        NSString*       iconName;
        int             index = 0;
        
        [image lockFocus];
        iconNameEnum = [_defaultIconNames objectEnumerator];
        while (iconName = [iconNameEnum nextObject]) {
            // Get icon image
            NSImage*    iconImage;
            iconImage = [icons objectForKey:iconName];
            
            // Draw icon
            [iconImage dissolveToPoint:NSMakePoint(index++ * 32, 0) fraction:1.0];
        }
        [image unlockFocus];
        
        // Create image menu item
        NSMenuItem* menuItem;
        menuItem = [[NSMenuItem alloc] initWithTitle:@"" 
                action:@selector(iconSelectedAction:) 
                keyEquivalent:@""];
        [menuItem autorelease];
        [menuItem setImage:image];
        [menuItem setRepresentedObject:key];
        
        [menu addItem:menuItem];
    }
    
    // Set menu popup button
    [popup setMenu:menu];
    
    // Select selected icons
    NSString*   iconName = nil;
    switch (texture) {
    case SRIconTextureAqua: {
        iconName = [defaults stringForKey:SRIconName];
        break;
    }
    case SRIconTextureMetal: {
        iconName = [defaults stringForKey:SRIconMetalName];
        break;
    }
    }
    if (iconName) {
        [popup selectItemAtIndex:[popup indexOfItemWithRepresentedObject:iconName]];
    }
}

- (void)_showAlertInstallDone
{
    NSAlert*    alert;
    alert = [[NSAlert alloc] init];
    [alert autorelease];
    [alert setAlertStyle:NSInformationalAlertStyle];
    [alert setMessageText:NSLocalizedString(@"Icon set install was done successfully.", nil)];
    
    [alert runModal];
}

- (void)_showAlertInstallFailed
{
    NSAlert*    alert;
    alert = [[NSAlert alloc] init];
    [alert autorelease];
    [alert setAlertStyle:NSInformationalAlertStyle];
    [alert setMessageText:NSLocalizedString(@"Icon set install failed.", nil)];
    
    [alert runModal];
}

- (void)loadIconPlist:(NSDictionary*)plist 
        baseURL:(NSURL*)baseURL 
        withModalWindow:(NSWindow*)window 
        modalDelegate:(id)delegate 
        didEndSelector:(SEL)selector
{
    NSFileManager*	fileMgr;
    fileMgr = [NSFileManager defaultManager];
    
    // Check 'shiiraVersion'
    NSString*   shiiraVersion;
    shiiraVersion = [plist objectForKey:@"shiiraVersion"];
    if (!shiiraVersion) {
        // Not Shiira icon plist
        SR_WARNING(@"Not Shiira icon plist");
        return;
    }
    
    // Get unique name
    NSString*   uniqueName;
    uniqueName = [plist objectForKey:@"uniqueName"];
    if (!uniqueName) {
        // No unique name
        SR_WARNING(@"No unique name");
        return;
    }
    
    // Ask permission
    NSAlert*    alert;
    int         result;
    alert = [[NSAlert alloc] init];
    [alert autorelease];
    [alert setAlertStyle:NSInformationalAlertStyle];
    [alert setMessageText:NSLocalizedString(@"Shiira icon set is found.", nil)];
    [alert setInformativeText:NSLocalizedString(@"Will you install Shiira icon set?", nil)];
    [alert addButtonWithTitle:NSLocalizedString(@"Yes", nil)];
    [alert addButtonWithTitle:NSLocalizedString(@"No", nil)];
    result = [alert runModal];
    if (result != NSAlertFirstButtonReturn) {
        return;
    }
    
    //
    // Create icons directory
    //
    
    // Get the paths of ~/Library/Shiira/Icons
    NSArray*	libraryPaths;
    NSString*   iconsDirectory;
    NSString*	iconsPlistPath;
    libraryPaths = NSSearchPathForDirectoriesInDomains(
            NSLibraryDirectory, NSUserDomainMask, YES);
    iconsDirectory = [[libraryPaths objectAtIndex:0] 
            stringByAppendingPathComponent:SRIconsDirectory];
    
    // Create file path for plist
    iconsDirectory = [iconsDirectory stringByAppendingPathComponent:uniqueName];
    iconsPlistPath = [iconsDirectory stringByAppendingPathComponent:@"ShiiraIcons.plist"];
    
    // Check existense
    if (![fileMgr fileExistsAtPath:iconsPlistPath]) {
        if (!SRCreateFile(iconsPlistPath)) {
            // Could not create file
            SR_WARNING(@"Could not create file");
            
            // Show alert for failure
            [self _showAlertInstallFailed];
            objc_msgSend(delegate, selector, SRIconInstalNG);
            
            return;
        }
    }
    
    // Write plist file
    if (![plist writeToFile:iconsPlistPath atomically:YES]) {
        // Could not write file
        SR_WARNING(@"Could not write file");
        
        // Show alert for failure
        [self _showAlertInstallFailed];
        objc_msgSend(delegate, selector, SRIconInstalNG);
        
        return;
    }
    
    //
    // For file URL
    //
    if ([baseURL isFileURL]) {
        // Get files
        NSString*       path;
        NSDictionary*   iconFiles;
        NSEnumerator*   enumerator;
        NSString*       fileName;
        path = [baseURL path];
        iconFiles = [plist objectForKey:@"icons"];
        enumerator = [[iconFiles allValues] objectEnumerator];
        
        while (fileName = [enumerator nextObject]) {
            // Copy file
            NSString*   source;
            NSString*   dest;
            source = [path stringByAppendingPathComponent:fileName];
            dest = [iconsDirectory stringByAppendingPathComponent:fileName];
            [fileMgr copyPath:source toPath:dest handler:nil];
        }
        
        // Load icons into memory
        [self _loadIconsAtUserLibraryName:iconsDirectory];
        
        // Show alert install done
        [self _showAlertInstallDone];
        objc_msgSend(delegate, selector, SRIconInstalOK);
        
        return;
    }
    
    //
    // Start download
    //
    
    // Initialize variables
    _baseURL = [baseURL retain];
    _iconsDirectory = [iconsDirectory retain];
    _iconFiles = [[plist objectForKey:@"icons"] retain];
    _downloadIndex = 0;
    
    // Start download
    [self _downloadNextFile];
}

- (void)_downloadNextFile
{
    if (_downloadIndex >= [_iconFiles count]) {
        // Done
        return;
    }
    
    // Create URL request for downloading file
    NSString*   fileName;
    NSURL*      URL;
    fileName = [[_iconFiles allValues] objectAtIndex:_downloadIndex];
    URL = [NSURL URLWithString:fileName relativeToURL:_baseURL];
    
    // Dwonload it
    NSURLRequest*   request;
    request = [NSURLRequest requestWithURL:URL];
    _download = [[NSURLDownload alloc] initWithRequest:request delegate:self];
}

// NSURLDownload delegate

- (void)downloadDidBegin:(NSURLDownload*)download
{
}

- (void)download:(NSURLDownload*)download 
        didCreateDestination:(NSString*)path
{
    [download setDestination:[_iconsDirectory stringByAppendingPathComponent:[path lastPathComponent]] 
            allowOverwrite:YES];
}

- (void)downloadDidFinish:(NSURLDownload*)download
{
    // Download next file
    _downloadIndex++;
    [self _downloadNextFile];
}

- (void)download:(NSURLDownload*)download 
        didFailWithError:(NSError*)error
{
    NSLog([error localizedDescription]);
}

- (BOOL)deleteIconOfName:(NSString*)name
{
    NSFileManager*	fileMgr;
    fileMgr = [NSFileManager defaultManager];
    
    // Get icon directory path
    NSString*   iconsDirectory = nil;
    
    // Find in bundle
    iconsDirectory = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:
            [NSString stringWithFormat:@"Icons/%@", name]];
    
    // Check existense
    if (![fileMgr fileExistsAtPath:iconsDirectory]) {
        // Find in ~/Library/Shiira/Icons
        NSArray*	libraryPaths;
        libraryPaths = NSSearchPathForDirectoriesInDomains(
                NSLibraryDirectory, NSUserDomainMask, YES);
        iconsDirectory = [[libraryPaths objectAtIndex:0] stringByAppendingPathComponent:
                [NSString stringWithFormat:@"%@/%@", SRIconsDirectory, name]];
    }
    
    // Check existense
    if (![fileMgr fileExistsAtPath:iconsDirectory]) {
        return NO;
    }
    
    // Delete directory
    if (![fileMgr removeFileAtPath:iconsDirectory handler:nil]) {
        return NO;
    }
    
    // Delete from icons
    if ([_icons objectForKey:name]) {
        [_icons removeObjectForKey:name];
    }
    
    return YES;
}

- (IBAction)cancelInstall:(id)sender
{
}

@end
