//  ASIPithosAccountRequest.m
//  Based on ASICloudFilesContainerRequest.m
//
// Copyright 2011 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 "ASIPithosAccountRequest.h"
#import "ASIPithosAccount.h"
#import "ASIPithosContainer.h"

@implementation ASIPithosAccountRequest
@synthesize currentContainer;

#pragma mark -
#pragma mark Constructors

+ (id)storageRequestWithMethod:(NSString *)method queryString:(NSString *)queryString {
    NSString *urlString = [NSString stringWithString:[self storageURL]];
    if (queryString)
        urlString = [urlString stringByAppendingString:queryString];

	ASIPithosAccountRequest *request = [[[self alloc] initWithURL:[NSURL URLWithString:urlString]] autorelease];
	[request setRequestMethod:method];
	[request addRequestHeader:@"X-Auth-Token" value:[self authToken]];
	return request;
}

+ (id)storageRequestWithMethod:(NSString *)method {
	return [self storageRequestWithMethod:method queryString:nil];
}

#pragma mark -
#pragma mark Memory Management

- (void)dealloc {
	[currentContainer release];
    [containers release];
    [metadata release];
    [policy release];
    [groups release];
	[super dealloc];
}

#pragma mark -
#pragma mark HEAD

// HEAD storageURL
+ (id)accountMetadataRequest {
	return [self storageRequestWithMethod:@"HEAD"];
}

// HEAD storageURL?[until=untilTimestamp]
+ (id)accountMetadataRequestUntil:(NSDate *)untilTimestamp {
    NSString *queryString = nil;
    if (untilTimestamp)
        queryString = [NSString stringWithFormat:@"?until=%d", (int)[untilTimestamp timeIntervalSince1970]];
	return [self storageRequestWithMethod:@"HEAD" queryString:queryString];
}

// HEAD storageURL?[until=untilTimestamp] [If-Modified-Since]
+ (id)accountMetadataRequestUntil:(NSDate *)untilTimestamp ifModifiedSince:(NSDate *)sinceTimestamp {
    ASIPithosAccountRequest *request = [self accountMetadataRequestUntil:untilTimestamp];
    [request addRequestIfModifiedSinceHeader:sinceTimestamp];
    return request;
}

// HEAD storageURL?[until=untilTimestamp] [If-Unmodified-Since]
+ (id)accountMetadataRequestUntil:(NSDate *)untilTimestamp ifUnmodifiedSince:(NSDate *)sinceTimestamp {
    ASIPithosAccountRequest *request = [self accountMetadataRequestUntil:untilTimestamp];
    [request addRequestIfUnmodifiedSinceHeader:sinceTimestamp];
    return request;
}

- (NSUInteger)containerCount {
	return [[[self responseHeaders] objectForKey:@"X-Account-Container-Count"] intValue];
}

- (NSUInteger)bytesUsed {
	return [[[self responseHeaders] objectForKey:@"X-Account-Bytes-Used"] intValue];
}

- (NSDate *)untilTimestamp {
    NSString *untilTimestampString = [[self responseHeaders] objectForKey:@"X-Account-Until-Timestamp"];
    if (untilTimestampString)
        return [[self dateFormatterWithFormatId:1] dateFromString:untilTimestampString];
    return nil;
}

- (NSDictionary *)groups {
    if (groups == nil) {
        groups = [self getHeadersDictionaryForPrefix:@"X-Account-Group-"];
        [groups retain];
    }
    return groups;
}

- (NSDictionary *)policy {
    if (policy == nil) {
        policy = [self getHeadersDictionaryForPrefix:@"X-Account-Policy-"];
        [policy retain];
    }
    return policy;
}

- (NSDictionary *)metadata {
    if (metadata == nil) {
        metadata = [self getHeadersDictionaryForPrefix:@"X-Account-Meta-"];
        [metadata retain];
    }
    return metadata;
}

- (NSDate *)lastModified {
    NSString *lastModifiedString = [[self responseHeaders] objectForKey:@"Last-Modified"];
    if (lastModifiedString)
        return [[self dateFormatterWithFormatId:1] dateFromString:lastModifiedString];
    return nil;
}

- (ASIPithosAccount *)account {
    ASIPithosAccount *account = [ASIPithosAccount account];
    account.containerCount = [self containerCount];
    account.bytesUsed = [self bytesUsed];
    account.untilTimestamp = [self untilTimestamp];
    account.groups = (NSMutableDictionary *)[self groups];
    account.policy = (NSMutableDictionary *)[self policy];
    account.metadata = (NSMutableDictionary *)[self metadata];
    account.lastModified = [self lastModified];
    return account;
}


#pragma mark -
#pragma mark GET

// GET storageURL
+ (id)listContainersRequest {
	return [self storageRequestWithMethod:@"GET" queryString:@"?format=xml"];
}

// GET storageURL?[limit=limit]&[marker=marker]&[shared=]&[until=untilTimestamp]
+ (id)listContainersRequestWithLimit:(NSUInteger)limit 
                              marker:(NSString *)marker 
                              shared:(BOOL)shared 
                               until:(NSDate *)untilTimestamp {
	NSString *queryString = @"?format=xml";
	if (limit && (limit > 0))
		queryString = [queryString stringByAppendingString:[NSString stringWithFormat:@"&limit=%lu", limit]];
	if (marker)
		queryString = [queryString stringByAppendingString:[NSString stringWithFormat:@"&marker=%@", [marker stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];
	if (shared)
		queryString = [queryString stringByAppendingString:@"&shared="];    
    if (untilTimestamp)
        queryString = [queryString stringByAppendingString:[NSString stringWithFormat:@"&until=%d", (int)[untilTimestamp timeIntervalSince1970]]];
	
	return [self storageRequestWithMethod:@"GET" queryString:queryString];
}

// GET storageURL?[limit=limit]&[marker=marker]&[shared=]&[until=untilTimestamp] [If-Modified-Since]
+ (id)listContainersRequestWithLimit:(NSUInteger)limit 
                              marker:(NSString *)marker 
                              shared:(BOOL)shared 
                               until:(NSDate *)untilTimestamp 
                     ifModifiedSince:(NSDate *)sinceTimestamp {
    ASIPithosAccountRequest *request = [self listContainersRequestWithLimit:limit marker:marker shared:shared until:untilTimestamp];
    [request addRequestIfModifiedSinceHeader:sinceTimestamp];
    return request;
}

// GET storageURL?[limit=limit]&[marker=marker]&[shared=]&[until=untilTimestamp] [If-Unmodified-Since]
+ (id)listContainersRequestWithLimit:(NSUInteger)limit 
                              marker:(NSString *)marker 
                              shared:(BOOL)shared 
                               until:(NSDate *)untilTimestamp 
                     ifUnmodifiedSince:(NSDate *)sinceTimestamp {
    ASIPithosAccountRequest *request = [self listContainersRequestWithLimit:limit marker:marker shared:shared until:untilTimestamp];
    [request addRequestIfUnmodifiedSinceHeader:sinceTimestamp];
    return request;
}


- (NSArray *)containers {
    if (containers == nil) {
        containers = [[NSMutableArray alloc] init];
        
        NSXMLParser *parser = [[[NSXMLParser alloc] initWithData:[self responseData]] autorelease];
        [parser setDelegate:self];
        [parser setShouldProcessNamespaces:NO];
        [parser setShouldReportNamespacePrefixes:NO];
        [parser setShouldResolveExternalEntities:NO];
        [parser parse];
    }    
	return containers;
}

#pragma mark -
#pragma mark POST


// POST storageURL
+ (id)updateAccountMetadataRequest {
    return [self storageRequestWithMethod:@"POST"];
}

// POST storageURL?[update=] [X-Account-Group-*] [X-Account-Meta-*]
+ (id)updateAccountMetadataRequestWithGroups:(NSDictionary *)groups metadata:(NSDictionary *)metadata update:(BOOL)update {
    NSString *queryString = nil;
    if (update)
        queryString = @"?update=";
    ASIPithosAccountRequest *request = [self storageRequestWithMethod:@"POST" queryString:queryString];
	
    if (groups) {
        for (NSString *key in [groups keyEnumerator]) {
            [request addRequestHeader:[[NSString stringWithFormat:@"X-Account-Group-%@", key] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] 
                                value:[[groups objectForKey:key] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
        }
    }
    
    if (metadata) {
        for (NSString *key in [metadata keyEnumerator]) {
            [request addRequestHeader:[[NSString stringWithFormat:@"X-Account-Meta-%@", key] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] 
                                value:[[metadata objectForKey:key] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
        }
    }
    
	return request;
}

#pragma mark -
#pragma mark NSXMLParserDelegate

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
    self.currentElement = elementName;
	if ([elementName isEqualToString:@"container"]) {
		self.currentContainer = [ASIPithosContainer container];
	}
	self.currentContent = @"";
}

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
	if ([elementName isEqualToString:@"name"]) {
        currentContainer.name = currentContent;
	} else if ([elementName isEqualToString:@"count"]) {
        currentContainer.count = [currentContent intValue];
	} else if ([elementName isEqualToString:@"bytes"]) {
        currentContainer.bytes = [currentContent intValue];
    } else if ([elementName isEqualToString:@"last_modified"]) {
        currentContainer.lastModified = [[self dateFormatterWithFormatId:0] dateFromString:
                                         [currentContent stringByReplacingOccurrencesOfString:@":" 
                                                                                   withString:@"" 
                                                                                      options:NSBackwardsSearch 
                                                                                        range:NSMakeRange(([currentContent length] - 3), 1)]];
    } else if ([elementName isEqualToString:@"x_container_until_timestamp"]) {
        currentContainer.untilTimestamp = [NSDate dateWithTimeIntervalSince1970:[currentContent doubleValue]];
    } else if ([elementName rangeOfString:@"x_container_policy_"].location == 0) {
        if (currentContainer.policy == nil)
            currentContainer.policy = [NSMutableDictionary dictionary];
        [currentContainer.policy setObject:currentContent forKey:[elementName substringFromIndex:19]];
    } else if ([elementName rangeOfString:@"x_container_meta_"].location == 0) {    
        if (currentContainer.metadata == nil)
            currentContainer.metadata = [NSMutableDictionary dictionary];
        [currentContainer.metadata setObject:currentContent forKey:[elementName substringFromIndex:17]];
	} else if ([elementName isEqualToString:@"container"]) {
		[containers addObject:currentContainer];
        self.currentContainer = nil;
	}
}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
    self.currentContent = [currentContent stringByAppendingString:string];
}
    
@end
