//
//  MainViewController.swift
//  GetGeoDemo
//
//  Created by CT-Armin.Budiman on 3/9/20.
//  Copyright © 2020 Cartrack. All rights reserved.
//

import UIKit
import CoreBluetooth
import CartrackBleLockSDK

class MainViewController: UIViewController {

    var bleTerminal: BleTerminal?
    private var isConfigure: Bool = false

    @IBOutlet weak var keyLabel: UILabel!

    @IBOutlet weak var apiKeyTextField: UITextField!
    @IBOutlet weak var vendorIdTextField: UITextField!
    @IBOutlet weak var countryCodeTextField: UITextField!

    @IBOutlet weak var nameTextField: UITextField!
    @IBOutlet weak var fetchKeyButton: UIButton!
    @IBOutlet weak var connectButton: UIButton!
    @IBOutlet weak var disconnectButton: UIButton!

    @IBOutlet weak var apiUrlTextField: UITextField!
    @IBOutlet weak var setApiUrlButton: UIButton!
    @IBOutlet weak var setDefaultApiUrlButton: UIButton!

    @IBOutlet weak var randomKeyButton: UIButton!
    @IBOutlet weak var removeKeyButton: UIButton!
    @IBOutlet weak var lockStateButton: UIButton!
    @IBOutlet weak var unlockButton: UIButton!
    @IBOutlet weak var lockButton: UIButton!
    @IBOutlet weak var headlightButton: UIButton!
    @IBOutlet weak var hornButton: UIButton!

    @IBOutlet weak var lockStateLabel: UILabel!
    @IBOutlet weak var rssiLabel: UILabel!
    @IBOutlet weak var versionLabel: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()

        rssiLabel.text = ""
        setVersionAndBuildNumber()
        showSavedInfo()
        updateViewsState()
    }

    private func isConfigureBleServiceSuccess(with apiUrl: String? = nil) -> Bool {
      guard let apiKey = apiKeyTextField.text?.trimmingCharacters(in: .whitespaces), apiKey.count != 0 else {
        showMessage(title: "", message: "Please enter API Key")
        return false
      }

      guard let vendorId = vendorIdTextField.text?.trimmingCharacters(in: .whitespaces), vendorId.count != 0 else {
        showMessage(title: "", message: "Please enter vendor ID")
        return false
      }

      guard let countryCode = countryCodeTextField.text?.trimmingCharacters(in: .whitespaces), countryCode.count != 0 else {
        showMessage(title: "", message: "Please enter country code")
        return false
      }

      BleService.configure(vendorID: vendorId, apiKey: apiKey, countryCode: countryCode, apiUrl: apiUrl)
      self.isConfigure = true
      self.updateViewsState()

      UserDefaults.standard.set(vendorId, forKey: "vendor_id")
      UserDefaults.standard.set(apiKey, forKey: "api_key")
      UserDefaults.standard.set(countryCode, forKey: "country_code")
      UserDefaults.standard.set(apiUrl, forKey: "api_url")

      return true
    }

    private func setTerminalID() {
      if nameTextField.text?.isEmpty ?? true {
        showMessage(title: "", message: "Please enter a Terminal ID")
        return
      }

      nameTextField.resignFirstResponder()
      bleTerminal = BleService.getTerminal(terminalID: nameTextField.text!)
      bleTerminal?.delegate = self

      UserDefaults.standard.set(nameTextField.text, forKey: "terminal_id")
    }

    private func showSavedInfo() {
      apiKeyTextField.text = UserDefaults.standard.value(forKey: "api_key") as? String ?? ""
      vendorIdTextField.text = UserDefaults.standard.value(forKey: "vendor_id") as? String ?? ""
      countryCodeTextField.text = UserDefaults.standard.value(forKey: "country_code") as? String ?? ""
      nameTextField.text = UserDefaults.standard.value(forKey: "terminal_id") as? String ?? ""
      apiUrlTextField.text = UserDefaults.standard.value(forKey: "api_url") as? String ?? ""
    }

    private func setVersionAndBuildNumber() {
      if let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String,
         let build = Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as? String {          versionLabel.text = "v\(version) (\(build))"
      }
    }

    @IBAction func configureBleServiceButton(_ sender: Any) {
      if self.isConfigureBleServiceSuccess() {
        showMessage(title: "", message: "Configure Ble Service Successfully")
      }
    }

    @IBAction func setApiUrlButtonTapped(_ sender: UIButton) {
        self.apiUrlTextField.resignFirstResponder()
        if self.verifyUrl(urlString: self.apiUrlTextField.text) {
          if self.isConfigureBleServiceSuccess(with: self.apiUrlTextField.text) {
            showMessage(title: "", message: "\(self.apiUrlTextField.text ?? "") set successfully")
          }
        } else {
          showMessage(title: "Invalid URL", message: "Please enter valid URL")
        }
    }

    @IBAction func setDefaultApiUrlButtonTapped(_ sender: UIButton) {
        self.apiUrlTextField.resignFirstResponder()
        if self.isConfigureBleServiceSuccess() {
          showMessage(title: "", message: "Default API URL set successfully")
        }
    }

    @IBAction func fetchKeyButtonTapped(_ sender: Any) {
      self.setTerminalID()
      if bleTerminal?.hasKey ?? false {
        showMessage(title: "Key Received", message: "Successful Receive Key\n Please proceed to connect")
      } else {
        LoaderView.show("Fetching Key")
        bleTerminal?.getAuthKey()
      }
      updateViewsState()
    }
  
    @IBAction func connectButtonTapped(_ sender: Any) {
      self.setTerminalID()
      if bleTerminal?.hasKey ?? false {
        LoaderView.show("Connecting")
        bleTerminal?.connect()
      } else {
        showMessage(title: "Empty Key", message: "Please fetch the key before connect")
      }
      updateViewsState()
    }
   
    @IBAction func disconnectButtonTapped(_ sender: Any) {
        LoaderView.show()
        bleTerminal?.disconnect()
    }

    @IBAction func randomKeyButtonTapped(_ sender: Any) {
      LoaderView.show()
      bleTerminal?.randomAuthKey()
    }

    @IBAction func removeKeyButtonTapped(_ sender: Any) {
      bleTerminal?.removeAuthKey()
      updateViewsState()
      if bleTerminal?.hasKey ?? false {
        showMessage(title: "Remove Key failed", message: "")
      } else {
        showMessage(title: "Remove Key Successful", message: "")
      }
    }

    @IBAction func unlockButtonTapped(_ sender: Any) {
        LoaderView.show()
        bleTerminal?.sendAction(.unlock)
        updateViewsState()
    }
    
    @IBAction func lockButtonTapped(_ sender: Any) {
        LoaderView.show()
        bleTerminal?.sendAction(.lock)
        updateViewsState()
    }
    
    @IBAction func headlightButtonTapped(_ sender: Any) {
        LoaderView.show()
        bleTerminal?.sendAction(.headlight)
        updateViewsState()
    }
    
    @IBAction func hornButtonTapped(_ sender: Any) {
        LoaderView.show()
        bleTerminal?.sendAction(.horn)
        updateViewsState()
    }
    
    @IBAction func lockStateButtonTapped(_ sender: Any) {
        LoaderView.show()
        bleTerminal?.sendAction(.lockState)
        updateViewsState()
    }
    
    private func showMessage(title: String, message: String) {
        let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
        alertController.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))
        self.present(alertController, animated: true, completion: nil)
    }

    private func verifyUrl(urlString: String?) -> Bool {
        if let urlString = urlString {
          if let url = NSURL(string: urlString) {
            return UIApplication.shared.canOpenURL(url as URL)
          }
        }
        return false
    }
    
    private func updateViewsState() {
        let initialized = bleTerminal != nil
        let connectionState = bleTerminal?.connectionState ?? BleConnectionState.disconnected
        self.keyLabel.text = bleTerminal == nil ? "N/A" : bleTerminal?.hasKey ?? false ? "Saved in Keychain" : "N/A"

        fetchKeyButton.isEnabled = connectionState == .disconnected && self.isConfigure
        connectButton.isEnabled = connectionState == .disconnected && self.isConfigure
        disconnectButton.isEnabled = connectionState == .connected
        randomKeyButton.isEnabled = initialized
        removeKeyButton.isEnabled = initialized
        lockStateLabel.text = "Lock State: \(bleTerminal?.lockState ?? .unknown)".capitalized
        
        [unlockButton, lockButton, headlightButton, hornButton, lockStateButton].forEach {
            $0?.isEnabled = initialized && connectionState == .connected
        }
        
    }

}

extension MainViewController: UITextFieldDelegate {
    
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        textField.resignFirstResponder()
        return true
    }
    
}

extension MainViewController: BleTerminalDelegate {
    
    func bleTerminalDidGetKey(terminal: BleTerminal, error: BleKeyError?) {
        updateViewsState()
        LoaderView.hide()
        if let _ = error {
            showMessage(title: "Get Key Error", message: error?.localizedDescription ?? "")
        } else {
            showMessage(title: "Key Received", message: "Successful Receive Key\n Please proceed to connect")
        }
    }
    
    func bleTerminalDidConnect(terminal: BleTerminal, error: BleConnectionError?) {
        if let _ = error {
            showMessage(title: "Connection Error", message: error?.localizedDescription ?? "")
        }
        updateViewsState()
        LoaderView.hide()
    }
    
    func bleTerminalDidAction(terminal: BleTerminal, action: BleAction, error: BleActionError?) {
        
        if let error = error {
            showMessage(title: "Action Failed", message: "[\(action)] \(error.localizedDescription)")
        } else {
            switch action {
            case .lock:
                showMessage(title: "Action", message: "Lock Action Success\nLock state: [\(terminal.lockState)]")
            case .unlock:
                showMessage(title: "Action", message: "Unlock Action Success\nLock state: [\(terminal.lockState)]")
            case .horn:
                showMessage(title: "Action", message: "Horn Action Success")
            case .headlight:
                showMessage(title: "Action", message: "Headlight Action Success")
            case .lockState:
                showMessage(title: "Action", message: "Vehicle's door is \(terminal.lockState)")
            @unknown default:
                break
            }
        }
        self.updateViewsState()
        LoaderView.hide()
    }
    
    func bleTerminalDisconnected(terminal: BleTerminal) {
        showMessage(title: "", message: "Disconnected")
        self.updateViewsState()
        rssiLabel.text = ""
        LoaderView.hide()
    }
    
    func bleTerminalSignalUpdate(rssi: Int, strength: BleSignalStrength) {
        rssiLabel.text = "RSSI: \(rssi) dBm (\(strength))"
    }

    func bleTerminalDidRandomAuthKey(terminal: BleTerminal, error: BleKeyError?) {
      LoaderView.hide()
      updateViewsState()
      if let _ = error {
        showMessage(title: "Random Key Error", message: error?.localizedDescription ?? "")
      } else {
        showMessage(title: "Random Key Successful", message: "")
      }
    }
}

class LoaderView: UIView {

    override init(frame: CGRect) {
        super.init(frame: frame)
        let activityIndicator = UIActivityIndicatorView(style: .whiteLarge)
        activityIndicator.backgroundColor = UIColor.black.withAlphaComponent(0.7)
        activityIndicator.frame = frame
        addSubview(activityIndicator)
        activityIndicator.startAnimating()
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    public static func show(_ text: String = "") {
        hide()
        let window = UIApplication.shared.keyWindow!
        let loaderView = LoaderView(frame: window.bounds)
        let label = UILabel(frame: window.bounds)
        label.text = text
        label.transform = CGAffineTransform(translationX: 0, y: 40)
        label.textAlignment = .center
        label.textColor = .white
        loaderView.addSubview(label)
        window.addSubview(loaderView)
    }
    
    public static func hide() {
        if let window = UIApplication.shared.keyWindow, let loaderView = window.subviews.first(where: { $0 is LoaderView}) {
            loaderView.removeFromSuperview()
        }
    }
    
}

