//
//  Copyright © 2018 WireGuard LLC. All rights reserved.
//

import UIKit
import CoreData
import NetworkExtension

import BNRCoreDataStack
import PromiseKit

protocol TunnelInfoTableViewControllerDelegate: class {
    func connect(tunnel: Tunnel, tunnelInfoTableViewController: TunnelInfoTableViewController)
    func disconnect(tunnel: Tunnel, tunnelInfoTableViewController: TunnelInfoTableViewController)
    func configure(tunnel: Tunnel, tunnelInfoTableViewController: TunnelInfoTableViewController)
    func showSettings()
    func status(for tunnel: Tunnel, tunnelInfoTableViewController: TunnelInfoTableViewController) -> NEVPNStatus
}

class TunnelInfoTableViewController: UITableViewController {

    @IBOutlet weak var editButton: UIBarButtonItem!

    private var viewContext: NSManagedObjectContext!
    private weak var delegate: TunnelInfoTableViewControllerDelegate?
    private var tunnel: Tunnel!

    func configure(context: NSManagedObjectContext, delegate: TunnelInfoTableViewControllerDelegate? = nil, tunnel: Tunnel) {
        viewContext = context
        self.delegate = delegate
        self.tunnel = tunnel
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        // Get rid of seperator lines in table.
        tableView.tableFooterView = UIView(frame: CGRect.zero)

        NotificationCenter.default.addObserver(self,
                                               selector: #selector(VPNStatusDidChange(notification:)),
                                               name: .NEVPNStatusDidChange,
                                               object: nil)
    }

    override func viewWillAppear(_ animated: Bool) {
        super .viewWillAppear(animated)

        self.tableView.reloadData()
    }

    override func numberOfSections(in tableView: UITableView) -> Int {
        return 2
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        switch section {
        case 1:
            return tunnel?.peers?.count ?? 0
        default:
            return 1
        }
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        switch indexPath.section {
        case 0:
            let cell = tableView.dequeueReusableCell(type: InterfaceInfoTableViewCell.self, for: indexPath)
            cell.delegate = self
            cell.configure(model: tunnel.interface, status: delegate?.status(for: tunnel, tunnelInfoTableViewController: self) ?? .invalid)
            return cell
        default:
            let cell =  tableView.dequeueReusableCell(type: PeerInfoTableViewCell.self, for: indexPath)
            if let peer = tunnel.peers?.object(at: indexPath.row) as? Peer {
                cell.peer = peer
            } else {
                let peer = Peer(context: tunnel.managedObjectContext!)
                tunnel.addToPeers(peer)
                cell.peer = peer
            }
            return cell
        }
    }

    @IBAction func showSettings(_ sender: Any) {
        delegate?.showSettings()
    }

    @IBAction func editTunnelConfiguration(_ sender: Any) {
        delegate?.configure(tunnel: self.tunnel, tunnelInfoTableViewController: self)
    }

    @objc private func VPNStatusDidChange(notification: NSNotification) {
        guard let session = notification.object as? NETunnelProviderSession else {
            return
        }

        guard let prot = session.manager.protocolConfiguration as? NETunnelProviderProtocol else {
            return
        }

        guard let changedTunnelIdentifier = prot.providerConfiguration?[PCKeys.tunnelIdentifier.rawValue] as? String else {
            return
        }

        guard tunnel.tunnelIdentifier == changedTunnelIdentifier else {
            return
        }

        self.tableView.reloadRows(at: [IndexPath(row: 0, section: 0)], with: .none)
    }
}

extension TunnelInfoTableViewController: InterfaceInfoTableViewCellDelegate {
    func connect(tunnelIdentifier: String) {
        delegate?.connect(tunnel: tunnel, tunnelInfoTableViewController: self)
    }

    func disconnect(tunnelIdentifier: String) {
        delegate?.disconnect(tunnel: tunnel, tunnelInfoTableViewController: self)
    }
}

protocol InterfaceInfoTableViewCellDelegate: class {
    func connect(tunnelIdentifier: String)
    func disconnect(tunnelIdentifier: String)
}

class InterfaceInfoTableViewCell: UITableViewCell {
    weak var delegate: InterfaceInfoTableViewCellDelegate?
    private var model: Interface! {
        didSet {
            nameField.text = model.tunnel?.title
            addressesField.text = model.addresses
            publicKeyField.text = model.publicKey
        }
    }

    func configure(model: Interface!, status: NEVPNStatus) {
        self.model = model

        if status == .connecting || status == .disconnecting || status == .reasserting {
            activityIndicator.startAnimating()
            tunnelSwitch.isHidden = true
        } else {
            activityIndicator.stopAnimating()
            tunnelSwitch.isHidden = false
        }

        tunnelSwitch.isOn = status == .connected
        tunnelSwitch.onTintColor = status == .invalid || status == .reasserting ? .gray : .green
        tunnelSwitch.isEnabled = true
    }

    @IBAction func tunnelSwitchChanged(_ sender: Any) {
        tunnelSwitch.isEnabled = false

        guard let tunnelIdentifier = model.tunnel?.tunnelIdentifier else {
            return
        }

        if tunnelSwitch.isOn {
            delegate?.connect(tunnelIdentifier: tunnelIdentifier)
        } else {
            delegate?.disconnect(tunnelIdentifier: tunnelIdentifier)
        }
    }

    @IBOutlet weak var nameField: UILabel!
    @IBOutlet weak var addressesField: UILabel!
    @IBOutlet weak var publicKeyField: CopyableLabel!
    @IBOutlet weak var tunnelSwitch: UISwitch!
    @IBOutlet weak var activityIndicator: UIActivityIndicatorView!
}

class PeerInfoTableViewCell: UITableViewCell {
    var peer: Peer! {
        didSet {
            publicKeyField.text = peer.publicKey
            allowedIpsField.text = peer.allowedIPs
            endpointField.text = peer.endpoint
        }
    }

    @IBOutlet weak var publicKeyField: CopyableLabel!
    @IBOutlet weak var allowedIpsField: UILabel!
    @IBOutlet weak var endpointField: UILabel!
    @IBOutlet weak var copiedStatusLabel: UILabel!

}

extension TunnelInfoTableViewController: Identifyable {}
extension InterfaceInfoTableViewCell: Identifyable {}
extension PeerInfoTableViewCell: Identifyable {}
