//
//  PithosSubdirNode.m
//  pithos-macos
//
// Copyright 2011-2012 GRNET S.A. 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 GRNET S.A. ``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 GRNET S.A 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.
// 
// The views and conclusions contained in the software and
// documentation are those of the authors and should not be
// interpreted as representing official policies, either expressed
// or implied, of GRNET S.A.

#import "PithosSubdirNode.h"
#import "ASIPithosRequest.h"
#import "ASIPithos.h"
#import "ASIPithosObjectRequest.h"
#import "ASIPithosContainer.h"
#import "ASIPithosObject.h"
#import "ASINetworkQueue.h"
#import "ASIDownloadCache.h"
#import "PithosUtilities.h"
#import "PithosObjectNodeInfoController.h"

static NSImage *sharedIcon = nil;

@implementation PithosSubdirNode
@synthesize pithosObject, versions;
@synthesize isPublic;

+ (void)initialize {
	if (self == [PithosSubdirNode class])
        sharedIcon = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGenericFolderIcon)];
}

#pragma mark -
#pragma mark Object Lifecycle

- (id)initWithPithos:(ASIPithos *)aPithos 
     pithosContainer:(ASIPithosContainer *)aPithosContainer 
        pithosObject:(ASIPithosObject *)aPithosObject {
    if ((self = [super init])) {
        self.pithos = aPithos;
        self.pithosContainer = aPithosContainer;
        self.pithosObject = aPithosObject;
        refreshParent = NO;
    }
    return self;
}

- (void)dealloc {
    [refreshVersionsObjectRequest clearDelegatesAndCancel];
    [refreshMetadataObjectRequest clearDelegatesAndCancel];
    [applyMetadataObjectRequest clearDelegatesAndCancel];
}

#pragma mark -
#pragma mark Properties

- (void)setPithos:(ASIPithos *)aPithos {
    if (aPithos && ![aPithos isEqualTo:pithos]) {
        pithos = aPithos;
        url = nil;
    }
}

- (NSString *)url {
    if (url == nil)
        url = [[NSString alloc] initWithFormat:@"subdir %@/%@/%@%@", 
               (sharingAccount ? [pithos storageURLWithAuthUser:sharingAccount] : pithos.storageURL), 
               pithosContainer.name, 
               prefix, 
               (shared ? @"?shared" : @"")];
    return url;
}

- (NSString *)displayName {
    if (displayName == nil) {
        displayName = [pithosObject.name lastPathComponent];
        if (!pithosObject.subdir && [pithosObject.name hasSuffix:@"/"])
            displayName = [displayName stringByAppendingString:@"/"];
    } 
    return [displayName copy];
}

- (void)setDisplayName:(NSString *)aDisplayName {    
}

- (NSImage *)icon {
    if (icon == nil)
        icon = sharedIcon;
    return icon;
}

- (void)setPithosObject:(ASIPithosObject *)aPithosObject {
    if (![pithosObject isEqualTo:aPithosObject]) {
        pithosObject = aPithosObject;
    }
    if (pithosObject.subdir) {
        self.prefix = [pithosObject.name substringToIndex:([pithosObject.name length] - 1)];
    } else {
        self.prefix = [NSString stringWithString:pithosObject.name];
    }
    self.isPublic = (pithosObject.publicURI != nil);
    // Refresh browser if the object is in my shared and is no longer shared
    if (shared && !pithosObject.subdir && !pithosObject.sharing) {
        [[NSNotificationCenter defaultCenter] postNotificationName:@"PithosBrowserRefreshNeeeded" object:self];
    }
}

- (void)setLimitedPithosObject:(ASIPithosObject *)aPithosObject {
    if (![pithosObject isEqualTo:aPithosObject]) {
        self.pithosObject.subdir = aPithosObject.subdir;
        self.pithosObject.name = aPithosObject.name;
        self.pithosObject.hash = aPithosObject.hash;
        self.pithosObject.objectHash = aPithosObject.objectHash;
        self.pithosObject.UUID = aPithosObject.UUID;
        self.pithosObject.bytes = aPithosObject.bytes;
        self.pithosObject.contentType = aPithosObject.contentType;
        self.pithosObject.lastModified = aPithosObject.lastModified;
        self.pithosObject.version = aPithosObject.version;
        self.pithosObject.versionTimestamp = aPithosObject.versionTimestamp;
        self.pithosObject.modifiedBy = aPithosObject.modifiedBy;
        self.pithosObject.sharedBy = aPithosObject.sharedBy;
        self.pithosObject.allowedTo = aPithosObject.allowedTo;
        if (!pithosNodeInfoController) {
            self.pithosObject.sharing = aPithosObject.sharing;
            self.pithosObject.publicURI = aPithosObject.publicURI;
            self.pithosObject = pithosObject;
        }
    }
}

#pragma mark -
#pragma mark ASIHTTPRequestDelegate

- (void)objectRequestFinished:(ASIPithosObjectRequest *)request {
    @autoreleasepool {
        DLog(@"URL: %@", [request url]);
        DLog(@"cached: %d", [request didUseCachedResponse]);
        
        if ([request isEqualTo:applyMetadataObjectRequest]) {
            int responseStatusCode = applyMetadataObjectRequest.responseStatusCode;
            if ((responseStatusCode != 201) && (responseStatusCode != 202))
                [PithosUtilities unexpectedResponseStatusAlertWithRequest:applyMetadataObjectRequest];
            @synchronized(self) {
                applyMetadataObjectRequest = nil;
            }
            if ((responseStatusCode == 201) || (responseStatusCode == 202))
                [self refreshInfo];
        } else if ([request isEqualTo:refreshMetadataObjectRequest]) {
            [[pithosNodeInfoController window] makeFirstResponder:nil];
            self.pithosObject = [refreshMetadataObjectRequest object];
            if (refreshParent) {
                // Ask the parent for refresh for the case where an object was removed
                // It is done here so that it doesn't affect the info window refresh
                [parent refresh];
                refreshParent = NO;
            }
            @synchronized(self) {
                refreshMetadataObjectRequest = nil;
            }
        } else if ([request isEqualTo:refreshVersionsObjectRequest]) {
            [[pithosNodeInfoController window] makeFirstResponder:nil];
            self.versions = [refreshVersionsObjectRequest versions];
            @synchronized(self) {
                refreshVersionsObjectRequest = nil;
            }
        }
    }
}

- (void)objectRequestFailed:(ASIPithosObjectRequest *)request {
    @autoreleasepool {
        NSUInteger retries = [[request.userInfo objectForKey:@"retries"] unsignedIntegerValue];
        if (retries > 0) {
            ASIPithosObjectRequest *newRequest = (ASIPithosObjectRequest *)[PithosUtilities copyRequest:request];
            [(NSMutableDictionary *)(newRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
            if ([request isEqualTo:applyMetadataObjectRequest]) {
                @synchronized(self) {
                    applyMetadataObjectRequest = newRequest;
                }
            } else if ([request isEqualTo:refreshMetadataObjectRequest]) {
                @synchronized(self) {
                    refreshMetadataObjectRequest = newRequest;
                }
            } else if ([request isEqualTo:refreshVersionsObjectRequest]) {
                @synchronized(self) {
                    refreshVersionsObjectRequest = newRequest;
                }
            }
            [[PithosUtilities prepareRequest:newRequest priority:[[newRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous];
        } else {
            if ([request isEqualTo:applyMetadataObjectRequest]) {
                [PithosUtilities httpRequestErrorAlertWithRequest:applyMetadataObjectRequest];
                @synchronized(self) {
                    applyMetadataObjectRequest = nil;
                }
            } else if ([request isEqualTo:refreshMetadataObjectRequest]) {
                [PithosUtilities httpRequestErrorAlertWithRequest:refreshMetadataObjectRequest];
                @synchronized(self) {
                    refreshMetadataObjectRequest = nil;
                }
            } else if ([request isEqualTo:refreshVersionsObjectRequest]) {
                [PithosUtilities httpRequestErrorAlertWithRequest:refreshVersionsObjectRequest];
                @synchronized(self) {
                    refreshVersionsObjectRequest = nil;
                }
            }
        }
    }
}

#pragma mark -
#pragma mark Info

- (void)applyInfo {
    @synchronized(self) {
        if (applyMetadataObjectRequest == nil) {
            if (pithosObject.subdir) {
                BOOL createObject = NO;
                NSAlert *alert;
                ASIPithosObjectRequest *request = [ASIPithosObjectRequest objectMetadataRequestWithPithos:pithos 
                                                                                            containerName:pithosContainer.name 
                                                                                               objectName:prefix];
                ASINetworkQueue *networkQueue = [ASINetworkQueue queue];
                [networkQueue go];
                [networkQueue addOperations:[NSArray arrayWithObject:[PithosUtilities prepareRequest:request]] waitUntilFinished:YES];
                if ([request error]) {
                    alert = [[NSAlert alloc] init];
                    [alert setMessageText:@"HTTP Request Error"];
                    [alert setInformativeText:[NSString stringWithFormat:@"An error occured: %@", [[request error] localizedDescription]]];
                    [alert addButtonWithTitle:@"OK"];
                    [alert runModal];
                    return;
                } else if (request.responseStatusCode == 200) {
                    alert = [[NSAlert alloc] init];
                    [alert setMessageText:@"Apply changes"];
                    [alert setInformativeText:[NSString stringWithFormat:@"In order to apply the changes in '%@', the object at the same path must be replaced. Continue?", self.displayName]];
                    [alert addButtonWithTitle:@"OK"];
                    [alert addButtonWithTitle:@"Cancel"];
                    NSInteger choice = [alert runModal];
                    if (choice == NSAlertFirstButtonReturn) {
                        request = [ASIPithosObjectRequest deleteObjectRequestWithPithos:pithos 
                                                                          containerName:pithosContainer.name 
                                                                             objectName:prefix];
                        ASINetworkQueue *networkQueue = [ASINetworkQueue queue];
                        [networkQueue go];
                        [networkQueue addOperations:[NSArray arrayWithObject:[PithosUtilities prepareRequest:request]] waitUntilFinished:YES];
                        if ([request error]) {
                            alert = [[NSAlert alloc] init];
                            [alert setMessageText:@"HTTP Request Error"];
                            [alert setInformativeText:[NSString stringWithFormat:@"An error occured: %@", [[request error] localizedDescription]]];
                            [alert addButtonWithTitle:@"OK"];
                            [alert runModal];
                            return;
                        } else if (request.responseStatusCode != 204) {
                            [PithosUtilities unexpectedResponseStatusAlertWithRequest:request];
                            return;
                        }
                        refreshParent = YES;
                        createObject = YES;
                    } else {
                        return;
                    }
                } else if (request.responseStatusCode == 404) {
                    createObject = YES;
                } else {
                    [PithosUtilities unexpectedResponseStatusAlertWithRequest:request];
                    return;
                }
                if (createObject) {
                    [[pithosNodeInfoController window] makeFirstResponder:nil];
                    applyMetadataObjectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithPithos:pithos 
                                                                                             containerName:pithosContainer.name 
                                                                                                objectName:prefix 
                                                                                                      eTag:nil
                                                                                               contentType:@"application/directory"
                                                                                           contentEncoding:pithosObject.contentEncoding 
                                                                                        contentDisposition:pithosObject.contentDisposition 
                                                                                                  manifest:pithosObject.manifest 
                                                                                                   sharing:pithosObject.sharing 
                                                                                                  isPublic:(isPublic ? ASIPithosObjectRequestPublicTrue : ASIPithosObjectRequestPublicFalse) 
                                                                                                  metadata:pithosObject.metadata 
                                                                                                      data:[NSData data]];
                    pithosObject.subdir = NO;
                    applyMetadataObjectRequest.delegate = self;
                    applyMetadataObjectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
                    applyMetadataObjectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
                    applyMetadataObjectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                                                           [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
                                                           [NSNumber numberWithUnsignedInteger:10], @"retries", 
                                                           NSStringFromSelector(@selector(objectRequestFinished:)), @"didFinishSelector", 
                                                           NSStringFromSelector(@selector(objectRequestFailed:)), @"didFailSelector", 
                                                           nil];
                    [[PithosUtilities prepareRequest:applyMetadataObjectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
                }
            } else {
                [[pithosNodeInfoController window] makeFirstResponder:nil];
                if (sharingAccount) {
                    applyMetadataObjectRequest = [ASIPithosObjectRequest updateObjectMetadataRequestWithPithos:pithos 
                                                                                                  containerName:pithosContainer.name 
                                                                                                     objectName:pithosObject.name 
                                                                                                contentEncoding:nil
                                                                                             contentDisposition:nil
                                                                                                       manifest:nil 
                                                                                                        sharing:nil 
                                                                                                       isPublic:(isPublic ? ASIPithosObjectRequestPublicTrue : ASIPithosObjectRequestPublicFalse) 
                                                                                                       metadata:pithosObject.metadata
                                                                                                         update:NO];
                    [applyMetadataObjectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
                } else {
                    applyMetadataObjectRequest = [ASIPithosObjectRequest updateObjectMetadataRequestWithPithos:pithos 
                                                                                                  containerName:pithosContainer.name 
                                                                                                     objectName:pithosObject.name 
                                                                                                contentEncoding:pithosObject.contentEncoding
                                                                                             contentDisposition:pithosObject.contentDisposition 
                                                                                                       manifest:pithosObject.manifest 
                                                                                                        sharing:(pithosObject.sharing ? pithosObject.sharing : @"") 
                                                                                                       isPublic:(isPublic ? ASIPithosObjectRequestPublicTrue : ASIPithosObjectRequestPublicFalse) 
                                                                                                       metadata:pithosObject.metadata
                                                                                                         update:NO];
                }
                applyMetadataObjectRequest.delegate = self;
                applyMetadataObjectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
                applyMetadataObjectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
                applyMetadataObjectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                                                       [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
                                                       [NSNumber numberWithUnsignedInteger:10], @"retries", 
                                                       NSStringFromSelector(@selector(objectRequestFinished:)), @"didFinishSelector", 
                                                       NSStringFromSelector(@selector(objectRequestFailed:)), @"didFailSelector", 
                                                       nil];
                [[PithosUtilities prepareRequest:applyMetadataObjectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
            }
        }
    }
}

- (void)refreshInfo {
    @synchronized(self) {
        if (pithosObject.subdir) {
            self.pithosObject = [ASIPithosObject subdirWithName:pithosObject.name];
            return;
        } else if (refreshMetadataObjectRequest == nil) {
            refreshMetadataObjectRequest = [ASIPithosObjectRequest objectMetadataRequestWithPithos:pithos 
                                                                                      containerName:pithosContainer.name 
                                                                                         objectName:prefix];
            if (sharingAccount)
                [refreshMetadataObjectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
            refreshMetadataObjectRequest.delegate = self;
            refreshMetadataObjectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
            refreshMetadataObjectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
            refreshMetadataObjectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                                                     [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
                                                     [NSNumber numberWithUnsignedInteger:10], @"retries", 
                                                     NSStringFromSelector(@selector(objectRequestFinished:)), @"didFinishSelector", 
                                                     NSStringFromSelector(@selector(objectRequestFailed:)), @"didFailSelector", 
                                                     nil];
            if (!sharingAccount)
                refreshMetadataObjectRequest.downloadCache = [ASIDownloadCache sharedCache];
            [[PithosUtilities prepareRequest:refreshMetadataObjectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
        }
    }
    [self refreshVersions];
}

#pragma mark -
#pragma mark Versions

- (void)refreshVersions {
    @synchronized(self) {
        if (pithosObject.subdir) {
            return;
        } else if (refreshVersionsObjectRequest == nil) {
            refreshVersionsObjectRequest = [ASIPithosObjectRequest objectVersionsRequestWithPithos:pithos 
                                                                                      containerName:pithosContainer.name 
                                                                                         objectName:pithosObject.name];
            if (sharingAccount)
                [refreshVersionsObjectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
            refreshVersionsObjectRequest.delegate = self;
            refreshVersionsObjectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
            refreshVersionsObjectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
            refreshVersionsObjectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                                                     [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
                                                     [NSNumber numberWithUnsignedInteger:10], @"retries", 
                                                     NSStringFromSelector(@selector(objectRequestFinished:)), @"didFinishSelector", 
                                                     NSStringFromSelector(@selector(objectRequestFailed:)), @"didFailSelector", 
                                                     nil];
            if (!sharingAccount)
                refreshVersionsObjectRequest.downloadCache = [ASIDownloadCache sharedCache];
            [[PithosUtilities prepareRequest:refreshVersionsObjectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
        }
    }
}

#pragma mark -
#pragma mark Actions

- (void)showPithosNodeInfo:(id)sender {
    if (!pithosNodeInfoController) {
        pithosNodeInfoController = [[PithosObjectNodeInfoController alloc] initWithPithosNode:self];
        [self refreshInfo];
    }
    [pithosNodeInfoController showWindow:sender];
    [[pithosNodeInfoController window] makeKeyAndOrderFront:sender];
    [NSApp activateIgnoringOtherApps:YES];
}

@end
