//
//  ZYJOSSModel.m
//  ZYJOSS_ios_sdk
//
//  Created by zhouzhuo on 8/16/15.
//  Copyright (c) 2015 aliyun.com. All rights reserved.
//
#import "ZYJOSSDefine.h"
#import "ZYJOSSModel.h"
#import "ZYJOSSBolts.h"
#import "ZYJOSSUtil.h"
#import "ZYJOSSNetworking.h"
#import "ZYJOSSLog.h"
#import "ZYJOSSXMLDictionary.h"
#if TARGET_OS_IOS
#import <UIKit/UIDevice.h>
#endif

#import "ZYJOSSAllRequestNeededMessage.h"

@implementation NSDictionary (ZYJOSS)

- (NSString *)base64JsonString {
    NSError * error;
    NSData * jsonData = [NSJSONSerialization dataWithJSONObject:self
                                                        options:0
                                                          error:&error];

    if (!jsonData) {
        return @"e30="; // base64("{}");
    } else {
        NSString * jsonStr = [[[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding] stringByReplacingOccurrencesOfString:@"\\/" withString:@"/"];
        NSLog(@"callback json - %@", jsonStr);
        return [[jsonStr dataUsingEncoding:NSUTF8StringEncoding] base64EncodedStringWithOptions:0];
    }
}

@end

@implementation ZYJOSSSyncMutableDictionary

- (instancetype)init {
    if (self = [super init]) {
        _dictionary = [NSMutableDictionary dictionary];
        _dispatchQueue = dispatch_queue_create("com.aliyun.aliyunsycmutabledictionary", DISPATCH_QUEUE_SERIAL);
    }

    return self;
}

- (NSArray *)allKeys {
    __block NSArray *allKeys = nil;
    dispatch_sync(self.dispatchQueue, ^{
        allKeys = [self.dictionary allKeys];
    });
    return allKeys;
}

- (id)objectForKey:(id)aKey {
    __block id returnObject = nil;

    dispatch_sync(self.dispatchQueue, ^{
        returnObject = [self.dictionary objectForKey:aKey];
    });

    return returnObject;
}

- (void)setObject:(id)anObject forKey:(id <NSCopying>)aKey {
    dispatch_sync(self.dispatchQueue, ^{
        [self.dictionary ZYJOSS_setObject:anObject forKey:aKey];
    });
}

- (void)removeObjectForKey:(id)aKey {
    dispatch_sync(self.dispatchQueue, ^{
        [self.dictionary removeObjectForKey:aKey];
    });
}

@end

@implementation ZYJOSSFederationToken

- (NSString *)description
{
    return [NSString stringWithFormat:@"ZYJOSSFederationToken<%p>:{AccessKeyId: %@\nAccessKeySecret: %@\nSecurityToken: %@\nExpiration: %@}", self, _tAccessKey, _tSecretKey, _tToken, _expirationTimeInGMTFormat];
}

@end

@implementation ZYJOSSPlainTextAKSKPairCredentialProvider

- (instancetype)initWithPlainTextAccessKey:(nonnull NSString *)accessKey secretKey:(nonnull NSString *)secretKey {
    if (self = [super init]) {
        self.accessKey = [accessKey ZYJOSS_trim];
        self.secretKey = [secretKey ZYJOSS_trim];
    }
    return self;
}

- (nullable NSString *)sign:(NSString *)content error:(NSError **)error {
    if (![self.accessKey ZYJOSS_isNotEmpty] || ![self.secretKey ZYJOSS_isNotEmpty])
    {
        if (error != nil)
        {
            *error = [NSError errorWithDomain:ZYJOSSClientErrorDomain
                                         code:ZYJOSSClientErrorCodeSignFailed
                                     userInfo:@{ZYJOSSErrorMessageTOKEN: @"accessKey or secretKey can't be null"}];
        }
        
        return nil;
    }
    NSString * sign = [ZYJOSSUtil calBase64Sha1WithData:content withSecret:self.secretKey];
    return [NSString stringWithFormat:@"OSS %@:%@", self.accessKey, sign];
}

@end

@implementation ZYJOSSCustomSignerCredentialProvider

- (instancetype)initWithImplementedSigner:(ZYJOSSCustomSignContentBlock)signContent
{
    NSParameterAssert(signContent);
    if (self = [super init])
    {
        _signContent = signContent;
    }
    return self;
}

- (NSString *)sign:(NSString *)content error:(NSError **)error
{
    NSString * signature = @"";
    @synchronized(self) {
        signature = self.signContent(content, error);
    }
    if (*error) {
        *error = [NSError errorWithDomain:ZYJOSSClientErrorDomain
                                     code:ZYJOSSClientErrorCodeSignFailed
                                 userInfo:[[NSDictionary alloc] initWithDictionary:[*error userInfo]]];
        return nil;
    }
    return signature;
}

@end

@implementation ZYJOSSFederationCredentialProvider

- (instancetype)initWithFederationTokenGetter:(ZYJOSSGetFederationTokenBlock)federationTokenGetter {
    if (self = [super init]) {
        self.federationTokenGetter = federationTokenGetter;
    }
    return self;
}

- (nullable ZYJOSSFederationToken *)getToken:(NSError **)error {
    ZYJOSSFederationToken * validToken = nil;
    @synchronized(self) {
        if (self.cachedToken == nil) {

            self.cachedToken = self.federationTokenGetter();
        } else {
            if (self.cachedToken.expirationTimeInGMTFormat) {
                NSDateFormatter * fm = [NSDateFormatter new];
                [fm setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZ"];
                self.cachedToken.expirationTimeInMilliSecond = [[fm dateFromString:self.cachedToken.expirationTimeInGMTFormat] timeIntervalSince1970] * 1000;
                self.cachedToken.expirationTimeInGMTFormat = nil;
                ZYJOSSLogVerbose(@"Transform GMT date to expirationTimeInMilliSecond: %lld", self.cachedToken.expirationTimeInMilliSecond);
            }

            NSDate * expirationDate = [NSDate dateWithTimeIntervalSince1970:(NSTimeInterval)(self.cachedToken.expirationTimeInMilliSecond / 1000)];
            NSTimeInterval interval = [expirationDate timeIntervalSinceDate:[NSDate ZYJOSS_clockSkewFixedDate]];
            /* if this token will be expired after less than 2min, we abort it in case of when request arrived ZYJOSS server,
               it's expired already. */
            if (interval < 5 * 60) {
                ZYJOSSLogDebug(@"get federation token, but after %lf second it would be expired", interval);
                self.cachedToken = self.federationTokenGetter();
            }
        }

        validToken = self.cachedToken;
    }
    if (!validToken)
    {
        if (error != nil)
        {
            *error = [NSError errorWithDomain:ZYJOSSClientErrorDomain
                                         code:ZYJOSSClientErrorCodeSignFailed
                                     userInfo:@{ZYJOSSErrorMessageTOKEN: @"Can't get a federation token"}];
        }
        
        return nil;
    }
    return validToken;
}

@end

@implementation ZYJOSSStsTokenCredentialProvider

- (ZYJOSSFederationToken *)getToken {
    ZYJOSSFederationToken * token = [ZYJOSSFederationToken new];
    token.tAccessKey = self.accessKeyId;
    token.tSecretKey = self.secretKeyId;
    token.tToken = self.securityToken;
    token.expirationTimeInMilliSecond = NSIntegerMax;
    return token;
}

- (instancetype)initWithAccessKeyId:(NSString *)accessKeyId secretKeyId:(NSString *)secretKeyId securityToken:(NSString *)securityToken {
    if (self = [super init]) {
        self.accessKeyId = [accessKeyId ZYJOSS_trim];
        self.secretKeyId = [secretKeyId ZYJOSS_trim];
        self.securityToken = [securityToken ZYJOSS_trim];
    }
    return self;
}

- (NSString *)sign:(NSString *)content error:(NSError **)error {
    NSString * sign = [ZYJOSSUtil calBase64Sha1WithData:content withSecret:self.secretKeyId];
    return [NSString stringWithFormat:@"OSS %@:%@", self.accessKeyId, sign];
}

@end

@implementation ZYJOSSAuthCredentialProvider

- (instancetype)initWithAuthServerUrl:(NSString *)authServerUrl
{
    return [self initWithAuthServerUrl:authServerUrl responseDecoder:nil];
}

- (instancetype)initWithAuthServerUrl:(NSString *)authServerUrl responseDecoder:(nullable ZYJOSSResponseDecoderBlock)decoder
{
    self = [super initWithFederationTokenGetter:^ZYJOSSFederationToken * {
        NSURL * url = [NSURL URLWithString:self.authServerUrl];
        NSURLRequest * request = [NSURLRequest requestWithURL:url];
        ZYJOSSTaskCompletionSource * tcs = [ZYJOSSTaskCompletionSource taskCompletionSource];
        NSURLSession * session = [NSURLSession sharedSession];
        NSURLSessionTask * sessionTask = [session dataTaskWithRequest:request
                                                    completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
                                                        if (error) {
                                                            [tcs setError:error];
                                                            return;
                                                        }
                                                        [tcs setResult:data];
                                                    }];
        [sessionTask resume];
        [tcs.task waitUntilFinished];
        if (tcs.task.error) {
            return nil;
        } else {
            NSData* data = tcs.task.result;
            if(decoder){
                data = decoder(data);
            }
            NSDictionary * object = [NSJSONSerialization JSONObjectWithData:data
                                                                    options:kNilOptions
                                                                      error:nil];
            int statusCode = [[object objectForKey:@"StatusCode"] intValue];
            if (statusCode == 200) {
                ZYJOSSFederationToken * token = [ZYJOSSFederationToken new];
                // All the entries below are mandatory.
                token.tAccessKey = [object objectForKey:@"AccessKeyId"];
                token.tSecretKey = [object objectForKey:@"AccessKeySecret"];
                token.tToken = [object objectForKey:@"SecurityToken"];
                token.expirationTimeInGMTFormat = [object objectForKey:@"Expiration"];
                ZYJOSSLogDebug(@"token: %@ %@ %@ %@", token.tAccessKey, token.tSecretKey, token.tToken, [object objectForKey:@"Expiration"]);
                return token;
            }else{
                return nil;
            }
            
        }
    }];
    if(self){
        self.authServerUrl = authServerUrl;
    }
    return self;
}

@end

NSString * const ZYJBACKGROUND_SESSION_IDENTIFIER = @"com.aliyun.oss.backgroundsession";

@implementation ZYJOSSClientConfiguration

- (instancetype)init {
    if (self = [super init]) {
        self.maxRetryCount = ZYJOSSDefaultRetryCount;
        self.maxConcurrentRequestCount = ZYJOSSDefaultMaxConcurrentNum;
        self.enableBackgroundTransmitService = NO;
        self.isHttpdnsEnable = YES;
        self.backgroundSesseionIdentifier = ZYJBACKGROUND_SESSION_IDENTIFIER;
        self.timeoutIntervalForRequest = ZYJOSSDefaultTimeoutForRequestInSecond;
        self.timeoutIntervalForResource = ZYJOSSDefaultTimeoutForResourceInSecond;
    }
    return self;
}

- (void)setCnameExcludeList:(NSArray *)cnameExcludeList {
    NSMutableArray * array = [NSMutableArray new];
    [cnameExcludeList enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        NSString * host = [(NSString *)obj lowercaseString];
        if ([host containsString:@"://"]) {
            NSString * trimHost = [host substringFromIndex:[host rangeOfString:@"://"].location + 3];
            [array addObject:trimHost];
        } else {
            [array addObject:host];
        }
    }];
    _cnameExcludeList = array.copy;
}

@end

@implementation ZYJOSSSignerInterceptor

- (instancetype)initWithCredentialProvider:(id<ZYJOSSCredentialProvider>)credentialProvider {
    if (self = [super init]) {
        self.credentialProvider = credentialProvider;
    }
    return self;
}

- (ZYJOSSTask *)interceptRequestMessage:(ZYJOSSAllRequestNeededMessage *)requestMessage {
    ZYJOSSLogVerbose(@"signing intercepting - ");
    NSError * error = nil;

    /****************************************************************
    * define a constant array to contain all specified subresource */
    static NSArray * ZYJOSSSubResourceARRAY = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        ZYJOSSSubResourceARRAY = @[@"acl", @"uploadId", @"partNumber", @"uploads", @"logging", @"website", @"location",
                                @"lifecycle", @"referer", @"cors", @"delete", @"append", @"position", @"security-token", @"x-oss-process", @"sequential",@"bucketInfo",@"symlink", @"restore"];
    });
    /****************************************************************/

    /* initial each part of content to sign */
    NSString * method = requestMessage.httpMethod;
    NSString * contentType = @"";
    NSString * contentMd5 = @"";
    NSString * date = requestMessage.date;
    NSString * xZYJOSSHeader = @"";
    NSString * resource = @"";

    ZYJOSSFederationToken * federationToken = nil;

    if (requestMessage.contentType) {
        contentType = requestMessage.contentType;
    }
    if (requestMessage.contentMd5) {
        contentMd5 = requestMessage.contentMd5;
    }

    /* if credential provider is a federation token provider, it need to specially handle */
    if ([self.credentialProvider isKindOfClass:[ZYJOSSFederationCredentialProvider class]]) {
        federationToken = [(ZYJOSSFederationCredentialProvider *)self.credentialProvider getToken:&error];
        if (error) {
            return [ZYJOSSTask taskWithError:error];
        }
        [requestMessage.headerParams ZYJOSS_setObject:federationToken.tToken forKey:@"x-oss-security-token"];
    } else if ([self.credentialProvider isKindOfClass:[ZYJOSSStsTokenCredentialProvider class]]) {
        federationToken = [(ZYJOSSStsTokenCredentialProvider *)self.credentialProvider getToken];
        [requestMessage.headerParams ZYJOSS_setObject:federationToken.tToken forKey:@"x-oss-security-token"];
    }
    
    [requestMessage.headerParams ZYJOSS_setObject:requestMessage.contentSHA1 forKey:ZYJOSSHttpHeaderHashSHA1];
        
    /* construct CanonicalizedZYJOSSHeaders */
    if (requestMessage.headerParams) {
        NSMutableArray * params = [[NSMutableArray alloc] init];
        NSArray * sortedKey = [[requestMessage.headerParams allKeys] sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
            return [obj1 compare:obj2];
        }];
        for (NSString * key in sortedKey) {
            if ([key hasPrefix:@"x-oss-"]) {
                [params addObject:[NSString stringWithFormat:@"%@:%@", key, [requestMessage.headerParams objectForKey:key]]];
            }
        }
        if ([params count]) {
            xZYJOSSHeader = [NSString stringWithFormat:@"%@\n", [params componentsJoinedByString:@"\n"]];
        }
    }

    /* construct CanonicalizedResource */
    resource = @"/";
    if (requestMessage.bucketName) {
        resource = [NSString stringWithFormat:@"/%@/", requestMessage.bucketName];
    }
    if (requestMessage.objectKey) {
        resource = [resource ZYJOSS_stringByAppendingPathComponentForURL:requestMessage.objectKey];
    }
    if (requestMessage.params) {
        NSMutableArray * querys = [[NSMutableArray alloc] init];
        NSArray * sortedKey = [[requestMessage.params allKeys] sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
            return [obj1 compare:obj2];
        }];
        for (NSString * key in sortedKey) {
            NSString * value = [requestMessage.params objectForKey:key];

            if (![ZYJOSSSubResourceARRAY containsObject:key]) { // notice it's based on content compare
                continue;
            }

            if ([value isEqualToString:@""]) {
                [querys addObject:[NSString stringWithFormat:@"%@", key]];
            } else {
                [querys addObject:[NSString stringWithFormat:@"%@=%@", key, value]];
            }
        }
        if ([querys count]) {
            resource = [resource stringByAppendingString:[NSString stringWithFormat:@"?%@",[querys componentsJoinedByString:@"&"]]];
        }
    }

    /* now, join every part of content and sign */
    NSString * stringToSign = [NSString stringWithFormat:@"%@\n%@\n%@\n%@\n%@%@", method, contentMd5, contentType, date, xZYJOSSHeader, resource];
    ZYJOSSLogDebug(@"string to sign: %@", stringToSign);
    if ([self.credentialProvider isKindOfClass:[ZYJOSSFederationCredentialProvider class]]
        || [self.credentialProvider isKindOfClass:[ZYJOSSStsTokenCredentialProvider class]])
    {
        NSString * signature = [ZYJOSSUtil sign:stringToSign withToken:federationToken];
        [requestMessage.headerParams ZYJOSS_setObject:signature forKey:@"Authorization"];
    }else if ([self.credentialProvider isKindOfClass:[ZYJOSSCustomSignerCredentialProvider class]])
    {
        ZYJOSSCustomSignerCredentialProvider *provider = (ZYJOSSCustomSignerCredentialProvider *)self.credentialProvider;
        
        NSError *customSignError;
        NSString * signature = [provider sign:stringToSign error:&customSignError];
        if (customSignError) {
            ZYJOSSLogError(@"ZYJOSSCustomSignerError: %@", customSignError)
            return [ZYJOSSTask taskWithError: customSignError];
        }
        [requestMessage.headerParams ZYJOSS_setObject:signature forKey:@"Authorization"];
    }else
    {
        NSString * signature = [self.credentialProvider sign:stringToSign error:&error];
        if (error) {
            return [ZYJOSSTask taskWithError:error];
        }
        [requestMessage.headerParams ZYJOSS_setObject:signature forKey:@"Authorization"];
    }
    return [ZYJOSSTask taskWithResult:nil];
}

@end

@implementation ZYJOSSUASettingInterceptor

- (instancetype)initWithClientConfiguration:(ZYJOSSClientConfiguration *)clientConfiguration{
    if (self = [super init]) {
        self.clientConfiguration = clientConfiguration;
    }
    return self;
}

- (ZYJOSSTask *)interceptRequestMessage:(ZYJOSSAllRequestNeededMessage *)request {
    NSString * userAgent = [self getUserAgent:self.clientConfiguration.userAgentMark];
    [request.headerParams ZYJOSS_setObject:userAgent forKey:@"User-Agent"];
    return [ZYJOSSTask taskWithResult:nil];
}


- (NSString *)getUserAgent:(NSString *)customUserAgent {
    static NSString * userAgent = nil;
    static dispatch_once_t once;
    NSString * tempUserAgent = nil;
    dispatch_once(&once, ^{
        NSString *localeIdentifier = [[NSLocale currentLocale] localeIdentifier];
#if TARGET_OS_IOS
        NSString *systemName = [[[UIDevice currentDevice] systemName] stringByReplacingOccurrencesOfString:@" " withString:@"-"];
        NSString *systemVersion = [[UIDevice currentDevice] systemVersion];
        userAgent = [NSString stringWithFormat:@"%@/%@(/%@/%@/%@)", ZYJOSSUAPrefix, ZYJOSSSDKVersion, systemName, systemVersion, localeIdentifier];
#elif TARGET_OS_OSX
        userAgent = [NSString stringWithFormat:@"%@/%@(/%@/%@/%@)", ZYJOSSUAPrefix, ZYJOSSSDKVersion, @"OSX", [NSProcessInfo processInfo].operatingSystemVersionString, localeIdentifier];
#endif
    });
    if(customUserAgent){
        if(userAgent){
            tempUserAgent = [[userAgent stringByAppendingString:@"/"] stringByAppendingString:customUserAgent];
        }else{
            tempUserAgent = customUserAgent;
        }
    }else{
        tempUserAgent = userAgent;
    }
    return tempUserAgent;
}

@end

@implementation ZYJOSSTimeSkewedFixingInterceptor

- (ZYJOSSTask *)interceptRequestMessage:(ZYJOSSAllRequestNeededMessage *)request {
    request.date = [[NSDate ZYJOSS_clockSkewFixedDate] ZYJOSS_asStringValue];
    return [ZYJOSSTask taskWithResult:nil];
}

@end

@implementation ZYJOSSRange

- (instancetype)initWithStart:(int64_t)start withEnd:(int64_t)end {
    if (self = [super init]) {
        self.startPosition = start;
        self.endPosition = end;
    }
    return self;
}

- (NSString *)toHeaderString {

    NSString * rangeString = nil;

    if (self.startPosition < 0 && self.endPosition < 0) {
        rangeString = [NSString stringWithFormat:@"bytes=%lld-%lld", self.startPosition, self.endPosition];
    } else if (self.startPosition < 0) {
        rangeString = [NSString stringWithFormat:@"bytes=-%lld", self.endPosition];
    } else if (self.endPosition < 0) {
        rangeString = [NSString stringWithFormat:@"bytes=%lld-", self.startPosition];
    } else {
        rangeString = [NSString stringWithFormat:@"bytes=%lld-%lld", self.startPosition, self.endPosition];
    }

    return rangeString;
}

@end

#pragma mark request and result objects

@implementation ZYJOSSGetServiceRequest

- (NSDictionary *)requestParams {
    NSMutableDictionary * params = [NSMutableDictionary dictionary];
    [params ZYJOSS_setObject:self.prefix forKey:@"prefix"];
    [params ZYJOSS_setObject:self.marker forKey:@"marker"];
    if (self.maxKeys > 0) {
        [params ZYJOSS_setObject:[@(self.maxKeys) stringValue] forKey:@"max-keys"];
    }
    return [params copy];
}

@end

@implementation ZYJOSSGetServiceResult
@end

@implementation ZYJOSSCreateBucketRequest

- (instancetype)init
{
    self = [super init];
    if (self) {
        _storageClass = ZYJOSSBucketStorageClassStandard;
    }
    return self;
}

- (NSString *)storageClassAsString {
    NSString *storageClassString = nil;
    switch (_storageClass) {
        case ZYJOSSBucketStorageClassStandard:
            storageClassString = @"Standard";
            break;
        case ZYJOSSBucketStorageClassIA:
            storageClassString = @"IA";
            break;
        case ZYJOSSBucketStorageClassArchive:
            storageClassString = @"Archive";
            break;
        default:
            storageClassString = @"Unknown";
            break;
    }
    return storageClassString;
}

@end

@implementation ZYJOSSCreateBucketResult
@end

@implementation ZYJOSSDeleteBucketRequest
@end

@implementation ZYJOSSDeleteBucketResult
@end

@implementation ZYJOSSGetBucketRequest

- (NSDictionary *)requestParams {
    NSMutableDictionary * params = [NSMutableDictionary dictionary];
    [params ZYJOSS_setObject:self.delimiter forKey:@"delimiter"];
    [params ZYJOSS_setObject:self.prefix forKey:@"prefix"];
    [params ZYJOSS_setObject:self.marker forKey:@"marker"];
    if (self.maxKeys > 0) {
        [params ZYJOSS_setObject:[@(self.maxKeys) stringValue] forKey:@"max-keys"];
    }
    return [params copy];
}

@end

@implementation ZYJOSSListMultipartUploadsRequest
- (NSDictionary *)requestParams {
    NSMutableDictionary * params = [NSMutableDictionary dictionary];
    [params ZYJOSS_setObject:self.delimiter forKey:@"delimiter"];
    [params ZYJOSS_setObject:self.prefix forKey:@"prefix"];
    [params ZYJOSS_setObject:self.keyMarker forKey:@"key-marker"];
    [params ZYJOSS_setObject:self.uploadIdMarker forKey:@"upload-id-marker"];
    [params ZYJOSS_setObject:self.encodingType forKey:@"encoding-type"];
    
    if (self.maxUploads > 0) {
        [params ZYJOSS_setObject:[@(self.maxUploads) stringValue] forKey:@"max-uploads"];
    }
    
    return [params copy];
}
@end

@implementation ZYJOSSListMultipartUploadsResult
@end

@implementation ZYJOSSGetBucketResult
@end

@implementation ZYJOSSGetBucketACLRequest

- (NSDictionary *)requestParams {
    return @{@"acl": @""};
}

@end

@implementation ZYJOSSGetBucketACLResult
@end

@implementation ZYJOSSHeadObjectRequest
@end

@implementation ZYJOSSHeadObjectResult
@end

@implementation ZYJOSSGetObjectRequest
@end

@implementation ZYJOSSGetObjectResult
@end

@implementation ZYJOSSPutObjectACLRequest

- (instancetype)init
{
    self = [super init];
    if (self) {
        _acl = @"default";
    }
    return self;
}

@end

@implementation ZYJOSSPutObjectACLResult
@end

@implementation ZYJOSSPutObjectRequest

- (instancetype)init {
    if (self = [super init]) {
        self.objectMeta = [NSDictionary new];
    }
    return self;
}

@end

@implementation ZYJOSSPutObjectResult
@end

@implementation ZYJOSSAppendObjectRequest

- (instancetype)init {
    if (self = [super init]) {
        self.objectMeta = [NSDictionary new];
    }
    return self;
}

@end

@implementation ZYJOSSAppendObjectResult
@end

@implementation ZYJOSSDeleteObjectRequest
@end

@implementation ZYJOSSDeleteObjectResult
@end

@implementation ZYJOSSCopyObjectRequest

- (instancetype)init {
    if (self = [super init]) {
        self.objectMeta = [NSDictionary new];
    }
    return self;
}

@end

@implementation ZYJOSSCopyObjectResult
@end

@implementation ZYJOSSInitMultipartUploadRequest

- (instancetype)init {
    if (self = [super init]) {
        self.objectMeta = [NSDictionary new];
    }
    return self;
}

@end

@implementation ZYJOSSInitMultipartUploadResult
@end

@implementation ZYJOSSUploadPartRequest
@end

@implementation ZYJOSSUploadPartResult
@end

@implementation ZYJOSSPartInfo

+ (instancetype)partInfoWithPartNum:(int32_t)partNum
                               eTag:(NSString *)eTag
                               size:(int64_t)size {
    return [self partInfoWithPartNum:partNum
                                eTag:eTag
                                size:size
                               crc64:0];
}

+ (instancetype)partInfoWithPartNum:(int32_t)partNum eTag:(NSString *)eTag size:(int64_t)size crc64:(uint64_t)crc64
{
    ZYJOSSPartInfo *parInfo = [ZYJOSSPartInfo new];
    parInfo.partNum = partNum;
    parInfo.eTag = eTag;
    parInfo.size = size;
    parInfo.crc64 = crc64;
    return parInfo;
}

- (nonnull NSDictionary *)entityToDictionary
{
    NSMutableDictionary *dict = [NSMutableDictionary dictionary];
    [dict setValue:@(_partNum) forKey:@"partNum"];
    if (_eTag)
    {
        [dict setValue:_eTag forKey:@"eTag"];
    }
    [dict setValue:@(_size) forKey:@"size"];
    [dict setValue:@(_crc64) forKey:@"crc64"];
    return [dict copy];
}

- (NSString *)description
{
    return [NSString stringWithFormat:@"ZYJOSSPartInfo<%p>:{partNum: %d,eTag: %@,partSize: %lld,crc64: %llu}",self,self.partNum,self.eTag,self.size,self.crc64];
}

#pragma marks - Protocol Methods
- (id)copyWithZone:(nullable NSZone *)zone
{
    ZYJOSSPartInfo *instance = [[[self class] allocWithZone:zone] init];
    instance.partNum = self.partNum;
    instance.eTag = self.eTag;
    instance.size = self.size;
    instance.crc64 = self.crc64;
    return instance;
}

@end

@implementation ZYJOSSCompleteMultipartUploadRequest
@end

@implementation ZYJOSSCompleteMultipartUploadResult
@end

@implementation ZYJOSSAbortMultipartUploadRequest
@end

@implementation ZYJOSSAbortMultipartUploadResult
@end

@implementation ZYJOSSListPartsRequest
@end

@implementation ZYJOSSListPartsResult
@end

@implementation ZYJOSSMultipartUploadRequest

- (instancetype)init {
    if (self = [super init]) {
        self.partSize = 256 * 1024;
    }
    return self;
}

- (void)cancel {
    [super cancel];
}

@end

@implementation ZYJOSSResumableUploadRequest

- (instancetype)init {
    if (self = [super init]) {
        self.deleteUploadIdOnCancelling = YES;
        self.partSize = 256 * 1024;
    }
    return self;
}

- (void)cancel {
    [super cancel];
    if(_runningChildrenRequest){
        [_runningChildrenRequest cancel];
    }
}

@end

@implementation ZYJOSSResumableUploadResult
@end

@implementation ZYJOSSCallBackRequest
@end

@implementation ZYJOSSCallBackResult
@end

@implementation ZYJOSSImagePersistRequest
@end

@implementation ZYJOSSImagePersistResult
@end
