// RGBiOSUtilities

//

// Use at your own risk, no guarantees..

//

// Updated (headerdoc added) on 30-01-2018

//

// Updated May 2023

//

//

import Foundation

import UIKit

//MARK: notifyUser(_:,alertTitle: alertMessage:ok:)


/// Shows an alert for the given controller. Requires a title and an alert message; only shows an OK button (no Cancel or other buttons)

///

/// - Parameters:

///   - controller: the ViewController inside which this alert is to be shown

///   - title: The title of the alert, at the top of the dialog

///   - alertMessage: The message to be shown in the alert, right below the title in a smaller script

///   - runOnOK: The function or closure to be called once the dialog ends (because the user taps OK)

public func notifyUser(_ controller:UIViewController,

                       alertTitle title:String,

                       alertMessage:String,

                       runOnOK:@escaping (UIAlertAction) -> Void) {

    

    let alertViewController = UIAlertController(title: title, message: alertMessage, preferredStyle: .alert)

    

    let okAction = UIAlertAction(title: "OK", style: .default, handler: runOnOK)

    

    alertViewController.addAction(okAction)

    

    controller.present(alertViewController, animated: true, completion: nil)

}


//public func doSomethingOnOK(alert:UIAlertAction) {

//    // Put this function in the class that calls notifyUser()

//    // Do whatever is needed

//}


//MARK: userConfirmsAction(_:,alertTitle:, alertMessage:, runOnConfirm:)


/// Shows an user-confirmed alert for this app

/// Shows an alert for the given controller. Requires a title and an alert message; the dialog shows both an OK button and a Cancel button. When one of these is tapped, the dialog closes and ither the runOnConfirm: or the runOnCancel: functions or closures are executed. Both may be { _ in }, that is, do nothing.

///

/// - Parameters:

///   - controller: The ViewController inside which this alert is to be shown

///   - title: The main title of the alert, shown in bold lettering at the top of the dialog

///   - alertMessage: The information shown in the alert, right below the main title

///   - runOnConfirm: A function or closure to be run when the user taps OK. It can be empty, { _ in }

///   - runOnCancel: A function or closure to be run when the user taps Cancel.It can be empt, { _ in }

public func userConfirmsAction(_ controller:UIViewController,

                               alertTitle title:String,

                               alertMessage:String,

                               runOnConfirm: @escaping (UIAlertAction) -> (),

                               runOnCancel: @escaping (UIAlertAction) -> ()) {

    

    let alertViewController = UIAlertController(title:title,

                                                message: alertMessage,

                                                preferredStyle: UIAlertController.Style.alert)

    

    alertViewController.addAction(UIAlertAction(title: "Confirm",

                                                style: .destructive,

                                                handler: runOnConfirm ))

    

    alertViewController.addAction(UIAlertAction(title: "CANCEL",

                                                style: .cancel,

                                                handler: runOnCancel))

    

    controller.present(alertViewController, animated: true, completion: nil)

    

}


//func doSomethingOnConfirm(alert:UIAlertAction) {

//    // Put this function in the class that calls userConfirmsAction()

//    // Do whatever is needed on confirm

//}


//public func doSomethingOnCancel(alert:UIAlertAction) {

//    // Put this function in the class that calls userConfirmsAction()

//    // Do whatever is needed on cancel

//}


/// Address of the tmp folder as an URL for this app

///

/// - Returns: An URL that points to the tmp directory for this app (inside its sandbox). We are working in iOS.

public func tmpURL() -> URL {

    if #available(iOS 10.0, *) {

        let dfm = FileManager.default

        return dfm.temporaryDirectory

    } else {

        // Fallback on earlier versions

        return URL(string: NSTemporaryDirectory())!

    }

}


/// Address of the tmp directory for this app. We are working in iOS.

///

/// - Returns: A String that points to the tmp directory for this app (inside its sandbox) We are working in iOS.

public func tmpPath() -> String{

    return tmpURL().path

}


/// Address of the Documents folder as an URL for this app

///

/// - Returns: An URL that points to the Documents folder for this app (inside its sandbox). We are working in iOS.

public func documentsURL() -> URL {

    let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)

    let documentsDirectory = paths[0]

    return documentsDirectory

}


/// Address of the Documents path as a String for this app for sandboxing

///

/// - Returns: A String that points to the Documents folder for this app (inside its sandbox). We are working in iOS.

public func documentsPath() -> String {

    return documentsURL().path

}


/// Address of the Library folder for this app as an URL

///

/// - Returns: An URL that points to the Library folder for this app (inside its sandbox). We are working in iOS.

public func libraryURL() -> URL {

    let dfm = FileManager.default

    return dfm.urls(for: FileManager.SearchPathDirectory.libraryDirectory,

                    in: FileManager.SearchPathDomainMask.userDomainMask).first!

    

}


/// Address of the Library folder for this app as a String

///

/// - Returns: A String that points to the Library folder for this app (inside its sandbox). We are working in iOS.

public func libraryPath() -> String {

    return libraryURL().path

}


/// Returns the name and extension of a given filename

///

/// - Parameter total: The given filename, in the form "name.extension"

/// - Returns: A [String] whose first element is the file name and whose second element is the extension as given in the input parameter.

public func splitIntoNameAndExtension(total:String) -> [String]? {

    

    let parts = total.components(separatedBy: ".")

    

    guard

        parts.count == 2,

        let name = parts.first,

        !name.isEmpty,

        let ext = parts.last,

        !ext.isEmpty

        else

    { return nil }

    

    return [name, ext]

}


/// Is object at the given path a directory?

///

/// - Parameter path: A String path to some object

/// - Returns: True if the object is a directory, false otherwise

public func isDirectory(path: String) -> Bool {

    let dfm = FileManager.default

    var isDir: ObjCBool = false;

    dfm.fileExists(atPath: path, isDirectory: &isDir)

    return isDir.boolValue;

}


/// Is the object at the given URL a directory?

///

/// - Parameter url: The URL of some object

/// - Returns: True if the object is a directory, false otherwise

public func isDirectory(url: URL) -> Bool {

    let dfm = FileManager.default

    var isDir: ObjCBool = false;

    dfm.fileExists(atPath: url.path, isDirectory: &isDir)

    return isDir.boolValue;

}


/// Is the object with the given URL a directory? Does it exist?

///

/// - Parameter url: The URL to some object

/// - Returns: A tuple whose first value is true if the object exists, or false otherwise, and whose second element is true if the object is a directory, or false otherwise.

public func existsDirAt(url: URL) -> (fileExists:Bool, fileIsDirectory:Bool) {

    let dfm = FileManager.default

    var isDir: ObjCBool = false;

    let exists = dfm.fileExists(atPath: url.path, isDirectory: &isDir)

    return (exists, isDir.boolValue);

    

}


//MARK: isItHidden?

/// Returns whether the given URL belongs to a hidden object (file or directory)

///

/// - Parameter url: The URL to some object

/// - Returns: true if the object is hidden, false otherwise

public func isHidden(url : URL) -> Bool {

    return url.lastPathComponent.hasPrefix(".")

}


//MARK: download()?

///   Asynchronously downloads thisFile (a remote URL) and stores it locally toThisURL (a local URL).

///    A callback must be provideded, which receives the UIViewControlelr (for notifyUser()), the error message and the

///    URL of the locally saved file.

///    See an example called completion, below.

///

/// - Parameter onView: The UIViewController from which the download() is called

/// - Parameter thisFile: The URL of the remote object to be downloaded

/// - Parameter toThisURL: The local URL where the remote object is saved after downloading

/// - Parameter callback: A function that is called upon completion; it must give feedback to the user and gets the URL of the downloaded object (will probably update on the main thread)

/// - Returns: Void


func download(_ onView:UIViewController, thisFile remote: URL, toThisURL local:URL, callback: @escaping (UIViewController,String?, URL) -> Void) {

    

    

    let session = URLSession(configuration: URLSessionConfiguration.default)

    

    let request = URLRequest(url:remote)

    

    let task = session.downloadTask(with: request) { tempLocalUrl, response, error in

        if let tmpURL = tempLocalUrl, error == nil {

            // State may be useful

            //            if let status = (response as? HTTPURLResponse)?.statusCode {

            //                print("Successfully downloaded. Status code: \(status)")

            //            }

           

            do {

                if FileManager.default.fileExists(atPath: local.path()) {

                    try FileManager.default.removeItem(at: local)

                    //callback(onView,"File already exists",tmpURL)

                    //return

                }

                try FileManager.default.copyItem(at: tmpURL, to: local)

                callback(onView, nil, local)

            } catch (let writeError) {

                callback(onView,"\(local): \(writeError)", local)

            }

            

        } else {

            callback(onView,error?.localizedDescription, local)

        }

    }

    task.resume()

} // End of download()



/*

 func parseAndShow(fileURL theURL:URL) -> Void {

     // First parse

     do {

         let data = try Data(contentsOf: theURL)

         let pld = PropertyListDecoder()

         let table = try pld.decode([[String]].self, from: data)

         let ppl = table.compactMap { Person($0) }

         persons = ppl

         tableView.reloadData()

     } catch {

         notifyUser(self, alertTitle: "PARSE ERROR", alertMessage: "DECODE UNSUCCESSFUL", runOnOK: { _ in })

     }

     // Now show

 }

 

 func completion(_ viewController: UIViewController,theErrorMessage:String?, localDownloadURL theURL:URL) {

     if let msg = theErrorMessage {

        / UPDATE ON THE MAIN THREAD

         OperationQueue.main.addOperation() {

             notifyUser(viewController, alertTitle: "DOWNLOAD ERROR", alertMessage: "\(msg)", runOnOK: { _ in })

             return

         }

     }

     // If all did go well, do sth with the fle

     OperationQueue.main.addOperation() {

         self.parseAndShow(fileURL: theURL)

     }

 } // End of completion()


 */