//
//  Operation.m
//  tuber
//
//  Created by ドラッサル 亜嵐 on 2016/06/09.
//  Copyright © 2016年 ドラッサル 亜嵐. All rights reserved.
//

#import "Operation.h"
#import "AFNetworking.h"
#import "DBManager.h"
#import "XCDYouTubeKit/XCDYouTubeKit.h"
#import "OperationDownload.h"

@implementation Operation

- (void)startup {
    NSLog(@"[%@] Operation startup", NSStringFromClass([self class]));
    
    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
    [nc addObserver:self selector:@selector(videoAvailableRequest:) name:@"videoAvailableRequestNotification" object:nil];
    [nc addObserver:self selector:@selector(videoDownloadInfoRequest:) name:@"videoDownloadInfoRequestNotification" object:nil];
    [nc addObserver:self selector:@selector(videoDownloadThumbRequest:) name:@"videoDownloadThumbRequestNotification" object:nil];
    [nc addObserver:self selector:@selector(videoDownloadDataRequest:) name:@"videoDownloadDataRequestNotification" object:nil];
    [nc addObserver:self selector:@selector(videoDownloadDataBackgroundRequest:) name:@"videoDownloadDataBackgroundRequestNotification" object:nil];
    
    //[[OperationDownload sharedInstance] dummy];
}

- (void)search:(NSString*)query
       success:(void(^)(id JSON))successHandler
       failure:(void(^)(NSError *error, id JSON))failureHandler {
    NSMutableDictionary *params = [NSMutableDictionary dictionary];
    
    NSString * urlString = [NSString stringWithFormat:URL_STRING_SEARCH,[query stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet alphanumericCharacterSet]]];
    
    [params setObject:urlString forKey:@"url"];
    
    params = [self buildParams:params needAuth:NO];
    [self startOperationWithParams:params
                              path:@"api"
                            method:@"GET"
                           success:^(id JSON) {
                               if (successHandler) {
                                   successHandler(JSON);
                               }
                           }
                           failure:^(NSError *error, id JSON) {
                               //NSLog(@"ERROR %@", error);
                               if (failureHandler) {
                                   failureHandler(error, JSON);
                               }
                           }];

    /*
    NSMutableDictionary *params = [NSMutableDictionary dictionary];
    
    [params setObject:YOUTUBE_KEY forKey:@"key"];
    [params setObject:query forKey:@"q"];
    [params setObject:@"id,snippet" forKey:@"part"];
    [params setObject:@"JP" forKey:@"regionCode"];
    [params setObject:@"ja-JP" forKey:@"hl"];
    [params setObject:@"50" forKey:@"maxResults"];
    [params setObject:@"video" forKey:@"type"];
    
    params = [self buildParams:params needAuth:NO];
    [self startCustomOperationWithParams:params
                                 reqPath:@"https://www.googleapis.com/%@"
                                    path:@"youtube/v3/search"
                                  method:@"GET"
                                 success:^(id JSON) {
                                     if (successHandler) {
                                         successHandler(JSON);
                                     }
                                 }
                                 failure:^(NSError *error, id JSON) {
                                     //NSLog(@"ERROR %@", error);
                                     if (failureHandler) {
                                         failureHandler(error, JSON);
                                     }
                                 }];
    */
    
    
    
    //NSMutableDictionary *params = [self buildParams:@{COID_KEY:coId, MEMBERID_KEY:memberId, CHANGEDEVICE_SECURITY_KEY:key} needAuth:YES];
    //NSMutableDictionary *params = [NSMutableDictionary dictionary];

    
    //[params setObject:urlString forKey:@"url"];
    /*
    [params setObject:@"AIzaSyAGL0g4CGOR4k5ZDTx9UvNlJPnP2cg8YIY" forKey:@"key"];
    [params setObject:@"video" forKey:@"type"];
    [params setObject:@"relevance" forKey:@"order"];
    [params setObject:@"akb48" forKey:@"q"];
    [params setObject:@"any" forKey:@"videoDuration"];
    [params setObject:@"snippet" forKey:@"part"];
    [params setObject:@"JP" forKey:@"regionCode"];
    [params setObject:@"ja" forKey:@"relevanceLanguage"];
    [params setObject:@"50" forKey:@"maxResults"];
    */
    /*
    params = [self buildParams:params needAuth:NO];
    [self startOperationWithParams:params
                              path:@"api"
                            method:@"GET"
                           success:^(id JSON) {
                               if (successHandler) {
                                   successHandler(JSON);
                               }
                           }
                           failure:^(NSError *error, id JSON) {
                               //NSLog(@"ERROR %@", error);
                               if (failureHandler) {
                                   failureHandler(error, JSON);
                               }
                           }];
     */
}

/*
- (void)search:(NSString*)query
           success:(void(^)(id JSON))successHandler
           failure:(void(^)(NSError *error, id JSON))failureHandler {
    //NSMutableDictionary *params = [self buildParams:@{COID_KEY:coId, MEMBERID_KEY:memberId, CHANGEDEVICE_SECURITY_KEY:key} needAuth:YES];
    NSMutableDictionary *params = [NSMutableDictionary dictionary];

    //NSString * urlString = [NSString stringWithFormat:URL_STRING_SEARCH,query];
    
    [params setObject:query forKey:@"query"];
    [params setObject:@"(all type:/music/artist created:\"The Lady Killer\")" forKey:@"filter"];
    [params setObject:@"10" forKey:@"limit"];
    [params setObject:@"true" forKey:@"indent"];

    //[params setObject:@"AIzaSyAGL0g4CGOR4k5ZDTx9UvNlJPnP2cg8YIY" forKey:@"key"];
    //[params setObject:@"video" forKey:@"type"];
    //[params setObject:@"relevance" forKey:@"order"];
    //[params setObject:@"akb48" forKey:@"q"];
    //[params setObject:@"any" forKey:@"videoDuration"];
    //[params setObject:@"snippet" forKey:@"part"];
    //[params setObject:@"JP" forKey:@"regionCode"];
    //[params setObject:@"ja" forKey:@"relevanceLanguage"];
    //[params setObject:@"50" forKey:@"maxResults"];

    params = [self buildParams:params needAuth:NO];
        
    [self startOperationWithParams:params
                              path:@"search?key=AIzaSyB-63vPrdThhKuerbB2N_l7Kwwcxj6yUAc"
                            method:@"POST"
                           success:^(id JSON) {
                               if (successHandler) {
                                   successHandler(JSON);
                               }
                           }
                           failure:^(NSError *error, id JSON) {
                               //NSLog(@"ERROR %@", error);
                               if (failureHandler) {
                                   failureHandler(error, JSON);
                               }
                           }];
}
*/

- (void)details:(NSArray*)listDetail
       success:(void(^)(id JSON))successHandler
       failure:(void(^)(NSError *error, id JSON))failureHandler {

    NSString * detailString = [[NSString alloc] init];
    for(id key in listDetail) {
        if([detailString isEqualToString: @""]) {
            detailString = [NSString stringWithFormat:@"%@",key];
        } else {
            detailString = [NSString stringWithFormat:@"%@,%@",detailString,key];
        }
    }
    
    NSMutableDictionary *params = [NSMutableDictionary dictionary];
    
    NSString * urlString = [NSString stringWithFormat:URL_STRING_DETAILS,[detailString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet alphanumericCharacterSet]]];
    
    [params setObject:urlString forKey:@"url"];

    params = [self buildParams:params needAuth:NO];
    [self startOperationWithParams:params
                              path:@"api"
                            method:@"GET"
                           success:^(id JSON) {
                               if (successHandler) {
                                   successHandler(JSON);
                               }
                           }
                           failure:^(NSError *error, id JSON) {
                               //NSLog(@"ERROR %@", error);
                               if (failureHandler) {
                                   failureHandler(error, JSON);
                               }
                           }];
}

- (void)getVideoInfo:(NSString*)videoId
         success:(void(^)(id JSON))successHandler
         failure:(void(^)(NSError *error, id JSON))failureHandler {
    
    NSMutableDictionary *params = [NSMutableDictionary dictionary];
    
    NSString * urlString = [NSString stringWithFormat:URL_STRING_DETAILS,[videoId stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet alphanumericCharacterSet]]];
    
    [params setObject:urlString forKey:@"url"];
    
    params = [self buildParams:params needAuth:NO];
    [self startOperationWithParams:params
                              path:@"api"
                            method:@"GET"
                           success:^(id JSON) {
                               if (successHandler) {
                                   successHandler(JSON);
                               }
                           }
                           failure:^(NSError *error, id JSON) {
                               //NSLog(@"ERROR %@", error);
                               if (failureHandler) {
                                   failureHandler(error, JSON);
                               }
                           }];
    /*
    NSMutableDictionary *params = [NSMutableDictionary dictionary];
    
    [params setObject:YOUTUBE_KEY forKey:@"key"];
    [params setObject:videoId forKey:@"id"];
    [params setObject:@"id,snippet,contentDetails" forKey:@"part"];
    
    params = [self buildParams:params needAuth:NO];
    [self startCustomOperationWithParams:params
                                 reqPath:@"https://www.googleapis.com/%@"
                                    path:@"youtube/v3/videos"
                                  method:@"GET"
                                 success:^(id JSON) {
                                     if (successHandler) {
                                         successHandler(JSON);
                                     }
                                 }
                                 failure:^(NSError *error, id JSON) {
                                     //NSLog(@"ERROR %@", error);
                                     if (failureHandler) {
                                         failureHandler(error, JSON);
                                     }
                                 }];
     */
}

- (void)getImage:(NSString*)urlString
       success:(void(^)(id JSON))successHandler
       failure:(void(^)(NSError *error, id JSON))failureHandler {

    NSMutableDictionary *params = [NSMutableDictionary dictionary];
    
    params = [self buildParams:params needAuth:NO];
    [self startImageOperationWithParams:params
                                    path:urlString
                                  method:@"GET"
                                 success:^(id JSON) {
                                     if (successHandler) {
                                         successHandler(JSON);
                                     }
                                 }
                                 failure:^(NSError *error, id JSON) {
                                     //NSLog(@"ERROR %@", error);
                                     if (failureHandler) {
                                         failureHandler(error, JSON);
                                     }
                                 }];
}

- (void)getRaw:(NSString*)urlString
         success:(void(^)(id JSON))successHandler
         failure:(void(^)(NSError *error, id JSON))failureHandler {
    
    NSMutableDictionary *params = [NSMutableDictionary dictionary];
    
    params = [self buildParams:params needAuth:NO];
    [self startRawOperationWithParams:params
                                   path:urlString
                                 method:@"GET"
                                success:^(id JSON) {
                                    if (successHandler) {
                                        successHandler(JSON);
                                    }
                                }
                                failure:^(NSError *error, id JSON) {
                                    //NSLog(@"ERROR %@", error);
                                    if (failureHandler) {
                                        failureHandler(error, JSON);
                                    }
                                }];
}

- (void)streamToFile:(NSString*)urlString
          saveToPath:(NSString*)saveToPath
       progressBlock:(void(^)(double fractionCompleted))progressBlock
             success:(void(^)(NSString *savedTo))successHandler
             failure:(void(^)(NSError *error))failureHandler {
    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
    
    NSURL *URL = [NSURL URLWithString:urlString];
    NSURLRequest *request = [NSURLRequest requestWithURL:URL];
    
    NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request
                                                                     progress:^(NSProgress * _Nonnull downloadProgress) {
                                                                          //NSLog(@"Progress: %f", downloadProgress.fractionCompleted);
                                                                           dispatch_async(dispatch_get_main_queue(), ^{
                                                                               progressBlock(downloadProgress.fractionCompleted);
                                                                           });
                                                                      }
                                                                  destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
        NSError *error;
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *documentsDirectory = [paths objectAtIndex:0]; // Get documents folder
        NSString *saveToPathFull = [documentsDirectory stringByAppendingPathComponent:saveToPath];
        NSString *saveToFolder = [saveToPathFull stringByDeletingLastPathComponent];
        if (![[NSFileManager defaultManager] fileExistsAtPath:saveToFolder])
        {
            [[NSFileManager defaultManager] createDirectoryAtPath:saveToFolder withIntermediateDirectories:NO attributes:nil error:&error];
        }

        NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
        //return [documentsDirectoryURL URLByAppendingPathComponent:[response suggestedFilename]];
        return [documentsDirectoryURL URLByAppendingPathComponent:saveToPath];
    } completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
        if(!error) {
            //NSLog(@"File downloaded to: %@", filePath);
            successHandler([NSString stringWithFormat:@"%@",filePath]);
        } else {
            failureHandler(error);
        }
    }];
    [downloadTask resume];
}

- (UIImage*)getImageFromFile:(NSString*)filename {
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
                                                         NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString* path = [documentsDirectory stringByAppendingPathComponent:
                      [NSString stringWithFormat: @"/save/%@",filename]];
    UIImage* image = [UIImage imageWithContentsOfFile:path];
    return image;
}

#pragma mark - Private Methods

- (void)startOperationWithParams:(NSDictionary *)aParams
                            path:(NSString *)pathString
                          method:(NSString *)method
                         success:(void(^)(id JSON))successHandler
                         failure:(void(^)(NSError *error, id JSON))failureHandler {
    [self startOperationWithParams:aParams reqPath:URL_REQUEST_PATH path:pathString method:method success:successHandler failure:failureHandler];
}

- (void)startOperationWithParams:(NSDictionary *)aParams
                         reqPath:(NSString *)reqPathStr
                            path:(NSString *)pathString
                          method:(NSString *)method
                         success:(void(^)(id JSON))successHandler
                         failure:(void(^)(NSError *error, id JSON))failureHandler {
    
    // form-urlencoded request
    NSString *path = [NSString stringWithFormat:reqPathStr, pathString];
    NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@",BASE_URL,path]];

    // set self.manager only if it hasn't been created yet
    if(!self.manager)
    {
        self.manager = [AFHTTPSessionManager manager]; // 71%
        //self.manager.requestSerializer = [AFJSONRequestSerializer serializer];
        self.manager.responseSerializer = [AFJSONResponseSerializer serializer]; // 9.7%
        self.manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"application/x-www-form-urlencoded", nil];

    }
    /*
     AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
     [manager setResponseSerializer:[AFHTTPResponseSerializer serializer]];
     */
    
    NSMutableDictionary *params = [self buildParams:aParams needAuth:NO];
    /*
     NSMutableURLRequest *request = [httpClient requestWithMethod:method
     path:path
     parameters:params];
     [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"content-type"];
     */
    
    if([method isEqualToString:@"GET"]) {
        self.manager.responseSerializer = [AFHTTPResponseSerializer serializer];
        [self.manager GET:URL.absoluteString parameters:params progress:nil success:^(NSURLSessionTask *task, id responseObject) {
            if([responseObject isKindOfClass:[NSDictionary class]]) {
                if(successHandler) {
                    successHandler(responseObject);
                }
            }
            else {
                NSDictionary *response = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingAllowFragments error:nil];
                NSLog(@"[Operation] => resultObject: %@", response);
                if(successHandler) {
                    successHandler(response);
                }
            }
        } failure:^(NSURLSessionTask *operation, NSError *error) {
            //NSLog(@"[Operation] => resultObject: %@", responseObject);
            NSLog(@"[Operation] => error: %@", error);
            if (failureHandler) {
                //failureHandler(error, responseObject);
                failureHandler(error, nil);
            }
        }];
    } else if([method isEqualToString:@"POST"]) {
        [self.manager POST:URL.absoluteString parameters:params progress:nil success:^(NSURLSessionTask *task, id responseObject) {
            if([responseObject isKindOfClass:[NSDictionary class]]) {
                if(successHandler) {
                    successHandler(responseObject);
                }
            }
            else {
                NSLog(@"[Operation] => resultObject: %@", responseObject);
                NSDictionary *response = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingAllowFragments error:nil];
                if(successHandler) {
                    successHandler(response);
                }
            }
        } failure:^(NSURLSessionTask *operation, NSError *error) {
            //NSLog(@"[Operation] => resultObject: %@", responseObject);
            NSLog(@"[Operation] => error: %@", error);
            if (failureHandler) {
                //failureHandler(error, responseObject);
                failureHandler(error, nil);
            }
        }];
    }
    
    //NSLog(@"[Operation] => startResuest: %@", [[request URL] absoluteString]);
    NSLog(@"[Operation] => params: %@", params);
}

- (void)startCustomOperationWithParams:(NSDictionary *)aParams
                                  path:(NSString *)pathString
                                method:(NSString *)method
                               success:(void(^)(id JSON))successHandler
                               failure:(void(^)(NSError *error, id JSON))failureHandler {
    [self startCustomOperationWithParams:aParams reqPath:@"%@" path:pathString method:method success:successHandler failure:failureHandler];
}

- (void)startCustomOperationWithParams:(NSDictionary *)aParams
                         reqPath:(NSString *)reqPathStr
                            path:(NSString *)pathString
                          method:(NSString *)method
                         success:(void(^)(id JSON))successHandler
                         failure:(void(^)(NSError *error, id JSON))failureHandler {
    
    // form-urlencoded request
    NSString *path = [NSString stringWithFormat:reqPathStr, pathString];
    NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"%@",path]];
    
    // set self.manager only if it hasn't been created yet
    if(!self.managerCustom)
    {
        self.managerCustom = [AFHTTPSessionManager manager]; // 71%
        //self.manager.requestSerializer = [AFJSONRequestSerializer serializer];
        self.managerCustom.responseSerializer = [AFHTTPResponseSerializer serializer]; // 9.7%
        self.managerCustom.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"application/x-www-form-urlencoded", nil];
        
    }
    /*
     AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
     [manager setResponseSerializer:[AFHTTPResponseSerializer serializer]];
     */
    
    NSMutableDictionary *params = [self buildParams:aParams needAuth:NO];
    /*
     NSMutableURLRequest *request = [httpClient requestWithMethod:method
     path:path
     parameters:params];
     [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"content-type"];
     */
    
    if([method isEqualToString:@"GET"]) {
        [self.managerCustom GET:URL.absoluteString parameters:params progress:nil success:^(NSURLSessionTask *task, id responseObject) {
            if([responseObject isKindOfClass:[NSDictionary class]]) {
                if(successHandler) {
                    successHandler(responseObject);
                }
            }
            else {
                NSDictionary *response = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingAllowFragments error:nil];
                NSLog(@"[Operation] => resultObject: %@", response);
                if(!response) {
                    NSMutableDictionary *queryStringDictionary = [[NSMutableDictionary alloc] init];
                    NSString * responseString = [[NSString alloc] initWithBytes:(char *)[responseObject bytes] length:[responseObject length] encoding:NSUTF8StringEncoding];
                    NSArray *urlComponents = [responseString componentsSeparatedByString:@"&"];

                    for (NSString *keyValuePair in urlComponents)
                    {
                        NSArray *pairComponents = [keyValuePair componentsSeparatedByString:@"="];
                        NSString *key = [[pairComponents firstObject] stringByRemovingPercentEncoding];
                        NSString *value = [[pairComponents lastObject] stringByRemovingPercentEncoding];
                        
                        value = [value stringByReplacingOccurrencesOfString:@"+" withString:@" "];
                        value = [value stringByRemovingPercentEncoding];

                        [queryStringDictionary setObject:value forKey:key];
                    }
                    response = queryStringDictionary;
                }
                if(successHandler) {
                    successHandler(response);
                }
            }
        } failure:^(NSURLSessionTask *operation, NSError *error) {
            //NSLog(@"[Operation] => resultObject: %@", responseObject);
            NSLog(@"[Operation] => error: %@", error);
            if (failureHandler) {
                //failureHandler(error, responseObject);
                failureHandler(error, nil);
            }
        }];
    } else if([method isEqualToString:@"POST"]) {
        [self.managerCustom POST:URL.absoluteString parameters:params progress:nil success:^(NSURLSessionTask *task, id responseObject) {
            if([responseObject isKindOfClass:[NSDictionary class]]) {
                if(successHandler) {
                    successHandler(responseObject);
                }
            }
            else {
                NSLog(@"[Operation] => resultObject: %@", responseObject);
                NSDictionary *response = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingAllowFragments error:nil];
                if(successHandler) {
                    successHandler(response);
                }
            }
        } failure:^(NSURLSessionTask *operation, NSError *error) {
            //NSLog(@"[Operation] => resultObject: %@", responseObject);
            NSLog(@"[Operation] => error: %@", error);
            if (failureHandler) {
                //failureHandler(error, responseObject);
                failureHandler(error, nil);
            }
        }];
    }
    
    //NSLog(@"[Operation] => startResuest: %@", [[request URL] absoluteString]);
    NSLog(@"[Operation] => params: %@", params);
}

- (void)startImageOperationWithParams:(NSDictionary *)aParams
                                  path:(NSString *)pathString
                                method:(NSString *)method
                               success:(void(^)(id JSON))successHandler
                               failure:(void(^)(NSError *error, id JSON))failureHandler {
    [self startImageOperationWithParams:aParams reqPath:@"%@" path:pathString method:method success:successHandler failure:failureHandler];
}

- (void)startImageOperationWithParams:(NSDictionary *)aParams
                               reqPath:(NSString *)reqPathStr
                                  path:(NSString *)pathString
                                method:(NSString *)method
                               success:(void(^)(id JSON))successHandler
                               failure:(void(^)(NSError *error, id JSON))failureHandler {
   
    // form-urlencoded request
    NSString *path = [NSString stringWithFormat:reqPathStr, pathString];
    NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"%@",path]];
    
    // set self.manager only if it hasn't been created yet
    if(!self.managerImage)
    {
        self.managerImage = [AFHTTPSessionManager manager]; // 71%
        //self.managerImage.requestSerializer = [AFJSONRequestSerializer serializer];
        self.managerImage.responseSerializer = [AFImageResponseSerializer serializer]; // 9.7%
    }
    /*
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    [manager setResponseSerializer:[AFHTTPResponseSerializer serializer]];
     */
    
    NSMutableDictionary *params = [self buildParams:aParams needAuth:NO];
    /*
     NSMutableURLRequest *request = [httpClient requestWithMethod:method
     path:path
     parameters:params];
     [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"content-type"];
     */
    
    if([method isEqualToString:@"GET"]) {
        [self.managerImage GET:URL.absoluteString parameters:params progress:nil success:^(NSURLSessionTask *task, id responseObject) {
            NSLog(@"[Operation] => resultObject: %@", responseObject);
            if(successHandler) {
                successHandler(responseObject);
            }
        } failure:^(NSURLSessionTask *operation, NSError *error) {
            //NSLog(@"[Operation] => resultObject: %@", responseObject);
            NSLog(@"[Operation] => error: %@", error);
            if (failureHandler) {
                //failureHandler(error, responseObject);
                failureHandler(error, nil);
            }
        }];
    } else if([method isEqualToString:@"POST"]) {
        [self.managerImage POST:URL.absoluteString parameters:params progress:nil success:^(NSURLSessionTask *task, id responseObject) {
            NSLog(@"[Operation] => resultObject: %@", responseObject);
            if(successHandler) {
                successHandler(responseObject);
            }
        } failure:^(NSURLSessionTask *operation, NSError *error) {
            //NSLog(@"[Operation] => resultObject: %@", responseObject);
            NSLog(@"[Operation] => error: %@", error);
            if (failureHandler) {
                //failureHandler(error, responseObject);
                failureHandler(error, nil);
            }
        }];
    }
    
    //NSLog(@"[Operation] => startResuest: %@", [[request URL] absoluteString]);
    NSLog(@"[Operation] => params: %@", params);
}

- (void)startRawOperationWithParams:(NSDictionary *)aParams
                                 path:(NSString *)pathString
                               method:(NSString *)method
                              success:(void(^)(id JSON))successHandler
                              failure:(void(^)(NSError *error, id JSON))failureHandler {
    [self startRawOperationWithParams:aParams reqPath:@"%@" path:pathString method:method success:successHandler failure:failureHandler];
}

- (void)startRawOperationWithParams:(NSDictionary *)aParams
                              reqPath:(NSString *)reqPathStr
                                 path:(NSString *)pathString
                               method:(NSString *)method
                              success:(void(^)(id JSON))successHandler
                              failure:(void(^)(NSError *error, id JSON))failureHandler {
    
    // form-urlencoded request
    NSString *path = [NSString stringWithFormat:reqPathStr, pathString];
    NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"%@",path]];
    
    // set self.manager only if it hasn't been created yet
    if(!self.managerRaw)
    {
        self.managerRaw = [AFHTTPSessionManager manager]; // 71%
        //self.managerRaw.responseSerializer = [AFJSONRequestSerializer serializer];
        //self.managerRaw.responseSerializer = [AFImageResponseSerializer serializer]; // 9.7%
        self.managerRaw.responseSerializer = [AFHTTPResponseSerializer serializer]; // 9.7%
    }
    /*
     AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
     [manager setResponseSerializer:[AFHTTPResponseSerializer serializer]];
     */
    
    NSMutableDictionary *params = [self buildParams:aParams needAuth:NO];
    /*
     NSMutableURLRequest *request = [httpClient requestWithMethod:method
     path:path
     parameters:params];
     [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"content-type"];
     */
    
    if([method isEqualToString:@"GET"]) {
        [self.managerRaw GET:URL.absoluteString parameters:params progress:nil success:^(NSURLSessionTask *task, id responseObject) {
            //NSLog(@"[Operation] => resultObject: %@", responseObject);
            if(successHandler) {
                successHandler(responseObject);
            }
        } failure:^(NSURLSessionTask *operation, NSError *error) {
            //NSLog(@"[Operation] => resultObject: %@", responseObject);
            NSLog(@"[Operation] => error: %@", error);
            if (failureHandler) {
                //failureHandler(error, responseObject);
                failureHandler(error, nil);
            }
        }];
    } else if([method isEqualToString:@"POST"]) {
        [self.managerRaw POST:URL.absoluteString parameters:params progress:nil success:^(NSURLSessionTask *task, id responseObject) {
            //NSLog(@"[Operation] => resultObject: %@", responseObject);
            if(successHandler) {
                successHandler(responseObject);
            }
        } failure:^(NSURLSessionTask *operation, NSError *error) {
            //NSLog(@"[Operation] => resultObject: %@", responseObject);
            NSLog(@"[Operation] => error: %@", error);
            if (failureHandler) {
                //failureHandler(error, responseObject);
                failureHandler(error, nil);
            }
        }];
    }
    
    //NSLog(@"[Operation] => startResuest: %@", [[request URL] absoluteString]);
    NSLog(@"[Operation] => params: %@", params);
}

#pragma mark - Notifications

- (void)videoAvailableRequest:(NSNotification *)notification
{
    NSString *selectedVideoId = [notification userInfo][@"videoId"];
    NSString *optionGetInfo = [notification userInfo][@"optionGetInfo"];
    NSString *optionGetVideo = [notification userInfo][@"optionGetVideo"];
    YouTubeVideoQuality selectedVideoQuality = [[notification userInfo][@"videoQuality"] integerValue];
    NSLog(@"[%@] videoAvailableRequest selectedVideoId = %@ selectedQuality = %lu",
          NSStringFromClass([self class]),
          selectedVideoId,
          (unsigned long)selectedVideoQuality);
    //video quality we will check for
    
    NSMutableArray *preferedVideoQuality = [[NSMutableArray alloc] init];
    
    if(selectedVideoQuality == YouTubeVideoQualitySmall240)
        [preferedVideoQuality addObject:@(XCDYouTubeVideoQualitySmall240)];
    if(selectedVideoQuality == YouTubeVideoQualityMedium360)
        [preferedVideoQuality addObject:@(XCDYouTubeVideoQualityMedium360)];
    if(selectedVideoQuality == YouTubeVideoQualityHD720)
        [preferedVideoQuality addObject:@(XCDYouTubeVideoQualityHD720)];

    /*
    NSArray *preferredVideoQualities = @[
                                         @(XCDYouTubeVideoQualityHD720),
                                         @(XCDYouTubeVideoQualityMedium360),
                                         @(XCDYouTubeVideoQualitySmall240)
                                         ];
    */
    id<XCDYouTubeOperation> videoOperation;
    videoOperation = [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:selectedVideoId completionHandler:^(XCDYouTubeVideo *video, NSError *error)
                      {
                          if (video)
                          {
                              for (NSNumber *videoQuality in preferedVideoQuality)
                              {
                                  NSURL *videoUrl = video.streamURLs[videoQuality];
                                  if(videoUrl != nil) {
                                      NSString *videoUrlString = [NSString stringWithFormat:@"%@", videoUrl];
                                      NSString *videoItag = [NSString stringWithFormat:@"%@",videoQuality];
                                      
                                      NSLog(@"[%@] videoAvailableRequest selectedVideoId = %@ selectedQuality = %lu found videoUrl = %@!",
                                            NSStringFromClass([self class]),
                                            selectedVideoId,
                                            (unsigned long)selectedVideoQuality,
                                            videoUrlString);
                                      
                                      //if this quality is available, continue
                                      //register video information in database
                                      
                                      [[DBManager sharedInstance] saveVideoIdWithDataUrl:selectedVideoId
                                                                               videoItag:videoItag
                                                                        videoFilenameUrl:videoUrlString];
                                      
                                      //get the recordId for this new video
                                      NSString *newRecordId = [[DBManager sharedInstance] getRecordIdByVideoId:selectedVideoId
                                                                                                     videoItag:videoItag];
                                      if(newRecordId != nil) {
                                          NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
                                          [nc postNotificationName:@"didUpdateVideoDataNotification" object:nil];
                                          
                                          //trigger video info download
                                          if([optionGetInfo isEqualToString:@"true"]) {
                                              NSDictionary *userInfo = @{@"recordId" : newRecordId,
                                                                         @"videoId" : selectedVideoId,
                                                                         @"videoItag" : videoItag,
                                                                         @"optionGetThumb" : @"true"};
                                              [nc postNotificationName:@"videoDownloadInfoRequestNotification" object:self userInfo:userInfo];
                                          }

                                          //we know the url so trigger video data download
                                          if([optionGetVideo isEqualToString:@"true"]) {
                                              //we know the url so trigger video data download
                                              NSDictionary *userInfo2 = @{@"recordId" : newRecordId};
                                              if(self.downloadInBackgroundFlg == true) {
                                                  //download in background
                                                  [nc postNotificationName:@"videoDownloadDataBackgroundRequestNotification" object:self userInfo:userInfo2];
                                              } else {
                                                  [nc postNotificationName:@"videoDownloadDataRequestNotification" object:self userInfo:userInfo2];
                                              }
                                          }
                                      } else {
                                          NSLog(@"[%@] videoAvailableRequest selectedVideoId = %@ selectedQuality = %lu Can't retrieve registered recordId, can't continue!",
                                                NSStringFromClass([self class]),
                                                selectedVideoId,
                                                (unsigned long)selectedVideoQuality);
                                      }
                                  } else {
                                      NSLog(@"[%@] videoAvailableRequest selectedVideoId = %@ selectedQuality = %lu videoUrl is nil! selected quality doesn't exist!",
                                            NSStringFromClass([self class]),
                                            selectedVideoId,
                                            (unsigned long)selectedVideoQuality);
                                      dispatch_async(dispatch_get_main_queue(), ^{
                                          UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"エラー" message:@"選択したサイズがありません。" delegate:self cancelButtonTitle:@"閉じる" otherButtonTitles:nil];
                                          [alert show];
                                      });
                                  }
                              }
                          }
                      }];
}

- (void)videoDownloadInfoRequest:(NSNotification *)notification
{
    NSString *selectedRecordId = [notification userInfo][@"recordId"];
    NSString *optionGetThumb = [notification userInfo][@"optionGetThumb"];
    NSLog(@"[%@] videoDownloadRequest selectedRecordId = %@",
          NSStringFromClass([self class]),
          selectedRecordId);
    
    //the video inforamtion is retrieved by the videoId, we need to get that
    NSString *seletedVideoId = [[DBManager sharedInstance] getVideoIdByRecordId:selectedRecordId];
    NSString *seletedVideoItag = [[DBManager sharedInstance] getVideoItagByRecordId:selectedRecordId];

    if(seletedVideoId != nil) {
    //get video information
        NSLog(@"[%@] videoDownloadInfoRequest selectedVideoId = %@ found!",
              NSStringFromClass([self class]),
              seletedVideoId);
        NSLog(@"[%@] videoDownloadInfoRequest Retrieving info for selectedVideoId = %@",
              NSStringFromClass([self class]),
              seletedVideoId);
        [[Operation sharedInstance] getVideoInfo:seletedVideoId
                                         success:^(id JSON) {
                                             NSLog(@"DONE!");
                                             
                                             if([JSON valueForKey:@"items"]) {
                                                 NSDictionary * selectedKey = [JSON valueForKey:@"items"];
                                                 
                                                 for(id key in selectedKey) {
                                                     NSString * valueId = [key objectForKey:@"id"];
                                                     NSString * valueKind = [key objectForKey:@"kind"];
                                                     NSString * valueDescription = [[key objectForKey:@"snippet"] objectForKey:@"description"];
                                                     NSString * valuePublishedAt = [[key objectForKey:@"snippet"] objectForKey:@"publishedAt"];
                                                     
                                                     NSString * valueChannelId = [[key objectForKey:@"snippet"] objectForKey:@"channelId"];
                                                     NSString * valueChannelTitle = [[key objectForKey:@"snippet"] objectForKey:@"channelTitle"];
                                                     
                                                     NSString * valueDuration = [[key objectForKey:@"contentDetails"] objectForKey:@"duration"];
                                                     NSString * valueDefinition = [[key objectForKey:@"contentDetails"] objectForKey:@"definition"];
                                                     NSString * valueThumbnailUrl = [[[[key objectForKey:@"snippet"] objectForKey:@"thumbnails"] objectForKey:@"default"] objectForKey:@"url"];
                                                     NSString * valueTitle = [[key objectForKey:@"snippet"] objectForKey:@"title"];
                                                     NSString * valueDislikeCount = [[key objectForKey:@"statistics"] objectForKey:@"dislikeCount"];
                                                     NSString * valueLikeCount = [[key objectForKey:@"statistics"] objectForKey:@"likeCount"];
                                                     NSString * valueViewCount = [[key objectForKey:@"statistics"] objectForKey:@"viewCount"];

                                                     NSLog(@"[%@] videoDownloadInfoRequest Registering info for selectedVideoId = %@ in database selectedRecordId = %@",
                                                           NSStringFromClass([self class]),
                                                           seletedVideoId,
                                                           selectedRecordId);

                                                     bool dbresult = [[DBManager sharedInstance] saveVideoInfo:selectedRecordId
                                                                                                    videoTitle:valueTitle
                                                                                              videoDescription:valueDescription
                                                                                                 videoDuration:valueDuration
                                                                                                videoChannelId:valueChannelId
                                                                                             videoChannelTitle:valueChannelTitle
                                                                                              videoPublishedAt:valuePublishedAt
                                                                                                 videoThumbUrl:valueThumbnailUrl
                                                                                                           raw:JSON];
                                                     if(dbresult == true) {
                                                         NSLog(@"[%@] videoDownloadInfoRequest Registering info for selectedVideoId = %@ in database selectedRecordId = %@ SUCCESS",
                                                               NSStringFromClass([self class]),
                                                               seletedVideoId,
                                                               selectedRecordId);
                                                         
                                                         NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
                                                         [nc postNotificationName:@"didUpdateVideoDataNotification" object:nil];
                                                         
                                                         //we can now get the thumbnail
                                                         if([optionGetThumb isEqualToString:@"true"]) {
                                                             NSDictionary *userInfo = @{@"recordId" : selectedRecordId};
                                                             [nc postNotificationName:@"videoDownloadThumbRequestNotification" object:self userInfo:userInfo];
                                                         }
                                                     } else {
                                                         NSLog(@"[%@] videoDownloadInfoRequest Registering info for selectedVideoId = %@ in database selectedRecordId = %@ FAILED",
                                                               NSStringFromClass([self class]),
                                                               seletedVideoId,
                                                               selectedRecordId);
                                                     }
                                                         

                                                 }
                                             }
                                             
                                         } failure:^(NSError *error, id JSON) {
                                             NSLog(@"FAIL!");
                                         }];
    } else {
        NSLog(@"[%@] videoDownloadInfoRequest selectedVideoId = %@ not found!",
              NSStringFromClass([self class]),
              seletedVideoId);
    }
}

- (void)videoDownloadThumbRequest:(NSNotification *)notification {
    NSString *selectedRecordId = [notification userInfo][@"recordId"];
    NSLog(@"[%@] videoDownloadThumbRequest selectedRecordId = %@",
          NSStringFromClass([self class]),
          selectedRecordId);
    
    //the video inforamtion is retrieved by the videoId, we need to get that
    NSString *seletedVideoId = [[DBManager sharedInstance] getVideoIdByRecordId:selectedRecordId];
    NSString *seletedVideoItag = [[DBManager sharedInstance] getVideoItagByRecordId:selectedRecordId];

    //we need the thumbnail URL
    //get this from the database
    NSString *selectedThumbnailUrl = [[DBManager sharedInstance] getVideoThumbnailUrlByRecordId:selectedRecordId];
    
    NSArray *filenameArray = [selectedThumbnailUrl componentsSeparatedByString:@"/"];
    NSString *filename = [filenameArray objectAtIndex:[filenameArray count] - 1];
    
    NSString *thumbnailFilename = [NSString stringWithFormat:@"%@_%@_%@", seletedVideoId,seletedVideoItag,filename];
    
    
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0]; // Get documents folder
    NSString *dataPath = [documentsDirectory stringByAppendingPathComponent:@"/save"];
    NSString *destinationPath = [dataPath stringByAppendingFormat:@"/%@", thumbnailFilename];
    
    if(selectedThumbnailUrl != nil) {
        NSLog(@"[%@] videoDownloadThumbRequest selectedThumbnailUrl = %@ found!",
              NSStringFromClass([self class]),
              seletedVideoId);
        [[Operation sharedInstance] getRaw:selectedThumbnailUrl
                                   success:^(id JSON) {
                                       NSLog(@"get thumbnail DONE!");
                                       
                                       NSError *error;
                                       
                                       if (![[NSFileManager defaultManager] fileExistsAtPath:dataPath])
                                       {
                                           [[NSFileManager defaultManager] createDirectoryAtPath:dataPath withIntermediateDirectories:NO attributes:nil error:&error];
                                       }
                                       bool writeResult = [JSON writeToFile:destinationPath atomically:true];
                                       if(writeResult == YES) {
                                           [[DBManager sharedInstance] saveVideoThumb:selectedRecordId videoFilename:thumbnailFilename];
                                           //[self showAlert:@"thumb アプリに保存" message:@"thumb 保存完了"];
                                           [[NSNotificationCenter defaultCenter]postNotificationName:@"didUpdateVideoDataNotification" object:nil];
                                       }
                                   } failure:^(NSError *error, id JSON) {
                                       NSLog(@"get thumbnail FAIL!");
                                   }];
    } else {
        NSLog(@"[%@] videoDownloadThumbRequest selectedThumbnailUrl = %@ not found!!",
              NSStringFromClass([self class]),
              seletedVideoId);
    }
}

- (void)videoDownloadDataRequest:(NSNotification *)notification {
    NSString *selectedRecordId = [notification userInfo][@"recordId"];
    NSLog(@"[%@] videoDownloadDataRequest selectedRecordId = %@",
          NSStringFromClass([self class]),
          selectedRecordId);
    
    //the video inforamtion is retrieved by the videoId, we need to get that
    NSString *seletedVideoId = [[DBManager sharedInstance] getVideoIdByRecordId:selectedRecordId];
    NSString *seletedVideoItag = [[DBManager sharedInstance] getVideoItagByRecordId:selectedRecordId];
    
    //we need the URL from the database
    NSString *streamUrl = [[DBManager sharedInstance] getVideoDataUrlByRecordId:selectedRecordId];
    NSString *streamToFilename = [NSString stringWithFormat:@"%@_%@.mp4",seletedVideoId,seletedVideoItag];
    NSString *streamToPath = [NSString stringWithFormat:@"save/%@",streamToFilename];
    
    if(streamUrl != nil) {
        [[Operation sharedInstance] streamToFile:streamUrl
                                      saveToPath:streamToPath
                                   progressBlock:^(double fractionCompleted) {
                                       //NSLog(@"Progress: %f", fractionCompleted);
                                       NSString *percentComplete = [NSString stringWithFormat:@"%.02f%%", fractionCompleted * 100];
                                       NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
                                       NSDictionary *userInfo = @{@"filename" : streamToFilename,
                                                                  @"progressPercent" : percentComplete};
                                       [nc postNotificationName:@"videoDownloadProgressNotification" object:self userInfo:userInfo];
                                   }
                                         success:^(NSString *savedTo) {
                                             NSLog(@"[%@] videoDownloadDataRequest selectedRecordId = %@ saved to file %@!",
                                                   NSStringFromClass([self class]),
                                                   selectedRecordId,
                                                   savedTo);
                                             //file was saved, now update database
                                             [[DBManager sharedInstance] saveVideoData:selectedRecordId videoFilename:streamToFilename];

                                             NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
                                             NSDictionary *userInfo = @{@"filename" : streamToFilename};
                                             [nc postNotificationName:@"videoDownloadDoneNotification" object:self userInfo:userInfo];

                                             //notify of the update
                                             [nc postNotificationName:@"didUpdateVideoDataNotification" object:nil];
                                         }
                                         failure:^(NSError *error) {
                                             NSLog(@"[%@] videoDownloadDataRequest selectedRecordId = %@ save failed!",
                                                   NSStringFromClass([self class]),
                                                   selectedRecordId);

                                             NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
                                             NSDictionary *userInfo = @{@"filename" : streamToFilename};
                                             [nc postNotificationName:@"videoDownloadFailedNotification" object:self userInfo:userInfo];
                                             //[self showAlert:@"アプリに保存" message:@"保存完了"];
                                             //percentComplete = @"失敗";
                                         }];
    } else {
        NSLog(@"[%@] videoDownloadDataRequest selectedRecordId = %@ streamUrl is nil!!",
              NSStringFromClass([self class]),
              selectedRecordId);
    }
}

- (void)videoDownloadDataBackgroundRequest:(NSNotification *)notification {
    NSString *selectedRecordId = [notification userInfo][@"recordId"];
    NSLog(@"[%@] videoDownloadDataBackgroundRequest selectedRecordId = %@",
          NSStringFromClass([self class]),
          selectedRecordId);
    
    //the video inforamtion is retrieved by the videoId, we need to get that
    NSString *seletedVideoId = [[DBManager sharedInstance] getVideoIdByRecordId:selectedRecordId];
    NSString *seletedVideoItag = [[DBManager sharedInstance] getVideoItagByRecordId:selectedRecordId];
    
    //we need the URL from the database
    NSString *streamUrl = [[DBManager sharedInstance] getVideoDataUrlByRecordId:selectedRecordId];
    NSString *streamToFilename = [NSString stringWithFormat:@"%@_%@.mp4",seletedVideoId,seletedVideoItag];
    NSString *streamToPath = [NSString stringWithFormat:@"save/%@",streamToFilename];
    
    if(streamToFilename != nil) {
        if(streamUrl != nil) {
            /*
            [[Operation sharedInstance] streamToFile:streamUrl
                                          saveToPath:streamToPath
                                       progressBlock:^(double fractionCompleted)
             */
            
            //set filename in database for background downloading
            [[DBManager sharedInstance] saveVideoData:selectedRecordId videoFilename:streamToFilename];
            
            NSURL *url = [NSURL URLWithString:streamUrl];
            NSURLRequest *request = [NSURLRequest requestWithURL:url];
            //[[[BackgroundSessionManager sharedManager] downloadTaskWithRequest:request progress:nil destination:nil completionHandler:nil] resume];
            int newFileId = [[OperationDownload sharedInstance] addDownload:streamToFilename path:streamUrl destinationPath:streamToFilename];
            [[OperationDownload sharedInstance] startDownloadingSingleFile:newFileId];


        } else {
            NSLog(@"[%@] videoDownloadDataBackgroundRequest selectedRecordId = %@ streamUrl is nil!!",
                  NSStringFromClass([self class]),
                  selectedRecordId);
        }
    } else {
        NSLog(@"[%@] videoDownloadDataBackgroundRequest selectedRecordId = %@ streamToFilename is nil!!",
              NSStringFromClass([self class]),
              selectedRecordId);
    }
}

# pragma mark - Network management methods

- (NSMutableDictionary *)buildParams:(NSDictionary *)params needAuth:(BOOL)needAuth {
    // APIの基本パラメータを生成
    NSMutableDictionary *result = [NSMutableDictionary dictionary];
    if (params) {
        [result setValuesForKeysWithDictionary:params];
    }
    if (needAuth) {
        NSString *username = USER_USERID;
        if (!username)
            username = @"";
        NSString *password = USER_PASSWORD;
        if (!password)
            password = @"";
        [result setObject:username forKey:KEY_USER_USERID];
        [result setObject:password forKey:KEY_USER_PASSWORD];
    }
    //[result setObject:@"ios" forKey:AGENT_KEY];
    //[result setObject:@"vname" forKey:VNAME_KEY];
    return result;
}

# pragma mark - Singleton pattern

static Operation *_sharedInstance;

- (id)init {
    self = [super init];
    if (self) {
        // 初期処理
        _lock = [[NSLock alloc] init];
        [self startup];
    }
    return self;
}

+ (instancetype)sharedInstance {
    @synchronized(self) {
        if (_sharedInstance == nil) {
            (void) [[self alloc] init]; // ここでは代入していない
        }
    }
    return _sharedInstance;
}

+ (id)allocWithZone:(NSZone *)zone {
    @synchronized(self) {
        if (_sharedInstance == nil) {
            _sharedInstance = [super allocWithZone:zone];
            return _sharedInstance;  // 最初の割り当てで代入し、返す
        }
    }
    return nil;
}

- (id)copyWithZone:(NSZone *)zone {
    return self;
}

@end
