/*
 *  Copyright © 2016 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 UIKit
import EnterpriseLib

class ServerLoginView: UIViewController, UIWebViewDelegate {
    
    @IBOutlet var FirstViewOutlet: UIView!
    
    @IBOutlet weak var oracleLogoImageViewOutlet: UIImageView! {
        didSet {
            oracleLogoImageViewOutlet.image = UIImage(named: "oracle_iot.png")
        }
    }
    
    @IBOutlet weak var headLineLabelOutlet: UILabel!
    
    @IBOutlet weak var urlTextFieldOutlet: UITextField! {
        didSet {
            urlTextFieldOutlet.layer.cornerRadius = 6.0
            urlTextFieldOutlet.layer.borderColor = UIColor.lightGray.cgColor
            urlTextFieldOutlet.layer.borderWidth = 1.0
            urlTextFieldOutlet.layer.backgroundColor = UIColor(white: 0.90,
                                                               alpha: 1.0).cgColor
        }
    }
    
    @IBOutlet weak var loadUrlButtonOutlet: UIButton! {
        didSet {
            loadUrlButtonOutlet.layer.cornerRadius = 6.0
            loadUrlButtonOutlet.layer.borderColor = UIColor.lightGray.cgColor
            loadUrlButtonOutlet.layer.borderWidth = 1.0
            loadUrlButtonOutlet.layer.backgroundColor = UIColor(white: 0.90,
                                                                alpha: 1.0).cgColor
        }
    }
    
    @IBOutlet weak var webViewOutlet: UIWebView! {
        didSet {
            webViewOutlet.layer.cornerRadius = 6.0
            webViewOutlet.layer.borderColor = UIColor.lightGray.cgColor
            webViewOutlet.layer.borderWidth = 1.0
            webViewOutlet.layer.backgroundColor = UIColor(white: 0.90,
                                                          alpha: 1.0).cgColor
        }
    }

    @objc var loadStatus: String = ""
    @objc var redirectOAM: Bool = false
    @objc var redirectURL: String = ""
    @objc let defaults = UserDefaults.standard
    
    @objc var running:Bool = false
    
    // Load URL in a UIWebView for user authentication
    override func viewDidLoad() {
        
        initializeEnterpriseClient()
        if self.running {
            return
        }
        
        super.viewDidLoad()
        
        UIGraphicsBeginImageContext(CGSize(width: self.view.frame.size.width,
                                           height: self.view.frame.size.width - 18))
        UIImage(named: "cloudes-sw.png")?.drawAsPattern(in: self.view.bounds)
        let image : UIImage = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()
        FirstViewOutlet.backgroundColor = UIColor(patternImage: image)
        
        let url = URL (string: serverHostName)
        urlTextFieldOutlet.text = url!.description
        webViewOutlet.scalesPageToFit = true
        webViewOutlet.isUserInteractionEnabled = true
        webViewOutlet.scrollView.isScrollEnabled = true
        
        webViewOutlet.delegate = self
        
        
    }
    
    override func viewWillAppear (_ animated: Bool) {
    
        self.navigationItem.setHidesBackButton(true, animated: false)
        headLineLabelOutlet.font = .boldSystemFont(ofSize: 18.0)
        headLineLabelOutlet.text = kEnterpriseHeadLine
        loadUrlButtonOutlet.setTitle(kLoadUrl, for: UIControlState())
        urlTextFieldOutlet.placeholder = kUrlTextFieldHint

        super.viewWillAppear(animated)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    func webView(_ webView: UIWebView, didFailLoadWithError error: Error) {
        print("WebView fail with error \(error)")
    }
    
    func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest,
                 navigationType: UIWebViewNavigationType) -> Bool {

        if self.redirectOAM == true && self.redirectURL.isEmpty == true {
            self.redirectURL = (request.url?.absoluteString)!
            defaults.setValue(self.redirectURL, forKey: "requestURL")
            defaults.synchronize()
        }
        
        if request.url?.absoluteString != self.urlTextFieldOutlet.text {
            self.loadStatus = "Redirect"
        } else {
            if self.loadStatus.isEmpty == false {
                self.loadStatus = "Authenticated"
            }
        }
        
        return true
    }
    
    func webViewDidStartLoad(_ webView: UIWebView) {
        if self.redirectOAM == false {
            self.redirectOAM = true
        }
    }
    
    func webViewDidFinishLoad(_ webView: UIWebView) {
        if self.loadStatus == "Authenticated" {
            // Go to application selection screen
            let secondViewController = self.storyboard?.instantiateViewController(
                withIdentifier: "ApplicationSelectionView") as! ApplicationSelectionView
            self.navigationController?.pushViewController(secondViewController,
                                                          animated: true)
        } else {
            if self.loadStatus.isEmpty == true {
                // Get cookies
                let storage = HTTPCookieStorage.shared
                for cookie in storage.cookies! {
                    storage.deleteCookie(cookie)
                }
                
                let getURL = defaults.string(forKey: "requestURL")
                defaults.setValue(self.redirectURL, forKey: "requestURL")
                defaults.synchronize()
                
                
                let url = URL (string: getURL!)
                let requestObject = URLRequest(url: url!)
                webViewOutlet.loadRequest(requestObject)
            }
        }
    }

    // actions

    // Load URL button action for user authentication 
    @IBAction func onUrlLoadButton(_ sender: UIButton) {
        let url = URL (string: urlTextFieldOutlet.text!)
        let requestObject = URLRequest(url: url!)
        webViewOutlet.loadRequest(requestObject)
    }

    @objc func initializeEnterpriseClient() {
        var errorMessage = String()
        self.startSecondViewController(callback: { eclClient, error in
            if error != nil {
                errorMessage = "Enterprise Client object has not been created."
                print(error as Any)
                showError(controller: self,
                          alertTitle: "Enterprise Client Alert",
                          message: errorMessage, actionTitle: "OK",
                          handler: { (action:UIAlertAction?) -> Void in
                            exit(0)
                })
            }
        })
        
        while self.running == false {
            RunLoop.current.run(until: Date(timeIntervalSinceNow: 1.0))
        }
    }
    
    // Create the enterprise client and choose the appropriate authentication
    // scheme depending on truststore content
    private func startSecondViewController(callback: @escaping (EnterpriseClient?, ClientError?) -> ()) {
        var errorMessage = String()
        
        func handleUserAuthError(ecl: EnterpriseClient?, error: ClientError,
                                 handler: ((UIAlertAction) -> Void)?) -> Bool {
            let autherr = NSLocalizedString(
                "User Authentication failed. Session cookie has expired.",
                comment: "")
            
            if "\(error)".range(of: autherr ) != nil {
                if let ecl = ecl {
                    do {
                        try ecl.close()
                    } catch {
                        print("\(error)")
                    }
                }
                
                showError(controller: self, alertTitle: "User Auth Alert",
                          message: autherr, actionTitle: "Login",
                          handler: { (action:UIAlertAction?) -> () in
                            /*
                            let firstViewController = self.storyboard?.instantiateViewController(
                                withIdentifier: "ServerLoginView") as! ServerLoginView
                            self.navigationController?.pushViewController(firstViewController,
                                                                          animated: false)
                            */
                            print("ServerLoginView is loaded")
                })
                return true
            } else {
                print(error)
                showError(controller: self, alertTitle: "Device Model Alert",
                          message: "\(error)", actionTitle: "OK",
                          handler: handler)
                return false
            }
        }
        
        // Create the enterprise client using empty application name.
        EnterpriseClient.newClient(appName: "", path: Provisioning.getProvisioningFilePath(),
                password: Provisioning.getProvisioningPassword(), callback: { eclClient, error in
            if ((error != nil) || (eclClient == nil)) {
                // An error has encountered during enterprise client creation.
                let _ = handleUserAuthError(ecl: eclClient, error: error!,
                        handler: { (action:UIAlertAction?) -> () in
                            exit(0)
                })
                
                callback(nil, error)
                self.running = true
                return
            }
            // Enterprise client is created
            // If the name is empty there has been an error
            if eclClient!.getApplication().getName().isEmpty {
                    print(error!)
                    showError(controller: self,
                              alertTitle: "Enterprise Client Alert",
                              message: "Enterprise Client object has not been created.",
                              actionTitle: "OK", handler: nil)
                return
            }
            ecl = eclClient
            var dmNumber = 0
            // Get device model list associated to the application.
            eclClient!.getDeviceModels(callback: { (pageable, error) in
                if let error = error {
                    // An error has encountered during getting the device model list.
                    let _ = handleUserAuthError(ecl: eclClient, error: error,
                                                handler: nil)
                    callback(nil, error)
                    self.running = true
                    return
                }
                
                // Process the device model recursively.
                func recursivePaging (pageable: Pageable<String>?,
                                      error: ClientError?) {
                    do {
                        if let error = error {
                            // An error has encountered during the
                            // device model paging.
                            let _ = handleUserAuthError(ecl: eclClient,
                                                        error: error,
                                                        handler: nil)
                            callback(nil, error)
                            self.running = true
                            return
                        }
                        // If there are no device models to process return
                        if (pageable!.size() == 0) {
                            return
                        }
                        
                        for urn: String in pageable!.elements() {
                            // Get details for one single device model.
                            try eclClient!.getDeviceModel(deviceModelUrn: urn,
                                    callback: { deviceModel, error in
                                if (error == nil) {
                                    if let dmObject = deviceModel {
                                        appDeviceModelsList.append(dmObject)
                                        dmArray.append(dmObject.getName())
                                        urnArray.append(dmObject.getURN())
                                        
                                        dmNumber += 1
                                        if dmNumber == pageable!.size() {
                                            DispatchQueue.main.async(execute: {
                                            let thirdViewController = self.storyboard?.instantiateViewController(
                                                withIdentifier: "DeviceModelSelectionView") as! DeviceModelSelectionView
                                            self.navigationController?.pushViewController(thirdViewController, animated: true)
                                            })
                                            
                                            callback(ecl, nil)
                                            self.running = true
                                            return
                                        }
                                    }
                                } else {
                                    // An error has encountered during 
                                    // getting the device model details.
                                    if handleUserAuthError(ecl: eclClient,
                                                           error: error!,
                                                           handler: nil) {
                                        appDeviceModelsList.removeAll()
                                        dmArray.removeAll()
                                        urnArray.removeAll()
                                    }
                                    
                                    callback(nil, error)
                                    self.running = true
                                    return
                                }
                            })
                        }
                        
                        if (pageable!.hasMore()) {
                            try pageable!.next(callback: recursivePaging)
                        } else {
                            return
                        }
                    } catch  {
                        print(error)
                        showError(controller: self,
                                  alertTitle: "Device Model Alert",
                                  message: "\(error)",
                            actionTitle: "OK", handler: nil)
                    }
                }
                recursivePaging(pageable: pageable, error: error)
            })
        })
    }

}
