//  SceneDelegate.swift

/*
    Package MobileWallet
    Created by Jason van den Berg on 2019/10/29
    Using Swift 5.0
    Running on macOS 10.15

    Copyright 2019 The Tari Project

    Redistribution and use in source and binary forms, with or
    without modification, are permitted provided that the
    following conditions are met:

    1. Redistributions of source code must retain the above copyright notice,
    this list of conditions and the following disclaimer.

    2. Redistributions in binary form must reproduce the above
    copyright notice, this list of conditions and the following disclaimer in the
    documentation and/or other materials provided with the distribution.

    3. Neither the name of the copyright holder nor the names of
    its contributors may be used to endorse or promote products
    derived from this software without specific prior written permission.

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
    CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
    INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
    OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
    NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
    OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

import UIKit
import YatLib

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?

    func scene(_ scene: UIScene,
               willConnectTo session: UISceneSession,
               options connectionOptions: UIScene.ConnectionOptions) {
        // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
        // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
        // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).

        // If the user opens the app from a notification when the app is closed
        if let notification = connectionOptions.notificationResponse {
            _ = notification.notification.request.content.userInfo
            // TODO handle notification content here
        }

        // If the user opens a deep link while the app is closed
        if let url = connectionOptions.urlContexts.first?.url {
            deepLinker.handleShortcut(type: .send(deeplink: NSString(string: url.absoluteString) as String))
        }

        // If the user opens a home screen shortcut while the app is closed
        if let shortcutItem = connectionOptions.shortcutItem {
            deepLinker.handleShortcut(item: shortcutItem)
        }

        if ICloudBackup.shared.iCloudBackupsIsOn {
            BackupScheduler.shared.startObserveEvents()
        }

        if let appReturnLink = TariSettings.shared.yatReturnLink, let organizationName = TariSettings.shared.yatOrganizationName, let organizationKey = TariSettings.shared.yatOrganizationKey {
            Yat.configuration = YatConfiguration(appReturnLink: appReturnLink, organizationName: organizationName, organizationKey: organizationKey)
        }
        
        if let yatWebServiceURL = TariSettings.shared.yatWebServiceURL, let yatApiURL = TariSettings.shared.yatApiURL {
            Yat.urls = YatURLs(webServiceURL: yatWebServiceURL, apiURL: yatApiURL)
        }
        
        setupYatIntegration()

        guard let _ = (scene as? UIWindowScene) else { return }
        if let windowScene = scene as? UIWindowScene {
            let window = UIWindow(windowScene: windowScene)
            let navigationController = AlwaysPoppableNavigationController(
                rootViewController: SplashViewController()
            )
            navigationController.setNavigationBarHidden(true, animated: false)
            window.rootViewController = navigationController
            self.window = window
            window.makeKeyAndVisible()
        }
    }

    func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
        guard let url = URLContexts.first?.url else { return }
        deepLinker.handleShortcut(type: .send(deeplink: url.absoluteString))
        Yat.integration.handle(deeplink: url)
    }

    func sceneDidDisconnect(_ scene: UIScene) {
        // Called as the scene is being released by the system.
        // This occurs shortly after the scene enters the background, or when its session is discarded.
        // Release any resources associated with this scene that can be re-created the next time the scene connects.
        // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
    }

    func sceneDidBecomeActive(_ scene: UIScene) {
        // Called when the scene has moved from an inactive state to an active state.
        // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.

        deepLinker.checkDeepLink()
    }

    func sceneWillResignActive(_ scene: UIScene) {
        // Called when the scene will move from an active state to an inactive state.
        // This may occur due to temporary interruptions (ex. an incoming phone call).
    }

    private func onTorSuccess(_ onComplete: @escaping () -> Void) {
        // Handle if tor ports opened later
        TariEventBus.onMainThread(self, eventType: .torPortsOpened) { [weak self] (_) in
            guard let self = self else { return }
            TariEventBus.unregister(self, eventType: .torPortsOpened)
            onComplete()
        }
        if TariLib.shared.torPortsOpened {
            TariEventBus.unregister(self, eventType: .torPortsOpened)
            onComplete()
        }
    }

    func sceneWillEnterForeground(_ scene: UIScene) {
        // Called as the scene transitions from the background to the foreground.
        // Use this method to undo the changes made on entering the background.

        // Remove badges from push notifications
        UIApplication.shared.applicationIconBadgeNumber = 0
        ConnectionMonitor.shared.start()
        TariLib.shared.startTor()
        // Only starts the wallet if it was stopped. Else wallet is started on the splash screen.
        if TariLib.shared.isWalletExist {
            onTorSuccess {
                guard TariLib.shared.walletState == .notReady else { return }
                TariLib.shared.startWallet(seedWords: nil)
            }
        }
        if UserDefaults.Key.backupOperationAborted.boolValue()
            && ICloudBackup.shared.iCloudBackupsIsOn
            && !ICloudBackup.shared.inProgress {
            BackupScheduler.shared.scheduleBackup(immediately: true)
        }
    }

    func sceneDidEnterBackground(_ scene: UIScene) {
        // Called as the scene transitions from the foreground to the background.
        // Use this method to save data, release shared resources, and store enough scene-specific state information
        // to restore the scene back to its current state.

        // Save changes in the application's managed object context when the application transitions to the background.
        (UIApplication.shared.delegate as? AppDelegate)?.saveContext()
        ConnectionMonitor.shared.stop()
        TariLib.shared.stopWallet()
        TariLib.shared.stopTor()
        ICloudBackup.shared.backgroundBackupWallet()
    }

    func windowScene(_ windowScene: UIWindowScene,
                     performActionFor shortcutItem: UIApplicationShortcutItem,
                     completionHandler: @escaping (Bool) -> Void) {
        completionHandler(deepLinker.handleShortcut(item: shortcutItem))
    }
    
    private func setupYatIntegration() {
        Yat.integration.onYatConnected = {
            TariSettings.shared.walletSettings.connectedYat = $0
        }
    }
}
