//
//  HSNSURLSession.m
//  HSDownloadManagerExample
//
//  Created by hans on 15/8/4.
//  Copyright © 2015年 hans. All rights reserved.
//

#import "WSDownloadSession.h"

#define CunCount 8*1024
@interface WSDownloadSession()<NSURLSessionDelegate>
@property (nonatomic,copy)NSString *msgUUID;
/** 流 */
@property (nonatomic, strong) NSOutputStream *stream;

//下载任务
@property (nonatomic, strong) NSURLSessionDataTask *task;


@property (nonatomic , strong) NSMutableURLRequest *request;
/** 下载地址 */
@property (nonatomic, copy) NSString *url;

/**
 *  缓存根路径/A/
 */
@property (nonatomic , copy) NSString *cacheRootPath;

/**
 *  绝对路径(最后的两个绝对,用来拼接)xxj/之后的路径,(需要传出来,所以带上filename)
 */
@property (nonatomic , copy) NSString *localPath;

@property (nonatomic , copy) NSString *attachName;

@property (nonatomic , copy) NSString *fileMd5;

/**
 *  附件完整路径/A/xx.yy
 */
@property (nonatomic , copy) NSString *attachFullPath;
/**
 *  文件大小描述文件完全路径
 */
@property (nonatomic , copy) NSString *attachTotalLengthFileFullPath;

/**
 *  已下载长度
 */
@property (nonatomic , assign)NSInteger downloadedLength;

/** 获得服务器这次请求 返回数据的总长度 */
@property (nonatomic, assign) NSInteger totalLengthSaveFromServer;

//请求时文件模型告知的实际大小.
@property (nonatomic , assign) NSInteger totalLengthNeedDownload;

@property (nonatomic, assign)NSURLSession *session;

@end
//取消
static const NSUInteger SessionCancerCode = -999;
//超时
@implementation WSDownloadSession

+ (id)oneDownloadSessionWithFileUrlString:(NSString *)downloadUrl savePath:(NSString *)path localPath:(NSString *)localPath fileMd5:(NSString *)md5 UUID:(NSString *)uuid fileName:(NSString *)fileName downloadedSize:(NSInteger)downloadedSize fileSize:(NSInteger)fileSize progressblock:(DownloadProgressBlock)progressBlock successblock:(DownloadSuccessBlock)successblock failedBlock:(DownloadFailedBlock)failedBlock;
{
    WSDownloadSession *wsDowunloadSession = [[WSDownloadSession alloc]initOneDownloadSessionWithFileUrlString:downloadUrl savePath:path localPath:localPath fileMd5:md5 UUID:uuid fileName:fileName downloadedSize:downloadedSize fileSize:fileSize progressblock:progressBlock successblock:successblock failedBlock:failedBlock];
    
    return wsDowunloadSession;
    
}

//SAVE PATH: .../Library/xxj/cloudstorage
//localpath /cloudstorage/filename


//                   savepath/  localpath /filename
//...library/xxj/cloudStorage/dada78dafada/xxx.filename
- (instancetype)initOneDownloadSessionWithFileUrlString:(NSString *)downloadUrl savePath:(NSString *)savePath localPath:(NSString *)localPath fileMd5:(NSString *)md5 UUID:(NSString *)uuid fileName:(NSString *)fileName downloadedSize:(NSInteger)downloadedSize fileSize:(NSInteger)fileSize progressblock:(DownloadProgressBlock)progressBlock successblock:(DownloadSuccessBlock)successblock failedBlock:(DownloadFailedBlock)failedBlock
{
    if (self = [super init]) {
        
        self.msgUUID = uuid;
        self.fileMd5 = md5;
        self.url = downloadUrl;
        self.totalLengthNeedDownload = fileSize;
        /**
         *  实际效果不大?,还是读取实际的大小.
         */
        self.downloadedLength = downloadedSize;
        
        self.downloadProgressBlock = progressBlock;
        self.downloadSuccessBlock = successblock;
        self.downloadFailedBlock = failedBlock;
        self.attachName = fileName;
        
        //localpath即本地绝对localpath,localpath可以不同,name可以相同.
        self.localPath = localPath;//![localPath isEqualToString:fileName]?[NSString stringWithFormat:@"%@/%@",localPath,fileName]:fileName;//存入数据库中.
        //下载完注意删除
        self.attachTotalLengthFileFullPath = [savePath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@_%@_totalLength.plist",fileName,md5]];
        self.attachFullPath = [NSString stringWithFormat:@"%@/%@",savePath,self.localPath];
        self.cacheRootPath = self.attachFullPath.stringByDeletingLastPathComponent;
        
        [self createCacheDirectory];
        
        NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration] delegate:self delegateQueue:[[NSOperationQueue alloc]init]];
      
        //写入文件专用.
        NSOutputStream *stream = [NSOutputStream outputStreamToFileAtPath:self.attachFullPath append:YES];
        
        //中文字符的链接处理下
        // 创建请求
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[downloadUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];

        self.stream = stream;
        
        self.session = session;
        
        self.request = request;
        
        [self sessionWithNewRequest];
        
    }
    
    return self;

}

- (void)sessionWithNewRequest
{
    //中文字符的链接处理下
    // 创建请求
    NSInteger maxDownloadLength = self.downloadedLength + CunCount;
    
    if (maxDownloadLength > self.totalLengthNeedDownload) {
        maxDownloadLength = self.totalLengthNeedDownload;
    }
    // 设置请求头
    NSString *range = [NSString stringWithFormat:@"bytes=%ld-%ld",self.downloadedLength,maxDownloadLength];
    [self.request setValue:range forHTTPHeaderField:@"Range"];
  
    
    NSURLSessionDataTask *task = [self.session dataTaskWithRequest:self.request];
    
    [task resume];
    
    self.task = task;

}

/**
 *  创建缓存目录文件
 */
- (void)createCacheDirectory
{

    NSFileManager *fileManager = [NSFileManager defaultManager];
    if (![fileManager fileExistsAtPath:self.cacheRootPath]) {
        [fileManager createDirectoryAtPath:self.cacheRootPath withIntermediateDirectories:YES attributes:nil error:NULL];
    }
}


//实时获取文件大小
- (NSInteger)downloadedLength
{
    return [[[NSFileManager defaultManager] attributesOfItemAtPath:self.attachFullPath error:nil][NSFileSize] integerValue];
}

//挂起.
- (void)handle
{
  
    if (self.task.state == NSURLSessionTaskStateRunning) {
        [self pause];
    } else {
        [self start];
    }
}

/**
 *  开始下载
 */
- (void)start
{
 
    [self.task resume];

}

/**
 *  暂停下载-如果url相同的话就有问题.
 */
- (void)pause
{

    //可能超时,show error,直接cancer吧。
    
    if (!self.task) {
        return;
    }
    [self.task suspend];
   
    

    
}

- (void)cancer
{

    if (!self.task) {
        return;
    }
    


    [self.session finishTasksAndInvalidate];
    [self.session invalidateAndCancel];
    
    
    // 关闭流
    [self.stream close];
    self.stream.delegate = nil;
    
    // 清除任务
    [self.task cancel];
    
    self.task = nil;
    self.stream = nil;
    self.session = nil;
}

/**
 *  判断该文件是否下载完成
 */
- (BOOL)isCompletion
{
 
    if (self.downloadedLength == self.totalLengthNeedDownload ) {
        return YES;
    }
    return NO;
    
}

/**
 *  根据服务端返回的总大小生成的文件 获取该资源总大小
 */
- (NSInteger)fileTotalLength
{
    return [[NSDictionary dictionaryWithContentsOfFile:self.attachTotalLengthFileFullPath][self.attachName] integerValue];
}

#pragma mrak --
#pragma mark -- URLSessionDelegate

/**
 * 接收到响应
 */
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSHTTPURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
    // 打开流
    if (self.stream.streamStatus != NSStreamStatusOpen) {
        [self.stream open];
    }

    // 接收这个请求，允许接收服务器的数据
    completionHandler(NSURLSessionResponseAllow);

}

/**
 * 接收到服务器返回的数据
 */
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
        // 写入数据
    [self.stream write:data.bytes maxLength:data.length];
    
    self.downloadedLength += data.length;
    // 下载进度
    NSUInteger receivedSize = self.downloadedLength;
    NSUInteger expectedSize = self.totalLengthNeedDownload;
    CGFloat progress = 1.0 * receivedSize / expectedSize;
    
    if (self.downloadProgressBlock) {
        self.downloadProgressBlock(receivedSize, expectedSize, progress,self.attachFullPath,self.msgUUID);
    }
    
}

/**
* 请求完毕（成功|失败）
*/
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
    __weak typeof(self)weakself = self;
    
    task = nil;

    if ([self isCompletion]) {
        // 下载完成
        
        if (weakself.downloadSuccessBlock) {

            weakself.downloadSuccessBlock(weakself.downloadedLength,weakself.localPath,weakself.msgUUID);
        }
        
        [self closeSession];

    }else if (self.downloadedLength < self.totalLengthNeedDownload && !error)
    {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01*NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            
            if(weakself.session)[weakself sessionWithNewRequest];
        });
        
    }else if (error && error.code != SessionCancerCode){
        // 下载失败
        if (self.downloadFailedBlock) {
            self.downloadFailedBlock(@"error",self.msgUUID);
        }
        
        [self closeSession];

    }
    
    

}

- (void)closeSession
{
    [self.session finishTasksAndInvalidate];
    [self.session invalidateAndCancel];
    
    
    // 关闭流
    [self.stream close];
    self.stream.delegate = nil;
    
    // 清除任务
    [self.task cancel];
    
    self.request = nil;
    self.task = nil;
    self.stream = nil;
    self.session = nil;
}

- (void)dealloc
{
    
}

@end
