//
//  ZYJQNFormUpload.m
//  ZYJQiniuSDK
//
//  Created by bailong on 15/1/4.
//  Copyright (c) 2015年 Qiniu. All rights reserved.
//

#import "ZYJQNFormUpload.h"

@interface ZYJQNFormUpload ()

@property (nonatomic, strong) NSData *data;
@property (nonatomic) int retryTimes;
@property (nonatomic, strong) NSString *fileName;
@property (nonatomic) float previousPercent;

@end

@implementation ZYJQNFormUpload

- (instancetype)initWithData:(NSData *)data
                     withKey:(NSString *)key
                withFileName:(NSString *)fileName
                   withToken:(ZYJQNUpToken *)token
              withIdentifier:(NSString *)identifier
       withCompletionHandler:(ZYJQNUpCompletionHandler)block
                  withOption:(ZYJQNUploadOption *)option
            withSessionManager:(ZYJQNSessionManager *)sessionManager
           withConfiguration:(ZYJQNConfiguration *)config {
    if (self = [super init]) {
        self.data = data;
        self.size = (UInt32)data.length;
        self.key = key;
        self.token = token;
        self.option = option != nil ? option : [ZYJQNUploadOption defaultOptions];
        self.complete = block;
        self.sessionManager = sessionManager;
        self.config = config;
        self.fileName = fileName != nil ? fileName : @"?";
        self.previousPercent = 0;
        self.access = token.access;
        self.currentZoneType = ZYJQNZoneInfoTypeMain;
        self.identifier = identifier;
        self.requestType = ZYJQNRequestType_form;
    }
    return self;
}

- (void)put {
    
    NSMutableDictionary *parameters = [NSMutableDictionary dictionary];
    if (self.key) {
        parameters[@"key"] = self.key;
    }
    parameters[@"token"] = self.token.token;
    [parameters addEntriesFromDictionary:self.option.params];
    parameters[@"crc32"] = [NSString stringWithFormat:@"%u", (unsigned int)[ZYJQNCrc32 data:_data]];
    
    [self nextTask:0 needDelay:NO host:[self.config.zone up:self.token zoneInfoType:self.currentZoneType isHttps:self.config.useHttps frozenDomain:nil] param:parameters];
}

- (void)nextTask:(int)retried needDelay:(BOOL)needDelay host:(NSString *)host param:(NSDictionary *)param {
    
    if (needDelay) {
        ZYJQNAsyncRunAfter(self.config.retryInterval, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            [self nextTask:retried host:host param:param];
        });
    } else {
        [self nextTask:retried host:host param:param];
    }
}

- (void)nextTask:(int)retried host:(NSString *)host param:(NSDictionary *)param {
        
    if (self.option.cancellationSignal()) {
        [self collectUploadQualityInfo];
        ZYJQNResponseInfo *info = [Collector userCancel:self.identifier];
        self.complete(info, self.key, nil);
        return;
    }
        
    ZYJQNInternalProgressBlock p = ^(long long totalBytesWritten, long long totalBytesExpectedToWrite) {
        float percent = (float)totalBytesWritten / (float)totalBytesExpectedToWrite;
        if (percent > 0.95) {
            percent = 0.95;
        }
        if (percent > self.previousPercent) {
            self.previousPercent = percent;
        } else {
            percent = self.previousPercent;
        }
        self.option.progressHandler(self.key, percent);
    };
    ZYJQNCompleteBlock complete = ^(ZYJQNHttpResponseInfo *httpResponseInfo, NSDictionary *respBody) {
        [self collectHttpResponseInfo:httpResponseInfo fileOffset:ZYJQN_IntNotSet];
        
        if (httpResponseInfo.isOK) {
            self.option.progressHandler(self.key, 1.0);
            [self collectUploadQualityInfo];
            ZYJQNResponseInfo *info = [Collector completeWithHttpResponseInfo:httpResponseInfo identifier:self.identifier];
            self.complete(info, self.key, respBody);
        } else if (httpResponseInfo.couldRetry) {
            if (retried < self.config.retryMax) {
                [self nextTask:retried + 1 needDelay:YES host:host param:param];
            } else {
                if (self.config.allowBackupHost) {
                    NSString *nextHost = [self.config.zone up:self.token zoneInfoType:self.currentZoneType isHttps:self.config.useHttps frozenDomain:host];
                    if (nextHost) {
                        [self nextTask:0 needDelay:YES host:nextHost param:param];
                    } else {
                        ZYJQNZonesInfo *zonesInfo = [self.config.zone getZonesInfoWithToken:self.token];
                        if (self.currentZoneType == ZYJQNZoneInfoTypeMain && zonesInfo.hasBackupZone) {
                            self.currentZoneType = ZYJQNZoneInfoTypeBackup;
                            [self nextTask:0 needDelay:YES host:[self.config.zone up:self.token zoneInfoType:self.currentZoneType isHttps:self.config.useHttps frozenDomain:nil] param:param];
                        } else {
                            [self collectUploadQualityInfo];
                            ZYJQNResponseInfo *info = [Collector completeWithHttpResponseInfo:httpResponseInfo identifier:self.identifier];
                            self.complete(info, self.key, respBody);
                        }
                    }
                } else {
                    [self collectUploadQualityInfo];
                    ZYJQNResponseInfo *info = [Collector completeWithHttpResponseInfo:httpResponseInfo identifier:self.identifier];
                    self.complete(info, self.key, respBody);
                }
            }
        } else {
            [self collectUploadQualityInfo];
            ZYJQNResponseInfo *info = [Collector completeWithHttpResponseInfo:httpResponseInfo identifier:self.identifier];
            self.complete(info, self.key, respBody);
        }        
    };
    [self.sessionManager multipartPost:host
                              withData:self.data
                     withParams:param
                          withFileName:self.fileName
                          withMimeType:self.option.mimeType
                        withIdentifier:self.identifier
              withCompleteBlock:complete
              withProgressBlock:p
                       withCancelBlock:self.option.cancellationSignal
                            withAccess:self.access];
}

@end
