<commit_before>/**
 * Copyright IBM Corporation 2015
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/

import Foundation
import Alamofire
import AlamofireObjectMapper
import ObjectMapper

/**
 The IBM Watson™ Dialog service provides a comprehensive, robust, platform for managing
 conversations between virtual agents and users through an application programming
 interface (API). These conversations are commonly referred to as dialogs.
 */
public class Dialog: WatsonService {

    // The shared WatsonGateway singleton.
    let gateway = WatsonGateway.sharedInstance

    // The authentication strategy to obtain authorization tokens.
    let authStrategy: AuthenticationStrategy

    // TODO: comment this initializer
    public required init(authStrategy: AuthenticationStrategy) {
        self.authStrategy = authStrategy
    }

    // TODO: comment this initializer
    public convenience required init(username: String, password: String) {
        let authStrategy = BasicAuthenticationStrategy(tokenURL: Constants.tokenURL,
            serviceURL: Constants.serviceURL, username: username, password: password)
        self.init(authStrategy: authStrategy)
    }

    // Date formatter
    private static var formatter: NSDateFormatter {
        let formatter = NSDateFormatter()
        formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
        return formatter
    }
    
    // MARK: Content Operations
    
    /**
     Get the content for each node associated with a Dialog application.
    
     Depending on the Dialog design, each node can either be an input/output
     pair or just a single node.
    
     - parameter dialogID: The Dialog application identifier.
     - parameter completionHandler: A function invoked with the response from Watson.
     */
    public func getContent(dialogID: String,
        completionHandler: ([Node]?, NSError?) -> Void)
    {
        let request = WatsonRequest(
            method: .GET,
            serviceURL: Constants.serviceURL,
            endpoint: Constants.content(dialogID),
            authStrategy: authStrategy,
            accept: .JSON)

        gateway.request(request, serviceError: DialogError()) { data, error in
            let nodes = Mapper<Node>().mapDataArray(data, keyPath: "items")
            completionHandler(nodes, error)
        }
    }
    
    /**
     Update the content for specific nodes of the Dialog application.
     
     - parameter dialogID: The Dialog application identifier.
     - parameter nodes: The specified nodes and updated content.
     - parameter completionHandler: A function invoked with the response from Watson.
     */
    public func updateContent(dialogID: DialogID, nodes: [Node],
        completionHandler: NSError? -> Void)
    {
        let request = WatsonRequest(
            method: .PUT,
            serviceURL: Constants.serviceURL,
            endpoint: Constants.content(dialogID),
            authStrategy: authStrategy,
            contentType: .JSON,
            messageBody: Mapper().toJSONData(nodes, header: "items"))

        gateway.request(request, serviceError: DialogError()) { data, error in
            completionHandler(error)
        }
    }
    
    /**
     List the Dialog applications associated with the service instance.

     - parameter completionHandler: A function invoked with the response from Watson.
     */
    public func getDialogs(completionHandler: ([DialogModel]?, NSError?) -> Void) {

        let request = WatsonRequest(
            method: .GET,
            serviceURL: Constants.serviceURL,
            endpoint: Constants.dialogs,
            authStrategy: authStrategy,
            accept: .JSON)

        gateway.request(request, serviceError: DialogError()) { data, error in
            let dialogs = Mapper<DialogModel>().mapDataArray(data, keyPath: "dialogs")
            completionHandler(dialogs, error)
        }
    }
    
    /**
     Create a dialog application by uploading an existing Dialog file to the
     service instance. The returned Dialog application identifier can be used
     for subsequent calls to the API.

     The file content type is determined by the file extension:
        - .mct for encrypted Dialog account file
        - .json for Watson Dialog document JSON format
        - .xml for Watson Dialog document XML format

     - parameter name: The desired name of the Dialog application instance.
     - parameter fileURL: The URL to a file that will be uploaded and used to create
            the Dialog application. Must contain an extension of .mct for encrypted
            Dialog account file, .json for Watson Dialog document JSON format, or .xml
            for Watson Dialog document XML format.
     - parameter completionHandler: A function invoked with the response from Watson.
     */
    public func createDialog(name: String, fileURL: NSURL,
        completionHandler: (DialogID?, NSError?) -> Void)
    {
        // TODO: Update this function after WatsonGateway supports uploads
        authStrategy.refreshToken() { error in
            var headerParams = [String: String]()
            if let token = self.authStrategy.token {
                headerParams["X-Watson-Authorization-Token"] = token
            }

            let request = WatsonRequest(
                method: .POST,
                serviceURL: Constants.serviceURL,
                endpoint: Constants.dialogs,
                authStrategy: self.authStrategy,
                accept: .JSON,
                headerParams: headerParams)

            Alamofire.upload(request,
                multipartFormData: { multipartFormData in
                    let nameData = name.dataUsingEncoding(NSUTF8StringEncoding)!
                    multipartFormData.appendBodyPart(data: nameData, name: "name")
                    multipartFormData.appendBodyPart(fileURL: fileURL, name: "file")
                },
                encodingCompletion: { encodingResult in
                    switch encodingResult {
                    case .Success(let upload, _, _):
                        upload.responseObject {
                            (response: Response<DialogIDModel, NSError>) in
                            let unwrapID = {
                                (dialogID: DialogIDModel?, error: NSError?) in
                                completionHandler(dialogID?.id, error) }
                            validate(response, successCode: 201,
                                serviceError: DialogError(),
                                completionHandler: unwrapID)
                        }
                    case .Failure:
                        let nsError = NSError(
                            domain: "com.alamofire.error",
                            code: -6008,
                            userInfo: [NSLocalizedDescriptionKey:
                                "Unable to encode data as multipart form."])
                        completionHandler(nil, nsError)
                    }
            })
        }
    }
    
    /**
     Delete a Dialog application associated with the service instance. This
     permanently removes all associated data.
    
     - parameter dialogID: The Dialog application identifier.
     - parameter completionHandler: A function invoked with the response from Watson.
     */
    public func deleteDialog(dialogID: DialogID, completionHandler: NSError? -> Void) {

        let request = WatsonRequest(
            method: .DELETE,
            serviceURL: Constants.serviceURL,
            endpoint: Constants.dialogID(dialogID),
            authStrategy: authStrategy)

        gateway.request(request, serviceError: DialogError()) { data, error in
            completionHandler(error)
        }
    }
    
    /**
     Download the Dialog file associated with the given Dialog application.
     
     - parameter dialogID: The Dialog application identifier.
     - parameter format: The format of the file. The format can be either OctetStream
            (.mct file), Watson Dialog document JSON format (.json file), or Watson
            Dialog document XML format (.xml file).
     - parameter completionHandler: A function invoked with the response from Watson.
     */
    public func getDialogFile(dialogID: DialogID, format: MediaType? = nil,
        completionHandler: (NSURL?, NSError?) -> Void)
    {
        // TODO: Update this function after WatsonGateway supports uploads
        authStrategy.refreshToken() { error in
            var headerParams = [String: String]()
            if let token = self.authStrategy.token {
                headerParams["X-Watson-Authorization-Token"] = token
            }

            let request = WatsonRequest(
                method: .GET,
                serviceURL: Constants.serviceURL,
                endpoint: Constants.dialogID(dialogID),
                authStrategy: self.authStrategy,
                accept: format,
                headerParams: headerParams)

            var fileURL: NSURL?
            let r = Alamofire.download(request) { (temporaryURL, response) in
                let manager = NSFileManager.defaultManager()
                let directoryURL = manager.URLsForDirectory(.DocumentDirectory,
                    inDomains: .UserDomainMask)[0]
                let pathComponent = response.suggestedFilename
                fileURL = directoryURL.URLByAppendingPathComponent(pathComponent!)
                return fileURL!
            }

            r.response { _, response, _, error in
                var data: NSData? = nil
                if let file = fileURL?.path { data = NSData(contentsOfFile: file) }
                let includeFileURL = { error in completionHandler(fileURL, error) }
                validate(response, data: data, error: error, serviceError: DialogError(),<commit_msg>Use a more descriptive variable name in Dialog<commit_after>/**
 * Copyright IBM Corporation 2015
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/

import Foundation
import Alamofire
import AlamofireObjectMapper
import ObjectMapper

/**
 The IBM Watson™ Dialog service provides a comprehensive, robust, platform for managing
 conversations between virtual agents and users through an application programming
 interface (API). These conversations are commonly referred to as dialogs.
 */
public class Dialog: WatsonService {

    // The shared WatsonGateway singleton.
    let gateway = WatsonGateway.sharedInstance

    // The authentication strategy to obtain authorization tokens.
    let authStrategy: AuthenticationStrategy

    // TODO: comment this initializer
    public required init(authStrategy: AuthenticationStrategy) {
        self.authStrategy = authStrategy
    }

    // TODO: comment this initializer
    public convenience required init(username: String, password: String) {
        let authStrategy = BasicAuthenticationStrategy(tokenURL: Constants.tokenURL,
            serviceURL: Constants.serviceURL, username: username, password: password)
        self.init(authStrategy: authStrategy)
    }

    // Date formatter
    private static var formatter: NSDateFormatter {
        let formatter = NSDateFormatter()
        formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
        return formatter
    }
    
    // MARK: Content Operations
    
    /**
     Get the content for each node associated with a Dialog application.
    
     Depending on the Dialog design, each node can either be an input/output
     pair or just a single node.
    
     - parameter dialogID: The Dialog application identifier.
     - parameter completionHandler: A function invoked with the response from Watson.
     */
    public func getContent(dialogID: String,
        completionHandler: ([Node]?, NSError?) -> Void)
    {
        let request = WatsonRequest(
            method: .GET,
            serviceURL: Constants.serviceURL,
            endpoint: Constants.content(dialogID),
            authStrategy: authStrategy,
            accept: .JSON)

        gateway.request(request, serviceError: DialogError()) { data, error in
            let nodes = Mapper<Node>().mapDataArray(data, keyPath: "items")
            completionHandler(nodes, error)
        }
    }
    
    /**
     Update the content for specific nodes of the Dialog application.
     
     - parameter dialogID: The Dialog application identifier.
     - parameter nodes: The specified nodes and updated content.
     - parameter completionHandler: A function invoked with the response from Watson.
     */
    public func updateContent(dialogID: DialogID, nodes: [Node],
        completionHandler: NSError? -> Void)
    {
        let request = WatsonRequest(
            method: .PUT,
            serviceURL: Constants.serviceURL,
            endpoint: Constants.content(dialogID),
            authStrategy: authStrategy,
            contentType: .JSON,
            messageBody: Mapper().toJSONData(nodes, header: "items"))

        gateway.request(request, serviceError: DialogError()) { data, error in
            completionHandler(error)
        }
    }
    
    /**
     List the Dialog applications associated with the service instance.

     - parameter completionHandler: A function invoked with the response from Watson.
     */
    public func getDialogs(completionHandler: ([DialogModel]?, NSError?) -> Void) {

        let request = WatsonRequest(
            method: .GET,
            serviceURL: Constants.serviceURL,
            endpoint: Constants.dialogs,
            authStrategy: authStrategy,
            accept: .JSON)

        gateway.request(request, serviceError: DialogError()) { data, error in
            let dialogs = Mapper<DialogModel>().mapDataArray(data, keyPath: "dialogs")
            completionHandler(dialogs, error)
        }
    }
    
    /**
     Create a dialog application by uploading an existing Dialog file to the
     service instance. The returned Dialog application identifier can be used
     for subsequent calls to the API.

     The file content type is determined by the file extension:
        - .mct for encrypted Dialog account file
        - .json for Watson Dialog document JSON format
        - .xml for Watson Dialog document XML format

     - parameter name: The desired name of the Dialog application instance.
     - parameter fileURL: The URL to a file that will be uploaded and used to create
            the Dialog application. Must contain an extension of .mct for encrypted
            Dialog account file, .json for Watson Dialog document JSON format, or .xml
            for Watson Dialog document XML format.
     - parameter completionHandler: A function invoked with the response from Watson.
     */
    public func createDialog(name: String, fileURL: NSURL,
        completionHandler: (DialogID?, NSError?) -> Void)
    {
        // TODO: Update this function after WatsonGateway supports uploads
        authStrategy.refreshToken() { error in
            var headerParams = [String: String]()
            if let token = self.authStrategy.token {
                headerParams["X-Watson-Authorization-Token"] = token
            }

            let request = WatsonRequest(
                method: .POST,
                serviceURL: Constants.serviceURL,
                endpoint: Constants.dialogs,
                authStrategy: self.authStrategy,
                accept: .JSON,
                headerParams: headerParams)

            Alamofire.upload(request,
                multipartFormData: { multipartFormData in
                    let nameData = name.dataUsingEncoding(NSUTF8StringEncoding)!
                    multipartFormData.appendBodyPart(data: nameData, name: "name")
                    multipartFormData.appendBodyPart(fileURL: fileURL, name: "file")
                },
                encodingCompletion: { encodingResult in
                    switch encodingResult {
                    case .Success(let upload, _, _):
                        upload.responseObject {
                            (response: Response<DialogIDModel, NSError>) in
                            let unwrapID = {
                                (dialogID: DialogIDModel?, error: NSError?) in
                                completionHandler(dialogID?.id, error) }
                            validate(response, successCode: 201,
                                serviceError: DialogError(),
                                completionHandler: unwrapID)
                        }
                    case .Failure:
                        let nsError = NSError(
                            domain: "com.alamofire.error",
                            code: -6008,
                            userInfo: [NSLocalizedDescriptionKey:
                                "Unable to encode data as multipart form."])
                        completionHandler(nil, nsError)
                    }
            })
        }
    }
    
    /**
     Delete a Dialog application associated with the service instance. This
     permanently removes all associated data.
    
     - parameter dialogID: The Dialog application identifier.
     - parameter completionHandler: A function invoked with the response from Watson.
     */
    public func deleteDialog(dialogID: DialogID, completionHandler: NSError? -> Void) {

        let request = WatsonRequest(
            method: .DELETE,
            serviceURL: Constants.serviceURL,
            endpoint: Constants.dialogID(dialogID),
            authStrategy: authStrategy)

        gateway.request(request, serviceError: DialogError()) { data, error in
            completionHandler(error)
        }
    }
    
    /**
     Download the Dialog file associated with the given Dialog application.
     
     - parameter dialogID: The Dialog application identifier.
     - parameter format: The format of the file. The format can be either OctetStream
            (.mct file), Watson Dialog document JSON format (.json file), or Watson
            Dialog document XML format (.xml file).
     - parameter completionHandler: A function invoked with the response from Watson.
     */
    public func getDialogFile(dialogID: DialogID, format: MediaType? = nil,
        completionHandler: (NSURL?, NSError?) -> Void)
    {
        // TODO: Update this function after WatsonGateway supports uploads
        authStrategy.refreshToken() { error in
            var headerParams = [String: String]()
            if let token = self.authStrategy.token {
                headerParams["X-Watson-Authorization-Token"] = token
            }

            let request = WatsonRequest(
                method: .GET,
                serviceURL: Constants.serviceURL,
                endpoint: Constants.dialogID(dialogID),
                authStrategy: self.authStrategy,
                accept: format,
                headerParams: headerParams)

            var fileURL: NSURL?
            let req = Alamofire.download(request) { (temporaryURL, response) in
                let manager = NSFileManager.defaultManager()
                let directoryURL = manager.URLsForDirectory(.DocumentDirectory,
                    inDomains: .UserDomainMask)[0]
                let pathComponent = response.suggestedFilename
                fileURL = directoryURL.URLByAppendingPathComponent(pathComponent!)
                return fileURL!
            }

            req.response { _, response, _, error in
                var data: NSData? = nil
                if let file = fileURL?.path { data = NSData(contentsOfFile: file) }
                let includeFileURL = { error in completionHandler(fileURL, error) }
                validate(response, data: data, error: error, serviceError: DialogError(),
{'fraction_non_alphanumeric': 0.07034689631815763, 'fraction_numerical': 0.0016449755672746625, 'mean_word_length': 2.0813953488372094, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 6}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5987863', 'n_tokens_mistral': 4876, 'n_tokens_neox': 4671, 'n_words': 1676}