/*
SRDownloadViewController.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 "SRBookmark.h"

#import "SRDownloadViewController.h"
#import "SRDownloadCenter.h"
#import "SRDownloadHistoryItem.h"

#import "SRDownloadStatusCell.h"

#import "SRUtil.h"

@interface SRDownloadViewController (private)
- (void)_updateDetailView;
- (void)_updateSegmentedControl;

- (void)_createProgressIndicatorForItem:(SRDownloadHistoryItem*)historyItem;
- (void)_removeProgressIndicatorForItem:(SRDownloadHistoryItem*)historyItem;
@end

#pragma mark -

@implementation SRDownloadViewController

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

- (id)init
{
    self = [super init];
    if (!self) {
        return nil;
    }
    
    // Initialize instance variables
    _progressIndicatorDict = [[NSMutableDictionary dictionary] retain];
    
    // Load nib file
    if (![NSBundle loadNibNamed:@"DownloadView" owner:self]) {
        // Fatal error
        SR_FATAL(@"Could not load DownloadView.nib");
    }
    
    return self;
}

static int  _tablePaddingTop = -1;
static int  _tablePaddingBottom = -1;

- (void)awakeFromNib
{
    // Configure download table
    SRDownloadStatusCell*   statusCell;
    NSTableColumn*          column;
    statusCell = [[SRDownloadStatusCell alloc] init];
    [statusCell autorelease];
    column = [_downloadTable tableColumnWithIdentifier:@"status"];
    [column setDataCell:statusCell];
    
    [_downloadTable setTarget:self];
    [_downloadTable setAction:@selector(downloadTableSelectedAction:)];
    
    // Create acitve download segmented control
    NSSegmentedControl* segmentedControl;
    segmentedControl = [[NSSegmentedControl alloc] init];
    [segmentedControl autorelease];
    [segmentedControl setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]];
    [segmentedControl setTarget:self];
    [segmentedControl setAction:@selector(controlDownloadAction:)];
    [[segmentedControl cell] setTrackingMode:NSSegmentSwitchTrackingMomentary];
//    [[segmentedControl cell] setControlSize:NSSmallControlSize]; // Metal dose not support small sizes
    [segmentedControl setSegmentCount:SRActiveDownloadCount];
    [segmentedControl setImage:[NSImage imageNamed:@"find_line"] forSegment:SRActiveDownloadFind];
    [segmentedControl setImage:[NSImage imageNamed:@"pause_line"] forSegment:SRActiveDownloadPause];
    [segmentedControl setImage:[NSImage imageNamed:@"resume_line"] forSegment:SRActiveDownloadResume];
    [segmentedControl setImage:[NSImage imageNamed:@"close_line"] forSegment:SRActiveDownloadRemove];
    [segmentedControl sizeToFit];
    
    NSRect  viewFrame, segmentedFrame;
    viewFrame = [_segmentedControlView frame];
    segmentedFrame = [segmentedControl frame];
    segmentedFrame.size.height = viewFrame.size.height;
    [segmentedControl setFrame:segmentedFrame];
    [segmentedControl setAutoresizingMask:NSViewMinXMargin];
    [_segmentedControlView addSubview:segmentedControl];
    
    // Update detail view and segmented control
    [self _updateDetailView];
    [self _updateSegmentedControl];
    
    // Create progress indicator for download items
    NSEnumerator*           enumerator;
    SRDownloadHistoryItem*  historyItem;
    enumerator = [[[SRDownloadCenter sharedInstance] downloadItems] objectEnumerator];
    while (historyItem = [enumerator nextObject]) {
        if (![_progressIndicatorDict objectForKey:historyItem]) {
            [self _createProgressIndicatorForItem:historyItem];
        }
    }
    
    // Set paddings
    [self resetPaddings];
    
    // Set frame change notifications
    [_downloadView setPostsFrameChangedNotifications:YES];
    
    // Register notification
    NSNotificationCenter*   center;
    center = [NSNotificationCenter defaultCenter];
    [center addObserver:self 
            selector:@selector(downloadHistoryItemsUpdated:) 
            name:SRDownloadAddedNotification 
            object:nil];
    [center addObserver:self 
            selector:@selector(downloadHistoryItemsUpdated:) 
            name:SRDownloadRemovedNotification 
            object:nil];
    [center addObserver:self 
            selector:@selector(downloadHistoryItemsUpdated:) 
            name:SRDownloadStatusChangedNotification 
            object:nil];
    [center addObserver:self 
            selector:@selector(downloadViewFrameDidChange:) 
            name:NSViewFrameDidChangeNotification 
            object:_downloadView];
}

- (void)dealloc
{
    // Release outlets
    [_downloadView release];
    _downloadView = nil;
    
    // Release instance variables
    [_progressIndicatorDict release];
    
    // Remove notification
    NSNotificationCenter*   center;
    center = [NSNotificationCenter defaultCenter];
    [center removeObserver:self];
    
    [super dealloc];
}

//--------------------------------------------------------------//
#pragma mark -- First responder --
//--------------------------------------------------------------//

- (BOOL)acceptsFirstResponder
{
    return YES;
}

//--------------------------------------------------------------//
#pragma mark -- View --
//--------------------------------------------------------------//

- (NSView*)downloadView
{
    return _downloadView;
}

- (NSTableView*)downloadTable
{
    return _downloadTable;
}

- (void)resetPaddings
{
    NSRect  tableFrame, downloadFrame;
    tableFrame = [[_downloadTable scrollView] frame];
    downloadFrame = [_downloadView frame];
    downloadFrame.origin = NSZeroPoint;
    _tablePaddingTop = (downloadFrame.origin.y + downloadFrame.size.height) - 
            (tableFrame.origin.y + tableFrame.size.height);
    _tablePaddingBottom = tableFrame.origin.y - downloadFrame.origin.y;
}

- (NSSegmentedControl*)downloadSegmentedControl
{
    return [[_segmentedControlView subviews] objectAtIndex:0];
}

- (void)_updateDetailView
{
    // Get selected download items
    NSArray*    items;
    items = [self selectedDownloadItems];
    if (!items || [items count] == 0) {
        // Reset all
        NSString*   noSelectString;
        noSelectString = NSLocalizedString(@"No selection", nil);
        
        [_imageView setImage:nil];
        [_nameTextField setStringValue:@""];
        [[_nameTextField cell] setPlaceholderString:noSelectString];
        [_URLTextField setStringValue:@""];
        [_statusTextField setStringValue:@""];
        
        return;
    }
    
    // For mulitple selection
    if ([items count] > 1) {
        NSString*   multiString;
        multiString = NSLocalizedString(@"Multiple selection", nil);
        
        [_imageView setImage:nil];
        [_nameTextField setStringValue:@""];
        [[_nameTextField cell] setPlaceholderString:multiString];
        [_URLTextField setStringValue:@""];
        [_statusTextField setStringValue:@""];
        
        return;
    }
    
    // Get single download item
    SRDownloadHistoryItem*  item;
    item = [items objectAtIndex:0];
    
    // Set download item property
    NSString*   name;
    NSString*   URLString;
    NSString*   status;
    
    [_imageView setValue:[item icon] forKey:@"image"];
    
    name = [item downloadedFileName];
    if (!name) {
        name = @"";
    }
    [_nameTextField setStringValue:name];
    [[_nameTextField cell] setPlaceholderString:@""];
    
    URLString = [item URLString];
    if (!URLString) {
        URLString = @"";
    }
    [_URLTextField setStringValue:URLString];
    
    long long   expectedLength;
    long long   downloadedLength;
    double      percentage = 0.0;
    expectedLength = [item expectedLength];
    downloadedLength = [item downloadedLength];
    if (expectedLength == -1 || expectedLength == 0) {
        status = @"(working...)";
    }
    else {
        percentage = (double)downloadedLength / expectedLength * 100.0;
        status = [NSString stringWithFormat:@"%@ / %@ (%d %%)", 
                SRCreateDataSizeString(downloadedLength), 
                SRCreateDataSizeString(expectedLength), 
                (int)percentage];
    }
    [_statusTextField setStringValue:status];
}

- (void)_updateSegmentedControl
{
    NSSegmentedControl* segmentedControl;
    segmentedControl = [self downloadSegmentedControl];
    
    // Get selected acitve download item
    NSArray*    items;
    items = [self selectedDownloadItems];
    if (!items) {
        // Disable all
        [segmentedControl setEnabled:NO forSegment:SRActiveDownloadFind];
        [segmentedControl setEnabled:NO forSegment:SRActiveDownloadPause];
        [segmentedControl setEnabled:NO forSegment:SRActiveDownloadResume];
        [segmentedControl setEnabled:NO forSegment:SRActiveDownloadRemove];
        
        return;
    }
    
    // Check download items status
    NSEnumerator*           enumerator;
    SRDownloadHistoryItem*  item;
    BOOL                    canFind = NO;
    BOOL                    canPause = NO;
    BOOL                    canResume = NO;
    enumerator = [items objectEnumerator];
    while (item = [enumerator nextObject]) {
        // Get status of download
        SRDownloadItemStatus    status;
        status = [item status];
        switch (status) {
        case SRDownloadStatusActive: {
            canFind = YES;
            canPause = YES;
            break;
        }
        case SRDownloadStatusPaused: {
            canFind = YES;
            canResume = YES;
            break;
        }
        case SRDownloadStatusCompleted:
        case SRDownloadStatusUnknown: {
            canFind = YES;
            break;
        }
        }
    }
    
    // Update segments state
    [segmentedControl setEnabled:canFind forSegment:SRActiveDownloadFind];
    [segmentedControl setEnabled:canPause forSegment:SRActiveDownloadPause];
    [segmentedControl setEnabled:canResume forSegment:SRActiveDownloadResume];
    // Always 'Remove' is enable
    [segmentedControl setEnabled:YES forSegment:SRActiveDownloadRemove];
}

//--------------------------------------------------------------//
#pragma mark -- Download item management --
//--------------------------------------------------------------//

- (NSArray*)selectedDownloadItems
{
    // Get selected rows
    NSIndexSet* indexSet;
    indexSet = [_downloadTable selectedRowIndexes];
    if (!indexSet || [indexSet count] == 0) {
        return nil;
    }
    
    // Get first selected download items
    NSArray*        downloadItems;
    unsigned int    index;
    NSMutableArray* items;
    items = [NSMutableArray array];
    downloadItems = [[SRDownloadCenter sharedInstance] downloadItems];
    index = [indexSet firstIndex];
    do {
        [items addObject:[downloadItems objectAtIndex:index]];
        index = [indexSet indexGreaterThanIndex:index];
    } while (index != NSNotFound);
    
    return items;
}

- (void)deleteSelectedItems
{
    // Get selected active download items
    NSArray*    items;
    items = [self selectedDownloadItems];
    if (!items) {
        return;
    }
    
    // Get first index of selected items
    int row = -1;
    row = [[_downloadTable selectedRowIndexes] firstIndex];
    
    // Delete items
    SRDownloadCenter*       downloadCenter;
    NSEnumerator*           enumerator;
    SRDownloadHistoryItem*  item;
    downloadCenter = [SRDownloadCenter sharedInstance];
    enumerator = [items objectEnumerator];
    while (item = [enumerator nextObject]) {
        // Remove download
        [downloadCenter removeDownloadForItem:item];
        
        // Remove progress indicator from superview
        NSProgressIndicator*    progress;
        progress = [_progressIndicatorDict objectForKey:item];
        if (progress && [progress superview]) {
            [progress removeFromSuperview];
        }
        [_progressIndicatorDict removeObjectForKey:item];
    }
    
    // Select previous one
    if (row > 0) {
        [_downloadTable selectRowIndexes:[NSIndexSet indexSetWithIndex:row - 1] byExtendingSelection:NO];
    }
    else {
        // Deselect all
        [_downloadTable deselectAll:self];
    }
    
    // Update detail view and segmented control
    [_downloadTable reloadData];
    [self _updateDetailView];
    [self _updateSegmentedControl];
}

- (void)_deleteProgressIndicatorForItem:(SRDownloadHistoryItem*)item
{
    NSProgressIndicator*    progress;
    progress = [_progressIndicatorDict objectForKey:item];
    if (progress && [progress superview]) {
        [progress removeFromSuperview];
    }
    [_progressIndicatorDict removeObjectForKey:item];
}

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

- (void)downloadTableSelectedAction:(id)sender
{
    // Update detail view and segmented control
    [self _updateDetailView];
    [self _updateSegmentedControl];
}

- (void)controlDownloadAction:(id)sender
{
    // Get selected download items
    NSArray*    items;
    items = [self selectedDownloadItems];
    if (!items || [items count] == 0) {
        return;
    }
    
    // Determines selected segment
    NSSegmentedControl* segmentedControl;
    int                 selectedSegment;
    segmentedControl = [self downloadSegmentedControl];
    selectedSegment = [segmentedControl selectedSegment];
    
    // Get SRDownloadCenter instance
    SRDownloadCenter*   downloadCenter;
    downloadCenter = [SRDownloadCenter sharedInstance];
    
    // Check download items
    NSEnumerator*           enumerator;
    SRDownloadHistoryItem*  item;
    enumerator = [items objectEnumerator];
    while (item = [enumerator nextObject]) {
        // Get download status
        SRDownloadItemStatus    status;
        status = [item status];
        
        switch (selectedSegment) {
        case SRActiveDownloadPause: {
            // Pause download
            if (status == SRDownloadStatusActive) {
                [downloadCenter pauseDownloadForItem:item];
            }
            break;
        }
        case SRActiveDownloadResume: {
            // Resume download
            if (status == SRDownloadStatusPaused) {
                [downloadCenter resumeDownloadForItem:item];
            }
            break;
        }
        case SRActiveDownloadFind: {
            // Find download item
            [downloadCenter findItem:item];
            break;
        }
        case SRActiveDownloadRemove: {
            // Remove download
            [downloadCenter removeDownloadForItem:item];
            
            // Delete progress indicator from superview
            [self _deleteProgressIndicatorForItem:item];
            
            break;
        }
        }
    }
    
    // Deselect all
    if (selectedSegment == SRActiveDownloadRemove) {
        [_downloadTable deselectAll:self];
    }
    
    // Update detail view and segmented control
    [_downloadTable reloadData];
    [self _updateDetailView];
    [self _updateSegmentedControl];
}

- (IBAction)copy:(id)sender
{
    // Get selected items
    NSArray*    selectedItems;
    selectedItems = [self selectedDownloadItems];
    if (!selectedItems || [selectedItems count] == 0) {
        return;
    }
    
    // Create bookmarks from selected items
    NSMutableArray*         bookmarks;
    NSEnumerator*           enumerator;
    SRDownloadHistoryItem*  item;
    bookmarks = [NSMutableArray array];
    enumerator = [selectedItems objectEnumerator];
    while (item = [enumerator nextObject]) {
        SRBookmark* bookmark;
        bookmark = SRCreateBookmarkFromDownloadHistoryItem(item);
        if (bookmark) {
            [bookmarks addObject:bookmark];
        }
    }
    
    // Write bookmarks to pasteboard
    NSPasteboard*   pboard;
    pboard = [NSPasteboard generalPasteboard];
    SRWriteBookmarksToPasteboard(bookmarks, pboard);
}

- (IBAction)delete:(id)sender
{
    // Delete selected items
    [self deleteSelectedItems];
}

- (BOOL)validateMenuItem:(NSMenuItem*)menuItem
{
    SEL         action = [menuItem action];
    NSArray*    selectedItems = [self selectedDownloadItems];
    
    // Copy and delete
    if (action == @selector(copy:) || action == @selector(delete:)) {
        return (selectedItems && [selectedItems count] > 0);
    }
    
    return NO;
}

//--------------------------------------------------------------//
#pragma mark -- SRDownloadHistory notification --
//--------------------------------------------------------------//

- (void)_createProgressIndicatorForItem:(SRDownloadHistoryItem*)historyItem
{
    if ([_progressIndicatorDict objectForKey:historyItem]) {
        return;
    }
    
    // Create progress indicator
    NSProgressIndicator*    progress;
    progress = [[NSProgressIndicator alloc] initWithFrame:NSZeroRect];
    [progress setMinValue:0.0];
    [progress setMaxValue:1.0];
    [progress setControlSize:NSSmallControlSize];
    [_downloadTable addSubview:progress];
    
    [_progressIndicatorDict setObject:progress forKey:historyItem];
}

- (void)_removeProgressIndicatorForItem:(SRDownloadHistoryItem*)historyItem
{
    // Remove progress indicator
    NSProgressIndicator*    progress;
    progress = [_progressIndicatorDict objectForKey:historyItem];
    if (progress && [progress superview]) {
        [progress removeFromSuperview];
    }
    [_progressIndicatorDict removeObjectForKey:historyItem];
}

- (void)downloadHistoryItemsUpdated:(NSNotification*)notification
{
    NSString*   name;
    name = [notification name];
    
    // For download history active items added
    if ([name isEqualToString:SRDownloadAddedNotification]) {
        NSEnumerator*   enumerator;
        SRDownloadHistoryItem*  historyItem;
        enumerator = [[notification object] objectEnumerator];
        while (historyItem = [enumerator nextObject]) {
            // Create progress indicator
            [self _createProgressIndicatorForItem:historyItem];
        }
    }
    // For download history active items removed
    if ([name isEqualToString:SRDownloadRemovedNotification]) {
        NSEnumerator*   enumerator;
        SRDownloadHistoryItem*  historyItem;
        enumerator = [[notification object] objectEnumerator];
        while (historyItem = [enumerator nextObject]) {
            // Remove progress indicator
            [self _removeProgressIndicatorForItem:historyItem];
        }
    }
    
    // Update detail view and segmented control
    [_downloadTable reloadData];
    [self _updateDetailView];
    [self _updateSegmentedControl];
    
    // Scroll to newly added item
    if ([name isEqualToString:SRDownloadAddedNotification]) {
        [_downloadTable scrollRowToVisible:([_downloadTable numberOfRows] - 1)];
    }
}

//--------------------------------------------------------------//
#pragma mark -- NSTableDataSource --
//--------------------------------------------------------------//

- (int)numberOfRowsInTableView:(NSTableView*)tableView
{
    // For download table view
    if (tableView == _downloadTable) {
        NSArray*    downloadItems;
        downloadItems = [[SRDownloadCenter sharedInstance] downloadItems];
        return [downloadItems count];
    }
    
    return 0;
}

- (id)tableView:(NSTableView*)tableView 
        objectValueForTableColumn:(NSTableColumn*)tableColumn 
        row:(int)rowIndex
{
    // For download table view
    if (tableView == _downloadTable) {
        // Get download history item
        NSArray*                downloadItems;
        SRDownloadHistoryItem*  historyItem;
        downloadItems = [[SRDownloadCenter sharedInstance] downloadItems];
        historyItem = [downloadItems objectAtIndex:rowIndex];
        return historyItem;
    }
    
    return nil;
}

//--------------------------------------------------------------//
#pragma mark -- NSTableView delegate --
//--------------------------------------------------------------//

- (void)tableView:(NSTableView*)tableView 
        willDisplayCell:(id)cell 
        forTableColumn:(NSTableColumn*)tableColumn 
        row:(int)rowIndex
{
    if (tableView == _downloadTable) {
        // Get download history item
        NSArray*                downloadItems;
        SRDownloadHistoryItem*  historyItem;
        downloadItems = [[SRDownloadCenter sharedInstance] downloadItems];
        historyItem = [downloadItems objectAtIndex:rowIndex];
        
        // Get progress indicator
        NSProgressIndicator*    progress;
        progress = [_progressIndicatorDict objectForKey:historyItem];
        
        if ([(SRDownloadStatusCell*)cell progressIndicator] != progress) {
            // Set progress indicator
            [cell setProgressIndicator:progress];
        }
    }
}

- (void)tableViewSelectionDidChange:(NSNotification*)notification
{
    if ([notification object] == _downloadTable) {
        // Update detail view and segmented control
        [self _updateDetailView];
        [self _updateSegmentedControl];
    }
}

// Extention
- (void)tableViewDeleteSelectedItem:(NSTableView*)tableView
{
    if (tableView == _downloadTable) {
        // Delete selected items
        [self deleteSelectedItems];
    }
}

//--------------------------------------------------------------//
#pragma mark -- NSView notification --
//--------------------------------------------------------------//

- (void)downloadViewFrameDidChange:(NSNotification*)notification
{
    // Get table frame and check padding
    NSRect  tableFrame, downloadFrame;
    int     tablePaddingTop, tablePaddingBottom;
    tableFrame = [[_downloadTable scrollView] frame];
    downloadFrame = [_downloadView frame];
    downloadFrame.origin = NSZeroPoint;
    tablePaddingTop = (downloadFrame.origin.y + downloadFrame.size.height) - 
            (tableFrame.origin.y + tableFrame.size.height);
    tablePaddingBottom = tableFrame.origin.y - downloadFrame.origin.y;
    
    if (tablePaddingTop != _tablePaddingTop || 
        tablePaddingBottom != _tablePaddingBottom)
    {
        // Reset padding
        NSRect  newFrame;
        newFrame.origin.x = tableFrame.origin.x;
        newFrame.origin.y = downloadFrame.origin.y + _tablePaddingBottom;
        newFrame.size.width = tableFrame.size.width;
        newFrame.size.height = downloadFrame.size.height - _tablePaddingBottom - _tablePaddingTop;
        
        if (newFrame.origin.y > 0 && newFrame.size.height > 0) {
            [[_downloadTable scrollView] setFrame:newFrame];
        }
    }
}

@end
