// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.

import Foundation
import MediaPlayer
import SessionUIKit
import NVActivityIndicatorView

// A modal view that be used during blocking interactions (e.g. waiting on response from
// service or on the completion of a long-running local operation).
public class ModalActivityIndicatorViewController: OWSViewController {
    let canCancel: Bool
    let message: String?
    private let onAppear: (ModalActivityIndicatorViewController) -> Void

    private var hasAppeared: Bool = false
    public var wasCancelled: Bool = false
    
    lazy var dimmingView: UIView = {
        let result = UIVisualEffectView()
        
        ThemeManager.onThemeChange(observer: result) { [weak result] theme, _ in
            result?.effect = UIBlurEffect(
                style: (theme.interfaceStyle == .light ?
                    UIBlurEffect.Style.systemUltraThinMaterialLight :
                    UIBlurEffect.Style.systemUltraThinMaterial
                )
            )
        }
        
        return result
    }()
    
    private lazy var spinner: NVActivityIndicatorView = {
        let result: NVActivityIndicatorView = NVActivityIndicatorView(
            frame: CGRect.zero,
            type: .circleStrokeSpin,
            color: .white,
            padding: nil
        )
        result.set(.width, to: 64)
        result.set(.height, to: 64)
        
        ThemeManager.onThemeChange(observer: result) { [weak result] theme, _ in
            guard let textPrimary: UIColor = theme.color(for: .textPrimary) else { return }
            
            result?.color = textPrimary
        }
        
        return result
    }()

    var wasDimissed: Bool = false

    // MARK: - Initializers

    @available(*, unavailable, message:"use other constructor instead.")
    public required init?(coder aDecoder: NSCoder) {
        notImplemented()
    }

    public required init(
        canCancel: Bool = false,
        message: String? = nil,
        onAppear: @escaping (ModalActivityIndicatorViewController) -> Void
    ) {
        self.canCancel = canCancel
        self.message = message
        self.onAppear = onAppear
        
        super.init(nibName: nil, bundle: nil)
        
        // Present this modal _over_ the current view contents.
        self.modalPresentationStyle = .overFullScreen
        self.modalTransitionStyle = .crossDissolve
    }

    public class func present(
        fromViewController: UIViewController?,
        canCancel: Bool = false,
        message: String? = nil,
        onAppear: @escaping (ModalActivityIndicatorViewController) -> Void
    ) {
        guard let fromViewController: UIViewController = fromViewController else { return }
        
        AssertIsOnMainThread()
        
        fromViewController.present(
            ModalActivityIndicatorViewController(canCancel: canCancel, message: message, onAppear: onAppear),
            animated: false
        )
    }

    public func dismiss(completion: @escaping () -> Void) {
        guard Thread.isMainThread else {
            DispatchQueue.main.async { [weak self] in
                self?.dismiss(completion: completion)
            }
            return
        }

        if !wasDimissed {
            // Only dismiss once.
            self.dismiss(animated: false, completion: completion)
            wasDimissed = true
        }
        else {
            // If already dismissed, wait a beat then call completion.
            DispatchQueue.main.async {
                completion()
            }
        }
    }

    public override func loadView() {
        super.loadView()

        self.view.themeBackgroundColor = .clear
        
        self.view.addSubview(dimmingView)
        dimmingView.pin(to: self.view)
        
        if let message = message {
            let messageLabel: UILabel = UILabel()
            messageLabel.font = .systemFont(ofSize: Values.mediumFontSize)
            messageLabel.text = message
            messageLabel.themeTextColor = .textPrimary
            messageLabel.numberOfLines = 0
            messageLabel.textAlignment = .center
            messageLabel.lineBreakMode = .byWordWrapping
            messageLabel.set(.width, to: UIScreen.main.bounds.width - 2 * Values.mediumSpacing)
            
            let stackView = UIStackView(arrangedSubviews: [ messageLabel, spinner ])
            stackView.axis = .vertical
            stackView.spacing = Values.largeSpacing
            stackView.alignment = .center
            self.view.addSubview(stackView)
            
            stackView.center(in: self.view)
        }
        else {
            self.view.addSubview(spinner)
            spinner.autoCenterInSuperview()
        }

        if canCancel {
            let cancelButton: SessionButton = SessionButton(style: .destructive, size: .large)
            cancelButton.setTitle(CommonStrings.cancelButton, for: .normal)
            cancelButton.addTarget(self, action: #selector(cancelPressed), for: .touchUpInside)
            self.view.addSubview(cancelButton)
            
            cancelButton.center(.horizontal, in: self.view)
            cancelButton.pin(.bottom, to: .bottom, of: self.view, withInset: -50)
            cancelButton.set(.width, to: Values.iPadButtonWidth)
        }

        // Hide the modal until the presentation animation completes.
        self.view.layer.opacity = 0.0
    }

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

        self.spinner.startAnimating()

        // Fade in the modal
        UIView.animate(withDuration: 0.35) {
            self.view.layer.opacity = 1.0
        }
    }
    
    public override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        
        if !self.hasAppeared {
            self.hasAppeared = true
            
            DispatchQueue.global().async {
                self.onAppear(self)
            }
        }
    }

    @objc func cancelPressed() {
        AssertIsOnMainThread()

        wasCancelled = true

        dismiss { }
    }
}
