/*
SRCacheController.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 "SRUtil.h"

#import "SRCacheController.h"

#import "WebKitEx.h"

@implementation SRCacheManager

//--------------------------------------------------------------//
#pragma mark -- Cache management --
//--------------------------------------------------------------//

- (void)_setMemoryCapacity:(unsigned int)capacity
{
    NSURLCache* cache;
    cache = [NSURLCache sharedURLCache];
    
    if ([cache memoryCapacity] != capacity) {
        [cache setMemoryCapacity:capacity];
    }
}

- (void)_setDiskCapacity:(unsigned int)capacity
{
    NSURLCache* cache;
    cache = [NSURLCache sharedURLCache];
    
    if ([cache diskCapacity] != capacity) {
        [cache setDiskCapacity:capacity];
    }
}

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

+ (void)startCacheManager
{
    static SRCacheManager*  _sharedInstance = nil;
    if (!_sharedInstance) {
        _sharedInstance = [[SRCacheManager alloc] init];
    }
}

- (id)init
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    self = [super init];
    if (!self) {
        return nil;
    }
    
    // Set memory and disk capacity
    unsigned    memoryCapacity;
    unsigned    diskCapacity;
    memoryCapacity = [[defaults objectForKey:SRCacheMemoryCapacity] unsignedIntValue];
    if (memoryCapacity != 0) {
        [self _setMemoryCapacity:memoryCapacity];
    }
    diskCapacity = [[defaults objectForKey:SRCacheDiskCapacity] unsignedIntValue];
    if (diskCapacity != 0) {
        [self _setMemoryCapacity:diskCapacity];
    }
    
    // Register key value observation
    [defaults addObserver:self 
            forKeyPath:SRCacheMemoryCapacity 
            options:NSKeyValueObservingOptionNew 
            context:NULL];
    [defaults addObserver:self 
            forKeyPath:SRCacheDiskCapacity 
            options:NSKeyValueObservingOptionNew 
            context:NULL];
    
    return self;
}

- (void)dealloc
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    [defaults removeObserver:self forKeyPath:SRCacheMemoryCapacity];
    [defaults removeObserver:self forKeyPath:SRCacheDiskCapacity];
    
    [super dealloc];
}

//--------------------------------------------------------------//
#pragma mark -- Key value observation --
//--------------------------------------------------------------//

- (void)observeValueForKeyPath:(NSString*)keyPath 
        ofObject:(id)object 
        change:(NSDictionary*)change 
        context:(void *)context
{
    // For NSUserDefault
    if (object == [NSUserDefaults standardUserDefaults]) {
        // Cache memory capacity
        if ([keyPath isEqualToString:SRCacheMemoryCapacity]) {
            // Set memory cache
            [self _setMemoryCapacity:
                [[change objectForKey:@"new"] unsignedIntValue]];
            return;
        }
        // Cache disk capacity
        if ([keyPath isEqualToString:SRCacheDiskCapacity]) {
            // Set disk cache
            [self _setDiskCapacity:
                [[change objectForKey:@"new"] unsignedIntValue]];
            return;
        }
    }
}

@end

#pragma mark -

@implementation SRCacheController

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

+ (void)initialize
{
    // Register dependent key
    [[self class] setKeys:[NSArray arrayWithObject:@"memoryCapacity"] 
            triggerChangeNotificationsForDependentKey:@"memoryCapacityString"];
    [[self class] setKeys:[NSArray arrayWithObject:@"diskCapacity"] 
            triggerChangeNotificationsForDependentKey:@"diskCapacityString"];
}
    
static const int  _SRCachePanelStandardHeight = 168;
static const int  _SRCachePanelExtendeddHeight = 238;

- (void)_updateAdvancedSettings
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Get panel frame
    NSWindow*   window;
    NSRect      frame;
    window = [self window];
    frame = [window frame];
    
    // Get SRCacheShowAdvancedSettings
    BOOL    showAdvancedSettings;
    showAdvancedSettings = [defaults boolForKey:SRCacheShowAdvancedSettings];
    
    // Change frame size
    if ((showAdvancedSettings && frame.size.height != _SRCachePanelExtendeddHeight) || 
        (!showAdvancedSettings && frame.size.height != _SRCachePanelStandardHeight))
    {
        int height;
        height = frame.size.height;
        
        if (showAdvancedSettings) {
            frame.size.height = _SRCachePanelExtendeddHeight;
        }
        else {
            frame.size.height = _SRCachePanelStandardHeight;
        }
        frame.origin.y -= frame.size.height - height;
        
        [window setFrame:frame display:YES animate:YES];
//        [window setContentSize:frame.size];
//        [window setFrameTopLeftPoint:topLeftPoint];
        
        // Update min and max size
        NSSize  minSize, maxSize;
        minSize = [window minSize];
        maxSize = [window maxSize];
        minSize.height = frame.size.height;
        maxSize.height = frame.size.height;
        [window setMinSize:minSize];
        [window setMaxSize:maxSize];
    }
}

- (id)initWithWindowNibName:(NSString*)windowNibName
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    self = [super initWithWindowNibName:windowNibName];
    if (!self) {
        return self;
    }
    
    // Configure progress indicator
    [_memoryProgress stopAnimation:self];
    [_diskProgress stopAnimation:self];
    
    // Update frame size
    [self _updateAdvancedSettings];
    
    // Set memory and disk capacity
    NSURLCache* cache;
    unsigned    memoryCapacity;
    unsigned    diskCapacity;
    cache = [NSURLCache sharedURLCache];
    memoryCapacity = [[defaults objectForKey:SRCacheMemoryCapacity] unsignedIntValue];
    if (memoryCapacity == 0) {
        memoryCapacity = [cache memoryCapacity];
    }
    diskCapacity = [[defaults objectForKey:SRCacheDiskCapacity] unsignedIntValue];
    if (diskCapacity == 0) {
        diskCapacity = [cache diskCapacity];
    }
    
    [self setValue:[NSNumber numberWithUnsignedInt:memoryCapacity] forKey:@"memoryCapacity"];
    [self setValue:[NSNumber numberWithUnsignedInt:diskCapacity] forKey:@"diskCapacity"];
    
    // Set frame top left point
    NSRect  frame;
    frame = NSRectFromString([defaults stringForKey:@"SRCacheFrame"]);
    if (!NSIsEmptyRect(frame)) {
        [[self window] setFrameTopLeftPoint:
                NSMakePoint(frame.origin.x, frame.origin.y + frame.size.height)];
    }
    
    // Register key value observation
    [defaults addObserver:self 
            forKeyPath:SRCacheShowAdvancedSettings 
            options:NSKeyValueObservingOptionNew 
            context:NULL];
    
    return self;
}

- (void)dealloc
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    [defaults removeObserver:self forKeyPath:SRCacheShowAdvancedSettings];
    
    [super dealloc];
}

- (void)_updateCache
{
    NSURLCache* cache;
    cache = [NSURLCache sharedURLCache];
    
    // Check memory usage
    unsigned    currentMemoryUsage;
    unsigned    memoryCapacity;
    currentMemoryUsage = [cache currentMemoryUsage];
    memoryCapacity = [cache memoryCapacity];
    if (currentMemoryUsage > memoryCapacity) {
        currentMemoryUsage = memoryCapacity;
    }
    
    double  memoryUsage;
    memoryUsage = (double)currentMemoryUsage / memoryCapacity;
    [self setValue:[NSNumber numberWithDouble:memoryUsage] forKey:@"memoryUsage"];
    [self setValue:[NSString stringWithFormat:@"%d%%", (int)(memoryUsage * 100)] 
            forKey:@"memoryUsagePercent"];
    [self setValue:[NSString stringWithFormat:@"(%@ / %@)", 
                SRCreateDataSizeString(currentMemoryUsage), 
                SRCreateDataSizeString(memoryCapacity)] 
            forKey:@"memoryUsageString"];
    
    // Check disk usage
    unsigned    currentDiskUsage;
    unsigned    diskCapacity;
    currentDiskUsage = [cache currentDiskUsage];
    diskCapacity = [cache diskCapacity];
    if (currentDiskUsage > diskCapacity) {
        currentDiskUsage = diskCapacity;
    }
    
    double  diskUsage;
    diskUsage = (double)currentDiskUsage / diskCapacity;
    [self setValue:[NSNumber numberWithDouble:diskUsage] forKey:@"diskUsage"];
    [self setValue:[NSString stringWithFormat:@"%d%%", (int)(diskUsage * 100)] 
            forKey:@"diskUsagePercent"];
    [self setValue:[NSString stringWithFormat:@"(%@ / %@)", 
                SRCreateDataSizeString(currentDiskUsage), 
                SRCreateDataSizeString(diskCapacity)] 
            forKey:@"diskUsageString"];
}

- (void)_timerFired:(id)userInfo
{
    // Update cache
    [self _updateCache];
}

//--------------------------------------------------------------//
#pragma mark -- Accessors --
//--------------------------------------------------------------//

- (void)setMemoryCapacity:(unsigned)capacity
{
    // Set memory capacity
    _memoryCapacity = capacity;
    
    // Update cache
    [self _updateCache];
    
    // Store to user defaults
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    [defaults setObject:[NSNumber numberWithUnsignedInt:capacity] forKey:SRCacheMemoryCapacity];
}

- (NSString*)memoryCapacityString
{
    return SRCreateDataSizeString(_memoryCapacity);
}

- (void)setDiskCapacity:(unsigned)capacity
{
    // Set disk capacity
    _diskCapacity = capacity;
    
    // Update cache
    [self _updateCache];
    
    // Store to user defaults
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    [defaults setObject:[NSNumber numberWithUnsignedInt:capacity] forKey:SRCacheDiskCapacity];
}

- (NSString*)diskCapacityString
{
    return SRCreateDataSizeString(_diskCapacity);
}

//--------------------------------------------------------------//
#pragma mark -- NSWindow delegate --
//--------------------------------------------------------------//

- (void)windowDidBecomeKey:(NSNotification*)notification
{
    // Update cache
    [self _updateCache];
    
    // Start timer
    if (!_timer) {
        // Create timer
        _timer = [[NSTimer 
                scheduledTimerWithTimeInterval:5 
                target:self 
                selector:@selector(_timerFired:) 
                userInfo:nil 
                repeats:YES] retain];
        [_timer fire];
    }
}

- (void)windowDidResignKey:(NSNotification*)notification
{
}

- (void)windowWillClose:(NSNotification*)notification
{
    // Stop timer
    if (_timer) {
        [_timer invalidate];
        [_timer release];
        _timer = nil;
    }
}

- (void)windowDidMove:(NSNotification*)notification
{
    if (![[self window] isVisible]) {
        return;
    }
    
    [[NSUserDefaults standardUserDefaults] 
            setObject:NSStringFromRect([[self window] frame]) forKey:@"SRCacheFrame"];
}

- (void)windowDidResize:(NSNotification*)notification
{
    if (![[self window] isVisible]) {
        return;
    }
    
    [[NSUserDefaults standardUserDefaults] 
            setObject:NSStringFromRect([[self window] frame]) forKey:@"SRCacheFrame"];
}

//--------------------------------------------------------------//
#pragma mark -- Actions --
//--------------------------------------------------------------//

- (IBAction)removeAllCacheAction:(id)sender
{
    // Remove all cache by NSURLCache
    [[NSURLCache sharedURLCache] removeAllCachedResponses];
    
    // Remove all cache by WebCoreStatistics
    //[WebCoreStatistics emptyCache];
    //[WebCoreStatistics garbageCollectJavaScriptObjects];
    
    // Remove all cache by WebTextRendererFactory
    //[[WebTextRendererFactory sharedFactory] clearCaches];
    
    // Update cache
    [self _updateCache];
}

//--------------------------------------------------------------//
#pragma mark -- Key value observation --
//--------------------------------------------------------------//

- (void)observeValueForKeyPath:(NSString*)keyPath 
        ofObject:(id)object 
        change:(NSDictionary*)change 
        context:(void *)context
{
    // For NSUserDefault
    if (object == [NSUserDefaults standardUserDefaults]) {
        // Bookmark preferences
        if ([keyPath isEqualToString:SRCacheShowAdvancedSettings]) {
            // Update panel frame
            [self _updateAdvancedSettings];
            return;
        }
    }
}

@end
