<commit_before>//
//  RouteBase.swift
//  APIBase
//
//  Created by <NAME> on 8/10/15.
//  Copyright (c) 2015 Adorkable. All rights reserved.
//

import Foundation

internal let SubclassShouldOverrideString = "Subclass should override"

internal let SubclassShouldOverrideUrl : NSURL? = NSURL(string: "subclassShouldOverride://asdf")

public protocol Route {
    static var baseUrl : NSURL? { get }
    
    var timeoutInterval : NSTimeInterval { get }
    var cachePolicy : NSURLRequestCachePolicy  { get }
    
    var path : String { get }
    var query : String { get }
    static var httpMethod : String { get }
    var httpBody : String { get }
    
    func buildUrl() -> NSURL?
    
    func dataTask(configureUrlRequest configureUrlRequest : ( (urlRequest : NSMutableURLRequest) -> Void)?, completionHandler : (data : NSData?, urlResponse : NSURLResponse?, error : NSError?) -> Void ) -> NSURLSessionDataTask?
    func jsonTask(configureUrlRequest : ( (urlRequest : NSMutableURLRequest) -> Void)?, completionHandler : (jsonObject : AnyObject?, error : NSError?) -> Void) -> NSURLSessionDataTask?
}

public extension Route {
    
    func buildUrl() -> NSURL? {
        
        // TODO: proper way to tell clients of this library the reason for this failure
        guard self.path != SubclassShouldOverrideString else {
            return nil
        }
        
        var combinedPath = self.path
        
        var query = String()
        if self.query != SubclassShouldOverrideString
        {
            query += self.query
        }
        
        if query.characters.count > 0
        {
            combinedPath += "?" + query
        }
        
        return NSURL(string: combinedPath, relativeToURL: self.dynamicType.baseUrl)
    }
    
    public func dataTask(configureUrlRequest configureUrlRequest : ( (urlRequest : NSMutableURLRequest) -> Void)? = nil, completionHandler : (data : NSData?, urlResponse : NSURLResponse?, error : NSError?) -> Void ) -> NSURLSessionDataTask? {
        
        guard let url = self.buildUrl() else {
            // TODO: notify client?
            return nil
        }
        
        guard self.dynamicType.httpMethod != SubclassShouldOverrideString else {
            // TODO: notify client
            return nil
        }
        
        let urlRequest = NSMutableURLRequest(URL: url, cachePolicy: cachePolicy, timeoutInterval: timeoutInterval)
        urlRequest.setValue("text/plain", forHTTPHeaderField: "Content-Type")
        
        urlRequest.HTTPMethod = self.dynamicType.httpMethod
    
        if self.httpBody != SubclassShouldOverrideString
        {
            if let bodyData = self.httpBody.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
            {
                urlRequest.HTTPBody = bodyData
                urlRequest.setValue("\(bodyData.length)", forHTTPHeaderField: "Content-Length")
            }
        }
        
        if configureUrlRequest != nil
        {
            configureUrlRequest!(urlRequest: urlRequest)
        }
        
        let urlSession = NSURLSession.sharedSession()
        return urlSession.dataTaskWithRequest(urlRequest, completionHandler: completionHandler)
    }
    
    public func jsonTask(configureUrlRequest : ( (urlRequest : NSMutableURLRequest) -> Void)? = nil, completionHandler : (jsonObject : AnyObject?, error : NSError?) -> Void) -> NSURLSessionDataTask? {
        
        return self.dataTask(configureUrlRequest: { (urlRequest : NSMutableURLRequest) -> Void in
            
            if let configureUrlRequest = configureUrlRequest
            {
                configureUrlRequest(urlRequest: urlRequest)
            }
            urlRequest.setValue("application/json", forHTTPHeaderField: "Accept") // TODO: should this go before or after the calls closer to the client?
            
            }, completionHandler: { (data, urlResponse, error) -> Void in
                if data != nil
                {
                    do
                    {
                        if let jsonObject: AnyObject = try NSJSONSerialization.JSONObjectWithData(data!, options: [])
                        {
                            completionHandler(jsonObject: jsonObject, error: nil)
                        } else if let errorString = NSString(data: data!, encoding: NSUTF8StringEncoding) as? String
                        {
                            completionHandler(jsonObject: nil, error: NSError(domain: errorString, code: 0, userInfo: nil) )
                        } else
                        {
                            completionHandler(jsonObject: nil, error: NSError(domain: "Unknown error from request", code: 0, userInfo: nil) )
                        }
                    } catch let error as NSError
                    {
                        completionHandler(jsonObject: nil, error: error)
                    }
                } else
                {
                    completionHandler(jsonObject: nil, error: error)
                }
        })
    }
}

// TODO: currently Generic Protocols are not supported
public class RouteBase<T : API>: NSObject, Route {
    
    public static var baseUrl : NSURL? {<commit_msg>refactoring to use SuccessResult enum<commit_after>//
//  RouteBase.swift
//  APIBase
//
//  Created by <NAME> on 8/10/15.
//  Copyright (c) 2015 Adorkable. All rights reserved.
//

import Foundation

internal let SubclassShouldOverrideString = "Subclass should override"

internal let SubclassShouldOverrideUrl : NSURL? = NSURL(string: "subclassShouldOverride://asdf")

public enum SuccessResult<T> {
    case Success(T)
    case Failure(NSError)
}

public typealias ConfigureUrlRequestHandler = (urlRequest : NSMutableURLRequest) -> Void
public typealias DataTaskCompletionHandler = (result : SuccessResult<NSData>, urlResponse : NSURLResponse?) -> Void
public typealias JsonTaskCompletionHandler = (result : SuccessResult<AnyObject>) -> Void

public protocol Route {
    static var baseUrl : NSURL? { get }
    
    var timeoutInterval : NSTimeInterval { get }
    var cachePolicy : NSURLRequestCachePolicy  { get }
    
    var path : String { get }
    var query : String { get }
    static var httpMethod : String { get }
    var httpBody : String { get }
    
    func buildUrl() -> NSURL?
    
    func dataTask(configureUrlRequest configureUrlRequest : ConfigureUrlRequestHandler?, completionHandler : DataTaskCompletionHandler) -> NSURLSessionDataTask?
    func jsonTask(configureUrlRequest : ConfigureUrlRequestHandler?, completionHandler : JsonTaskCompletionHandler) -> NSURLSessionDataTask?
}

public extension Route {
    
    func buildUrl() -> NSURL? {
        
        // TODO: proper way to tell clients of this library the reason for this failure
        guard self.path != SubclassShouldOverrideString else {
            return nil
        }
        
        var combinedPath = self.path
        
        var query = String()
        if self.query != SubclassShouldOverrideString
        {
            query += self.query
        }
        
        if query.characters.count > 0
        {
            combinedPath += "?" + query
        }
        
        return NSURL(string: combinedPath, relativeToURL: self.dynamicType.baseUrl)
    }
    
    public func dataTask(configureUrlRequest configureUrlRequest : ConfigureUrlRequestHandler? = nil, completionHandler : DataTaskCompletionHandler) -> NSURLSessionDataTask? {
        
        guard let url = self.buildUrl() else {
            // TODO: notify client?
            return nil
        }
        
        guard self.dynamicType.httpMethod != SubclassShouldOverrideString else {
            // TODO: notify client
            return nil
        }
        
        let urlRequest = NSMutableURLRequest(URL: url, cachePolicy: cachePolicy, timeoutInterval: timeoutInterval)
        urlRequest.setValue("text/plain", forHTTPHeaderField: "Content-Type")
        
        urlRequest.HTTPMethod = self.dynamicType.httpMethod
    
        if self.httpBody != SubclassShouldOverrideString
        {
            if let bodyData = self.httpBody.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
            {
                urlRequest.HTTPBody = bodyData
                urlRequest.setValue("\(bodyData.length)", forHTTPHeaderField: "Content-Length")
            }
        }
        
        if let configureUrlRequest = configureUrlRequest {
            configureUrlRequest(urlRequest: urlRequest)
        }
        
        let urlSession = NSURLSession.sharedSession()
        
        return urlSession.dataTaskWithRequest(urlRequest, completionHandler: { (data, urlResponse, error) -> Void in
            
            guard let data = data else {
                
                if let error = error {
                    
                    completionHandler(result: .Failure(error), urlResponse: urlResponse)
                } else {
                    
                    completionHandler(result: .Failure(NSError(domain: "Response is empty", code: 0, userInfo: nil) ), urlResponse: urlResponse)
                }
                
                return
            }
            
            completionHandler(result: .Success(data), urlResponse: urlResponse)
        })
    }
    
    public func jsonTask(configureUrlRequest : ConfigureUrlRequestHandler? = nil, completionHandler : JsonTaskCompletionHandler) -> NSURLSessionDataTask? {
        
        return self.dataTask(configureUrlRequest: { (urlRequest : NSMutableURLRequest) -> Void in
            
            if let configureUrlRequest = configureUrlRequest
            {
                configureUrlRequest(urlRequest: urlRequest)
            }
            urlRequest.setValue("application/json", forHTTPHeaderField: "Accept") // TODO: should this go before or after the calls closer to the client?
            
            }, completionHandler: { (result, urlResponse) -> Void in
                
                switch result {
                case .Success(let data):
                    
                    do
                    {
                        let jsonObject = try NSJSONSerialization.JSONObjectWithData(data, options: [])
                        completionHandler(result: .Success(jsonObject))
                        
                    } catch let error as NSError
                    {
                        completionHandler(result: .Failure(error))
                    }
                    
                    break
                    
                case .Failure(let error):
                    completionHandler(result: .Failure(error))
                    break
                }
        })
    }
}

// TODO: currently Generic Protocols are not supported
public class RouteBase<T : API>: NSObject, Route {
    
    public static var baseUrl : NSURL? {
{'fraction_non_alphanumeric': 0.07987485046470967, 'fraction_numerical': 0.0023925646452562805, 'mean_word_length': 1.9043292357028327, 'pattern_counts': {'":': 0, '<': 10, '<?xml version=': 0, '>': 40, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4589534', 'n_tokens_mistral': 2714, 'n_tokens_neox': 2612, 'n_words': 685}