//
//  RequestHandler.m
//  Giwa
//
//  Created by Jason Lee on 12/16/14.
//  Copyright (c) jasondevelop. All rights reserved.
//
#import "JDObject.h"
#import "AFHTTPRequestOperation.h"

#import "JDJSONModel.h"

#import "RequestHandler.h"
#import "AFHTTPRequestOperationManager.h"
#import "AFHTTPRequestOperationManager+Synchronous.h"


#define NSEUCKREncoding -2147481280


@interface RequestHandler () {
    NSString *_requestPath;
}
@end

@implementation RequestHandler


#pragma mark - URL Encoding
- (NSString *)URLEncodedString:(NSString *)string
{
    return [string stringByAddingPercentEscapesUsingEncoding:NSEUCKREncoding];

//    NSString *encodedString = (__bridge NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,
//                                                                                           (CFStringRef)string,
//                                                                                           NULL,
//                                                                                           (CFStringRef)@"!*'();:@&=+$,/?%#[]",
//                                                                                           kCFStringEncodingUTF8);
//    return encodedString;
}

//- (NSString*)URLDecodedString:(NSString *)string
//{
//
//
//
//	NSString *result = (__bridge NSString *)CFURLCreateStringByReplacingPercentEscapesUsingEncoding(kCFAllocatorDefault,
//                                                                                                    (CFStringRef)string,
//                                                                                                    CFSTR(""),
//                                                                                                    kCFStringEncodingUTF8);
//	return result;
//}

#pragma mark - Prepare Request
- (void)sendRequest {
    [[JDFacade facade] loadIndicator:YES allowUserInteraction:NO];
}

- (void)finishRequest {
    [[JDFacade facade] loadIndicator:NO allowUserInteraction:YES];
}


#pragma mark - URL Request
- (void)sendAsyncRequestURLString:(NSString *)URLString method:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters completion:(RequestHandlerCompletionBlock)completion failure:(RequestHandlerFailureBlock)failure {

    [self sendRequest];

    NSString *encodURLString = [self URLEncodedString:[NSString stringWithFormat:@"%@%@", URLString, path]];

    NSLog(@"URL=%@", encodURLString);
    NSLog(@"PARAM=%@", parameters);

    NSError *error = nil;
    BOOL hasFile = NO, isDataImage = NO, isMultipartForm = parameters[ksHTTPMultipartForm] && [parameters[ksHTTPRequestPOST] boolValue] ? YES : NO;
    NSData *dataToUpload = nil;
    NSString *dataParameter = nil;

    NSInteger i = 0;
    for (NSObject *obj in parameters.allValues) {//파일형식이 있는지 체크,

        if (!hasFile && ([obj isKindOfClass:[UIImage class]] || [obj isKindOfClass:[NSData class]])) {
            isDataImage = [obj isKindOfClass:[UIImage class]];
            dataToUpload = isDataImage ? UIImageJPEGRepresentation((UIImage *)obj, 1.0) : (NSData *)obj;
            dataParameter = parameters.allKeys[i];
            hasFile = YES;
            break;
        }
        i++;
    }

    NSMutableURLRequest *request = nil;
    if (!hasFile && !isMultipartForm) {//no image
        request = [[AFHTTPRequestSerializer serializer] requestWithMethod:method URLString:encodURLString parameters:parameters error:&error];

    } else {//Multipart-form

        NSMutableDictionary *tmpParams = [NSMutableDictionary dictionaryWithDictionary:parameters];
        [tmpParams removeObjectForKey:dataParameter];

        request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:method URLString:encodURLString parameters:tmpParams constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
            NSString *fileName = isDataImage ? @"tmp.jpg" : @"tmp.txt";
            NSString *mimeType = isDataImage ? @"image/jpeg" : @"application/json"; //text/plain

            [formData appendPartWithFileData:dataToUpload name:dataParameter fileName:fileName mimeType:mimeType];

        } error:&error];
    }

    AFHTTPRequestOperation *op = [[AFHTTPRequestOperation alloc] initWithRequest:request];
    op.responseSerializer = [AFJSONResponseSerializer serializer];

    [[JDFacade facade] setCurrentOperation:op];
    
    [op setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
        [self finishRequest];

        NSLog(@"\n\nJSON=%@\n\n", responseObject);
        if (completion) {
            completion(responseObject);
        }

    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        [self finishRequest];

        if (failure) {
            failure(error);
        }
    }];

    [[NSOperationQueue mainQueue] addOperation:op];
}

- (void)sendAsyncPostRequestURL:(NSString *)URLString path:(NSString *)path parameters:(NSDictionary *)parameters completion:(RequestHandlerCompletionBlock)completion failure:(RequestHandlerFailureBlock)failure {
    [self sendAsyncRequestURLString:URLString method:ksHTTPRequestPOST path:(NSString *)path parameters:parameters completion:completion failure:failure];
}

- (void)sendAsyncGetRequestURL:(NSString *)URLString path:(NSString *)path parameters:(NSDictionary *)parameters completion:(RequestHandlerCompletionBlock)completion failure:(RequestHandlerFailureBlock)failure {
    [self sendAsyncRequestURLString:URLString method:ksHTTPRequestGET path:(NSString *)path parameters:parameters completion:completion failure:failure];
}



#pragma mark - 비동기 API 요청

- (void)sendAsyncGetRequestAPIPath:(NSString *)apiPath parameters:(NSDictionary *)parameters modelClass:(Class)modelClass completion:(RequestHandlerCompletionBlock)completion failure:(RequestHandlerFailureBlock)failure {
    [self sendAsyncRequestAPIPath:apiPath method:ksHTTPRequestGET parameters:parameters modelClass:modelClass showLoadingView:YES completion:completion failure:failure];
}

- (void)sendAsyncPostRequestAPIPath:(NSString *)apiPath parameters:(NSDictionary *)parameters modelClass:(Class)modelClass completion:(RequestHandlerCompletionBlock)completion failure:(RequestHandlerFailureBlock)failure {
    [self sendAsyncRequestAPIPath:apiPath method:ksHTTPRequestPOST parameters:parameters modelClass:modelClass showLoadingView:YES completion:completion failure:failure];
}

- (void)sendAsyncPostRequestAPIPath:(NSString *)apiPath parameters:(NSDictionary *)parameters modelClass:(Class)modelClass showLoadingView:(BOOL)showLoadingView completion:(RequestHandlerCompletionBlock)completion failure:(RequestHandlerFailureBlock)failure {
    [self sendAsyncRequestAPIPath:apiPath method:ksHTTPRequestPOST parameters:parameters modelClass:modelClass showLoadingView:showLoadingView completion:completion failure:failure];
}


- (void)sendAsyncRequestAPIPath:(NSString *)apiPath method:(NSString *)method parameters:(NSDictionary *)parameters modelClass:(Class)modelClass showLoadingView:(BOOL)showLoadingView completion:(RequestHandlerCompletionBlock)completion failure:(RequestHandlerFailureBlock)failure {

    _requestPath = apiPath;
    
    if (showLoadingView) {
        [self sendRequest];
    }

    NSString *rootPath = API_ROOT_PATH;

    NSString *pathURL = [self URLEncodedString:[NSString stringWithFormat:@"%@%@%@", kAPIServer, rootPath, apiPath]];

    NSLog(@"PATH=%@", pathURL);
    NSLog(@"PARAM=%@", parameters);

    NSError *error = nil;
    BOOL hasFile = NO, isDataImage = NO, isMultipartForm = parameters[ksHTTPMultipartForm] && [parameters[ksHTTPRequestPOST] boolValue] ? YES : NO;
    NSMutableArray *fileArray = nil;
    NSMutableArray *fileParams = nil;

    NSInteger index = 0;
    for (NSObject *obj in parameters.allValues) {//파일형식이 있는지 체크,

        if (([obj isKindOfClass:[UIImage class]] || [obj isKindOfClass:[NSData class]])) {
            if (!fileArray) {
                fileArray = [[NSMutableArray alloc] init];
                fileParams = [[NSMutableArray alloc] init];
                hasFile = YES;
            }

            isDataImage = [obj isKindOfClass:[UIImage class]];
            [fileArray addObject:isDataImage ? UIImageJPEGRepresentation((UIImage *)obj, 1.0) : (NSData *)obj];
            [fileParams addObject:parameters.allKeys[index]];
        }
        index++;
    }


    NSMutableURLRequest *request = nil;

    if (!hasFile && !isMultipartForm) {//no file

        request = [[AFJSONRequestSerializer serializer] requestWithMethod:method URLString:pathURL parameters:parameters error:&error];
        [request setHTTPMethod:method];
        [request setTimeoutInterval:kDefaultTimeOut];
        [request setValue:@"application/json;charset=UTF-8" forHTTPHeaderField:@"Content-Type"];
        
        NSString *language = [[NSLocale preferredLanguages] objectAtIndex:0];
        //FIXME : when production
#ifdef DEBUG_MODE
        language = @"ko";
#endif
        [request setValue:language forHTTPHeaderField:@"Accept-Language"];

    } else {//Multipart-form

        NSMutableDictionary *tmpParams = [NSMutableDictionary dictionaryWithDictionary:parameters];
        [tmpParams removeObjectForKey:fileParams];

        request = [[AFJSONRequestSerializer serializer] multipartFormRequestWithMethod:method URLString:pathURL parameters:tmpParams constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
                NSString *mimeType = isDataImage ? @"image/jpeg" : @"text/plain";
                NSInteger i = 0;
//                NSString *name = [fileParams[i] substringToIndex:[fileParams[i] rangeOfString:@"_"].location];
                NSString *name = fileParams[i];
            for (NSData *dataToUpload in fileArray) {

                    NSString *fileName = isDataImage ? [NSString stringWithFormat:@"tmp_%zd.jpg", i+1] : [NSString stringWithFormat:@"tmp_%zd.txt", i+1];
                    [formData appendPartWithFileData:dataToUpload name:name fileName:fileName mimeType:mimeType];
                }

        } error:&error];
    }

    [request setValue:self.authorization ? self.authorization : ksEmptyString forHTTPHeaderField:@"Authorization"];
    [request setValue:self.homegrpId ? self.homegrpId : ksEmptyString forHTTPHeaderField:@"X-kneet-homegrp"];

    AFHTTPRequestOperation *op = [[AFHTTPRequestOperation alloc] initWithRequest:request];
    op.responseSerializer = [AFHTTPResponseSerializer serializer];

    [JDFacade facade].currentOperation = op;
    
    [op setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
        [self finishRequest];

        NSString *JSONString = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];

#ifndef PRODUCT_MODE
        NSLog(@"\n\nJSON=%@\n\n", JSONString);
#endif

        NSError *error = nil;
        id rawJSON = [NSJSONSerialization JSONObjectWithData:responseObject
                                                 options:NSJSONReadingAllowFragments
                                                   error:&error];

        id JSONModel = nil;
        if (modelClass) {
            if ([rawJSON isKindOfClass:[NSArray class]]) {
                NSDictionary *JSONDic = @{@"list": rawJSON};
                JSONModel = [[modelClass alloc] initWithDictionary:JSONDic error:&error];
            } else {
                JSONModel = [[modelClass alloc] initWithString:JSONString error:&error];
            }
        } else {
            JSONModel = rawJSON;
        }


        if (error) {//오류 처리
//            JSONModel *result = [[JSONModel alloc] initWithString:JSONString error:nil];
//
//            NSString *resultMsg = result.resultMsg ? result.resultMsg : MSG_ALERT_API_FAIL;
//            [[GWFacade facade] alert:resultMsg];
            JSONModel = nil;
        }

        //쿠키 설정
        NSArray* cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:[operation.response allHeaderFields] forURL:operation.request.URL];
        if (cookies && cookies.count > 0) {
            [self setCookies:cookies];
        }

        if (completion) {
            completion(JSONModel);
        }


    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        [self finishRequest];
        NSLog(@"HTTPStatus=%zd\n%@", operation.response.statusCode, error.localizedDescription);

        NSString *JSONString = [[NSString alloc] initWithData:operation.responseData encoding:NSUTF8StringEncoding];

#ifndef PRODUCT_MODE
        NSLog(@"\n\nERROR=%@\n\n", JSONString);
#endif

        JDErrorModel *jerror = [[JDErrorModel alloc] initWithString:JSONString error:&error];

        BOOL isLoginView = [[JDFacade facade].currentViewController isKindOfClass:[NSClassFromString(@"LoginViewController") class]];
        if (!isLoginView && [jerror.errorCode isEqualToString:API_RESPONSE_UNAUTHORIZED_TOKEN]) {//인증토큰이 만료된 경우, 로그인 이동
            [[JDFacade facade] gotoLoginViewWithExpiring];

        } else if (failure && jerror) {
            failure(jerror ? jerror : error);
        } else {
            
            [[JDFacade facade] retryAlert:MSG_ALERT_SERVER_FAIL completionHander:^{
                [self sendAsyncRequestAPIPath:apiPath method:method parameters:parameters modelClass:modelClass
                              showLoadingView:showLoadingView completion:completion failure:failure];
            }];
        }
    }];

    [[NSOperationQueue mainQueue] addOperation:op];
}


#pragma mark - 동기 API 요청

- (id)sendSyncRequestAPIPath:(NSString *)apiPath method:(NSString *)method parameters:(NSDictionary *)parameters modelClass:(Class)modelClass showLoadingView:(BOOL)showLoadingView {

    if (showLoadingView) {
        [self sendRequest];
    }

    NSString *rootPath = API_ROOT_PATH;

    NSString *pathURL = [self URLEncodedString:[NSString stringWithFormat:@"%@%@%@", kAPIServer, rootPath, apiPath]];

    NSLog(@"PATH=%@", pathURL);
    NSLog(@"PARAM=%@", parameters);

    NSError *error = nil;

    NSMutableURLRequest *request = [[AFJSONRequestSerializer serializer] requestWithMethod:method URLString:pathURL parameters:parameters error:&error];
    [request setHTTPMethod:method];
    [request setTimeoutInterval:kDefaultTimeOut];
    [request setValue:@"application/json;charset=UTF-8" forHTTPHeaderField:@"Content-Type"];
    [request setValue:@"en" forHTTPHeaderField:@"Accept-Language"];

    [request setValue:self.authorization ? self.authorization : ksEmptyString forHTTPHeaderField:@"Authorization"];
    [request setValue:self.homegrpId ? self.homegrpId : ksEmptyString forHTTPHeaderField:@"X-kneet-homegrp"];


    AFHTTPRequestOperation *op = [[AFHTTPRequestOperation alloc] initWithRequest:request];
    op.responseSerializer = [AFHTTPResponseSerializer serializer];
    [op start];
    [op waitUntilFinished];

    // Must call responseObject before checking the error
    id responseObject = [op responseObject];

    if (showLoadingView) {
        [self finishRequest];
    }

    if (error) {
        return nil;
    }

    NSString *JSONString = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];

#ifdef DEBUG
    NSLog(@"\n\nJSON=%@\n\n", JSONString);
#endif

    id rawJSON = [NSJSONSerialization JSONObjectWithData:responseObject
                                                 options:NSJSONReadingAllowFragments
                                                   error:&error];

    id JSONModel = nil;
    if (modelClass) {
        if ([rawJSON isKindOfClass:[NSArray class]]) {
            NSDictionary *JSONDic = @{@"list": rawJSON};
            JSONModel = [[modelClass alloc] initWithDictionary:JSONDic error:&error];
        } else {
            JSONModel = [[modelClass alloc] initWithString:JSONString error:&error];
        }
    } else {
        JSONModel = rawJSON;
    }

    return JSONModel;
}

- (id)sendSyncPostRequestAPIPath:(NSString *)apiPath parameters:(NSDictionary *)parameters modelClass:(Class)modelClass showLoadingView:(BOOL)showLoadingView {
    return [self sendSyncRequestAPIPath:apiPath method:ksHTTPRequestPOST parameters:parameters modelClass:modelClass showLoadingView:showLoadingView];
}

- (id)sendSyncGetRequestAPIPath:(NSString *)apiPath parameters:(NSDictionary *)parameters modelClass:(Class)modelClass showLoadingView:(BOOL)showLoadinView {
    return [self sendSyncRequestAPIPath:apiPath method:ksHTTPRequestGET parameters:parameters modelClass:modelClass showLoadingView:showLoadinView];
}

#pragma mark - Cookies

- (void)setCookies:(NSArray *)cookies {
    NSMutableDictionary* cookieDict = [NSMutableDictionary new];

    for(NSHTTPCookie* cookie in cookies){
        [cookieDict setValue:cookie.properties forKey:cookie.name];
    }

    //쿠키 설정
    NSHTTPCookie *localCookie = [NSHTTPCookie cookieWithProperties:cookieDict];
    [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:localCookie];
}

#pragma mark - Singleton

+ (RequestHandler *)handler {

    static RequestHandler *sharedRequestHandler = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedRequestHandler = [[self alloc] init];
    });
    return sharedRequestHandler;
}

- (id)init {
    if (self = [super init]) {

    }
    return self;
}


@end
