/*
 *  Copyright © 2016, 2017, Oracle and/or its affiliates. All rights reserved.
 *  This software is dual-licensed to you under the MIT License (MIT) and
 *  the Universal Permissive License (UPL). See the LICENSE file in the root
 *  directory for license terms. You may choose either license, or both.
 */

import Foundation
import UIKit

// A filename saved to the Documents directory as a property list that
// preserves the last used provisioning file information
fileprivate let kProvPlist = "prov.plist"
// The name of the pre bundled provisioning file information
fileprivate let kTrustStorePlist = "TrustStore.plist"

fileprivate let dateFormatter = DateFormatter()

func showError(controller: UIViewController, alertTitle: String,
        message: String, actionTitle: String,
        handler: ((UIAlertAction) -> ())?) {
    
    let alertController = UIAlertController(title: alertTitle, message:
        message, preferredStyle: .alert)
    let defaultAction = UIAlertAction(title: actionTitle, style: .default,
                                      handler: handler)
    alertController.addAction(defaultAction)
    
    DispatchQueue.main.async() {
        controller.present(alertController, animated: true, completion: nil)
    }
    return
}

public enum SettingError : Error {
    case setting(message: String)
}

// Convenience for the return type methods that return provisioning file
// information
typealias StaticSettings = (path: String, password: String)

// TODO: Localize message strings.
/*
 * Try and load a bundled settings file in the bundle directory `kTrustStorePlist`
 * The file defined in the settings file must also be in the bundle directory
 * Return a path and password for a provisioning file
 */
func loadBundledSettings() throws -> StaticSettings? {
    guard let url = Bundle.main.url(forResource: kTrustStorePlist,
                                    withExtension: nil) else {
                                        return nil
    }
    guard let dict = getPropertyList(url: url) else {
        return nil
    }
    guard let filename:String = dict["filename"] as? String else {
        return nil
    }
    if filename == "" {
        return nil
    }
    guard let passphrase:String = dict["password"] as? String else {
        throw SettingError.setting(message: "No password in configuration file \(kTrustStorePlist)")
    }
    if passphrase == "" {
        throw SettingError.setting(message: "No password in configuration file \(kTrustStorePlist)")
    }
    
    // TODO: These should be removed. No need for extension or subdirectory.
    let fileextension:String? = dict["fileextension"] as? String ?? ""
    let subdirectory:String? = dict["subdirectory"] as? String  ?? ""
    
    if let filepath = Bundle.main.path(forResource: filename,
                                       ofType: fileextension,
                                       inDirectory: subdirectory) {
        return (filepath, passphrase)
    } else {
        throw SettingError.setting(message:
            "No valid path for the provisioning file \(filename)")
    }
}

// Return the full path to the "prov.plist" (kProvPlist) file. This
// file is recorded as a relative path. The location of the documents
// directory changes, especially in the simulator.
func lastSettingsFile() -> URL {
    let dd:URL = FileManager.default.urls(for: .documentDirectory,
                                          in: .userDomainMask)[0] as URL
    return dd.appendingPathComponent(kProvPlist, isDirectory: false)
}

func getDocumentPath(file: String) -> String {
    let dd:URL = FileManager.default.urls(for: .documentDirectory,
                                          in: .userDomainMask)[0] as URL
    return dd.appendingPathComponent(file, isDirectory: false).path
}

// TODO: Localize message strings
/*
 * Try and load a bundled settings file in the documents directory `kProvPlist`.
 * The file defined in the settings file is assumed to be in the documents directory.
 * Construct that full path and return it.
 * Return a path and password for a provisioning file
 */
func loadLastSettings() throws -> StaticSettings? {
    let url = lastSettingsFile()
    guard let dict = getPropertyList(url: url) else {
        return nil
    }
    guard var filename:String = dict["path"] as? String else {
        return nil
    }
    if filename == "" {
        return nil
    } else {
        filename = (FileManager.default.urls(for: .documentDirectory,
                                             in: .userDomainMask)[0] as URL)
            .appendingPathComponent(filename, isDirectory: false).path
    }
    guard let passphrase:String = dict["password"] as? String else {
        throw SettingError.setting(message: "No password in configuration file \(kProvPlist)")
    }
    if passphrase == "" {
        throw SettingError.setting(message: "No password in configuration file \(kProvPlist)")
    }
    return (filename, passphrase)
}

func removeLastSettingsFile() {
    removeFile(file: lastSettingsFile().path)
}

func saveSettingsFile(path: String, password: String) {
    let url = lastSettingsFile()
    var dict: Dictionary<String, Any> =
        ["XInitializerItem" : "DoNotEverChangeMe"]
    dict["path"] = path
    dict["password"] = password
    
    var err:NSError?
    if let out: OutputStream = OutputStream(url: url, append: false) {
        out.open()
        PropertyListSerialization.writePropertyList(dict,to: out,
            format: PropertyListSerialization.PropertyListFormat.binary,
            options: PropertyListSerialization.WriteOptions(
                Data.WritingOptions.atomic.rawValue),
            error: &err)
        if (err != nil) {
            print(err!)
        }
        defer {
            out.close()
        }
    } else {
        print("Cannot save property list \(url)")
    }
}

// Should be configurable
func getImageDir() -> String {
    let dir = getDocumentPath(file:"downloads")
    return dir + "/"
}

@discardableResult func removeFile(file: String) -> Bool {
    if !FileManager.default.fileExists(atPath: file) {
        return true
    }
    do {
        try FileManager.default.removeItem(atPath: file)
        return true
    } catch {
        print(error)
        return false
    }
}

func formatDate(date:Date) -> String {
    dateFormatter.timeStyle = .medium
    dateFormatter.dateStyle = .medium
    return dateFormatter.string(from: date)
}

// Used in ProvisioningView.swift only referenced in EnterpriseClientSample
// But other than that only in Utils.swift can be fileprivate
fileprivate func getPropertyList(url: URL) -> Dictionary<String, Any>? {
    do {
        if !FileManager.default.fileExists(atPath: url.path) {
            return nil
        }
        let data = try Foundation.Data(contentsOf: url)
        let plist = try PropertyListSerialization.propertyList(from: data,
                                                               options: .mutableContainers, format: nil)
        return (plist as? Dictionary<String, Any>)!
    } catch {
        print(error)
        return nil
    }
}

