// 
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import Foundation
import CommonKit
import MatrixSDK
import UIKit

/// A set of user interactors commonly used across the app
enum UserIndicatorType {
    case loading(label: String, isInteractionBlocking: Bool)
    case success(label: String)
    case failure(label: String)
    case custom(label: String, icon: UIImage?)
}

/// A presenter which can handle `UserIndicatorType` by creating the underlying `UserIndicator`
/// and adding it to its `UserIndicatorQueue`
protocol UserIndicatorTypePresenterProtocol {
    /// Present a new type of user indicator, such as loading spinner or success message.
    ///
    /// The presenter will internally convert the type into a `UserIndicator` and add it to its internal queue
    /// of other indicators.
    ///
    /// If the queue is empty, the indicator will be displayed immediately, otherwise it will be pending
    /// until the previously added indicators have completed / been cancelled.
    ///
    /// To remove an indicator, `cancel` or deallocate the returned `UserIndicator`
    func present(_ type: UserIndicatorType) -> UserIndicator
    
    /// The queue of user indicators owned by the presenter
    ///
    /// Clients can access the queue to add custom `UserIndicatorRequest`s
    /// above and beyond those defined by `UserIndicatorType`
    var queue: UserIndicatorQueue { get }
}

class UserIndicatorTypePresenter: UserIndicatorTypePresenterProtocol {
    private let presentationContext: UserIndicatorPresentationContext
    let queue: UserIndicatorQueue
    
    init(presentationContext: UserIndicatorPresentationContext) {
        self.presentationContext = presentationContext
        self.queue = UserIndicatorQueue()
    }
    
    convenience init(presentingViewController: UIViewController) {
        let context = StaticUserIndicatorPresentationContext(viewController: presentingViewController)
        self.init(presentationContext: context)
    }
    
    func present(_ type: UserIndicatorType) -> UserIndicator {
        let request = userIndicatorRequest(for: type)
        return queue.add(request)
    }
    
    private func userIndicatorRequest(for type: UserIndicatorType) -> UserIndicatorRequest {
        switch type {
        case .loading(let label, let isInteractionBlocking):
            if isInteractionBlocking {
                return fullScreenLoadingRequest(label: label)
            } else {
                return loadingRequest(label: label)
            }
        case .success(let label):
            return successRequest(label: label)
        case .failure(let label):
            return failureRequest(label: label)
        case .custom(let label, let icon):
            return customRequest(label: label, icon: icon)
        }
    }
    
    private func loadingRequest(label: String) -> UserIndicatorRequest {
        let presenter = ToastViewPresenter(
            viewState: .init(
                style: .loading,
                label: label
            ),
            presentationContext: presentationContext
        )
        return UserIndicatorRequest(
            presenter: presenter,
            dismissal: .manual
        )
    }
    
    private func fullScreenLoadingRequest(label: String) -> UserIndicatorRequest {
        let presenter = FullscreenLoadingViewPresenter(
            label: label,
            presentationContext: presentationContext
        )
        return UserIndicatorRequest(
            presenter: presenter,
            dismissal: .manual
        )
    }
    
    private func successRequest(label: String) -> UserIndicatorRequest {
        let presenter = ToastViewPresenter(
            viewState: .init(
                style: .success,
                label: label
            ),
            presentationContext: presentationContext
        )
        return UserIndicatorRequest(
            presenter: presenter,
            dismissal: .timeout(1.5)
        )
    }
    
    private func failureRequest(label: String) -> UserIndicatorRequest {
        let presenter = ToastViewPresenter(
            viewState: .init(
                style: .failure,
                label: label
            ),
            presentationContext: presentationContext
        )
        return UserIndicatorRequest(
            presenter: presenter,
            dismissal: .timeout(1.5)
        )
    }
    
    private func customRequest(label: String, icon: UIImage?) -> UserIndicatorRequest {
        let presenter = ToastViewPresenter(
            viewState: .init(
                style: .custom(icon: icon),
                label: label
            ),
            presentationContext: presentationContext
        )
        return UserIndicatorRequest(
            presenter: presenter,
            dismissal: .manual
        )
    }
}
