import Foundation
import UIKit
import Display
import SwiftSignalKit
import LegacyComponents
import TelegramPresentationData
import AttachmentUI

public enum LegacyControllerPresentation {
    case custom
    case modal(animateIn: Bool)
    case navigation
}

private func passControllerAppearanceAnimated(in: Bool, presentation: LegacyControllerPresentation) -> Bool {
    switch presentation {
        case let .modal(animateIn):
            if `in` {
                return animateIn
            } else {
                return true
            }
        default:
            return false
    }
}

private final class LegacyComponentsOverlayWindowManagerImpl: NSObject, LegacyComponentsOverlayWindowManager {
    private weak var contentController: UIViewController?
    private weak var parentController: ViewController?
    private var controller: LegacyController?
    private var boundController = false
    
    init(parentController: ViewController?, theme: PresentationTheme?) {
        self.parentController = parentController
        self.controller = LegacyController(presentation: .custom, theme: theme)
        self.controller?.hasSparseContainerView = (parentController as? LegacyController)?.hasSparseContainerView ?? false
        
        super.init()
        
        if let parentController = parentController {
            if parentController.statusBar.statusBarStyle == .Hide {
                self.controller?.statusBar.statusBarStyle = parentController.statusBar.statusBarStyle
            }
            if parentController.view.disablesInteractiveTransitionGestureRecognizer {
                self.controller?.view.disablesInteractiveTransitionGestureRecognizer = true
            }
            self.controller?.view.frame = parentController.view.bounds
        }
    }
    
    func managesWindow() -> Bool {
        return true
    }
    
    func bindController(_ controller: UIViewController!) {
        self.contentController = controller
        if controller.prefersStatusBarHidden {
            self.controller?.statusBar.statusBarStyle = .Hide
        }
        
        controller.state_setNeedsStatusBarAppearanceUpdate({ [weak self, weak controller] in
            if let parentController = self?.parentController, let controller = controller {
                if parentController.statusBar.statusBarStyle != .Hide && !controller.prefersStatusBarHidden {
                    self?.controller?.statusBar.statusBarStyle = StatusBarStyle(systemStyle: controller.preferredStatusBarStyle)
                }
            }
        })
        if let parentController = self.parentController {
            if parentController.statusBar.statusBarStyle != .Hide && !controller.prefersStatusBarHidden {
                self.controller?.statusBar.statusBarStyle = StatusBarStyle(systemStyle: controller.preferredStatusBarStyle)
            }
        }
    }
    
    func context() -> LegacyComponentsContext! {
        return self.controller?.context
    }
    
    func setHidden(_ hidden: Bool, window: UIWindow!) {
        if hidden {
            self.controller?.dismiss()
            self.controller = nil
        } else if let contentController = self.contentController, let parentController = self.parentController, let controller = self.controller {
            if !self.boundController {
                controller.bind(controller: contentController)
                self.boundController = true
            }
            parentController.present(controller, in: .window(.root))
        }
    }
}

public final class LegacyControllerContext: NSObject, LegacyComponentsContext {
    public private(set) weak var controller: ViewController?
    private let theme: PresentationTheme?
    
    public init(controller: ViewController?, theme: PresentationTheme?) {
        self.controller = controller
        self.theme = theme
        
        super.init()
    }
    
    public func fullscreenBounds() -> CGRect {
        if let controller = self.controller {
            return controller.view.bounds
        } else {
            return CGRect()
        }
    }
    
    
    public func lockPortrait() {
        if let controller = self.controller as? LegacyController {
            controller.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
        }
    }
    
    public func unlockPortrait() {
        if let controller = self.controller as? LegacyController {
            controller.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .allButUpsideDown)
        }
    }
    
    public func disableInteractiveKeyboardGesture() {
        if let controller = self.controller as? LegacyController {
            controller.view.disablesInteractiveModalDismiss = true
            controller.view.disablesInteractiveKeyboardGestureRecognizer = true
        }
    }

    public func keyCommandController() -> TGKeyCommandController! {
        return nil
    }
    
    public func rootCallStatusBarHidden() -> Bool {
        return true
    }
    
    public func statusBarFrame() -> CGRect {
        return legacyComponentsApplication!.statusBarFrame
    }
    
    public func isStatusBarHidden() -> Bool {
        if let controller = self.controller {
            return controller.statusBar.isHidden || controller.navigationPresentation == .modal
        } else {
            return true
        }
    }
    
    public func setStatusBarHidden(_ hidden: Bool, with animation: UIStatusBarAnimation) {
        if let controller = self.controller {
            controller.statusBar.isHidden = hidden
            self.updateDeferScreenEdgeGestures()
        }
    }
    
    public func forceSetStatusBarHidden(_ hidden: Bool, with animation: UIStatusBarAnimation) {
        if let controller = self.controller {
            controller.statusBar.isHidden = hidden
        }
    }
    
    public func statusBarStyle() -> UIStatusBarStyle {
        if let controller = self.controller {
            switch controller.statusBar.statusBarStyle {
                case .Black:
                    return .default
                case .White:
                    return .lightContent
                default:
                    return .default
            }
        } else {
            return .default
        }
    }
    
    public func setStatusBarStyle(_ statusBarStyle: UIStatusBarStyle, animated: Bool) {
        if let controller = self.controller {
            switch statusBarStyle {
                case .default:
                    controller.statusBar.statusBarStyle = .Black
                case .lightContent:
                    controller.statusBar.statusBarStyle = .White
                default:
                    controller.statusBar.statusBarStyle = .Black
            }
        }
    }
    
    public func forceStatusBarAppearanceUpdate() {
    }
    
    public func currentlyInSplitView() -> Bool {
        if let controller = self.controller as? LegacyController, let validLayout = controller.validLayout {
            return validLayout.isNonExclusive
        }
        return false
    }
    
    public func currentSizeClass() -> UIUserInterfaceSizeClass {
        if let controller = self.controller as? LegacyController, let validLayout = controller.validLayout {
            if case .regular = validLayout.metrics.widthClass, case .regular = validLayout.metrics.heightClass {
                return .regular
            }
        }
        return .compact
    }
    
    public func currentHorizontalSizeClass() -> UIUserInterfaceSizeClass {
        if let controller = self.controller as? LegacyController, let validLayout = controller.validLayout {
            if case .regular = validLayout.metrics.widthClass {
                return .regular
            }
        }
        return .compact
    }
    
    public func currentVerticalSizeClass() -> UIUserInterfaceSizeClass {
        if let controller = self.controller as? LegacyController, let validLayout = controller.validLayout {
            if case .regular = validLayout.metrics.heightClass {
                return .regular
            }
        }
        return .compact
    }
    
    public func sizeClassSignal() -> SSignal! {
        if let controller = self.controller as? LegacyController, let validLayout = controller.validLayout {
            if case .regular = validLayout.metrics.heightClass {
                return SSignal.single(UIUserInterfaceSizeClass.regular.rawValue as NSNumber)
            }
        }
        if let controller = self.controller as? LegacyController, controller.enableSizeClassSignal {
            //return controller.sizeClassSignal
        }
        return SSignal.single(UIUserInterfaceSizeClass.compact.rawValue as NSNumber)
    }
    
    public func canOpen(_ url: URL!) -> Bool {
        return false
    }
    
    public func open(_ url: URL!) {
    }
    
    public func serverMediaData(forAssetUrl url: String!) -> [AnyHashable : Any]! {
        return nil
    }
    
    public func presentActionSheet(_ actions: [LegacyComponentsActionSheetAction]!, view: UIView!, completion: ((LegacyComponentsActionSheetAction?) -> Void)!) {
        
    }
    
    public func presentActionSheet(_ actions: [LegacyComponentsActionSheetAction]!, view: UIView!, sourceRect: (() -> CGRect)!, completion: ((LegacyComponentsActionSheetAction?) -> Void)!) {
        
    }
    
    public func makeOverlayWindowManager() -> LegacyComponentsOverlayWindowManager! {
        return LegacyComponentsOverlayWindowManagerImpl(parentController: self.controller, theme: self.theme)
    }
    
    public func applicationStatusBarAlpha() -> CGFloat {
        if let controller = self.controller {
            return controller.statusBar.alpha
        }
        return 0.0
    }
    
    public func setApplicationStatusBarAlpha(_ alpha: CGFloat) {
        if let controller = self.controller {
            controller.statusBar.updateAlpha(alpha, transition: .immediate)
            self.updateDeferScreenEdgeGestures()
        }
    }
    
    private func updateDeferScreenEdgeGestures() {
        if let controller = self.controller {
            if controller.statusBar.isHidden || controller.statusBar.alpha.isZero {
                controller.deferScreenEdgeGestures = [.top]
            } else {
                controller.deferScreenEdgeGestures = []
            }
        }
    }

    public func animateApplicationStatusBarAppearance(_ statusBarAnimation: Int32, delay: TimeInterval, duration: TimeInterval, completion: (() -> Void)!) {
        completion?()
    }
    
    public func animateApplicationStatusBarAppearance(_ statusBarAnimation: Int32, duration: TimeInterval, completion: (() -> Void)!) {
        self.animateApplicationStatusBarAppearance(statusBarAnimation, delay: 0.0, duration: duration, completion: completion)
    }
    
    public func animateApplicationStatusBarStyleTransition(withDuration duration: TimeInterval) {
    }
    
    public func safeAreaInset() -> UIEdgeInsets {
        if let controller = self.controller as? LegacyController, let validLayout = controller.validLayout {
            var safeInsets = validLayout.safeInsets
            if safeInsets.top.isEqual(to: 44.0) {
                safeInsets.bottom = 34.0
            }
            if validLayout.intrinsicInsets.bottom.isEqual(to: 21.0) {
                safeInsets.bottom = 21.0
            } else if validLayout.intrinsicInsets.bottom.isEqual(to: 34.0) {
                safeInsets.bottom = 34.0
            } else {
                if let knownSafeInset = validLayout.deviceMetrics.onScreenNavigationHeight(inLandscape: validLayout.size.width > validLayout.size.height, systemOnScreenNavigationHeight: nil) {
                    if knownSafeInset > 0.0 {
                        safeInsets.bottom = knownSafeInset
                    }
                }
            }
            if controller.navigationPresentation == .modal {
                safeInsets.top = 0.0
            }
            return safeInsets
        }
        return UIEdgeInsets()
    }
    
    public func prefersLightStatusBar() -> Bool {
        if let controller = self.controller {
            switch controller.statusBar.statusBarStyle {
                case .Black:
                    return false
                case .White:
                    return true
                default:
                    return false
            }
        } else {
            return false
        }
    }
    
    public func navigationBarPallete() -> TGNavigationBarPallete! {
        let presentationTheme: PresentationTheme
        if let theme = self.theme {
            presentationTheme = theme
        } else {
            presentationTheme = defaultPresentationTheme
        }
        let theme = presentationTheme
        let barTheme = theme.rootController.navigationBar
        return TGNavigationBarPallete(backgroundColor: barTheme.opaqueBackgroundColor, separatorColor: barTheme.separatorColor, titleColor: barTheme.primaryTextColor, tintColor: barTheme.accentTextColor)
    }
    
    public func menuSheetPallete() -> TGMenuSheetPallete! {
        let presentationTheme: PresentationTheme
        if let theme = self.theme {
            presentationTheme = theme
        } else {
            presentationTheme = defaultPresentationTheme
        }
        let theme = presentationTheme
        let sheetTheme = theme.actionSheet
        
        return TGMenuSheetPallete(dark: theme.overallDarkAppearance, backgroundColor: sheetTheme.opaqueItemBackgroundColor, selectionColor: sheetTheme.opaqueItemHighlightedBackgroundColor, separatorColor: sheetTheme.opaqueItemSeparatorColor, accentColor: sheetTheme.controlAccentColor, destructiveColor: sheetTheme.destructiveActionTextColor, textColor: sheetTheme.primaryTextColor, secondaryTextColor: sheetTheme.secondaryTextColor, spinnerColor: sheetTheme.secondaryTextColor, badgeTextColor: sheetTheme.controlAccentColor, badgeImage: nil, cornersImage: generateStretchableFilledCircleImage(diameter: 11.0, color: nil, strokeColor: nil, strokeWidth: nil, backgroundColor: sheetTheme.opaqueItemBackgroundColor))
    }
    
    public func darkMenuSheetPallete() -> TGMenuSheetPallete! {
        let presentationTheme: PresentationTheme
        if let theme = self.theme {
            presentationTheme = theme
        } else {
            presentationTheme = defaultPresentationTheme
        }
        let theme = presentationTheme
        let sheetTheme = theme.actionSheet
        return TGMenuSheetPallete(dark: theme.overallDarkAppearance, backgroundColor: sheetTheme.opaqueItemBackgroundColor, selectionColor: sheetTheme.opaqueItemHighlightedBackgroundColor, separatorColor: sheetTheme.opaqueItemSeparatorColor, accentColor: sheetTheme.controlAccentColor, destructiveColor: sheetTheme.destructiveActionTextColor, textColor: sheetTheme.primaryTextColor, secondaryTextColor: sheetTheme.secondaryTextColor, spinnerColor: sheetTheme.secondaryTextColor, badgeTextColor: sheetTheme.controlAccentColor, badgeImage: nil, cornersImage: generateStretchableFilledCircleImage(diameter: 11.0, color: nil, strokeColor: nil, strokeWidth: nil, backgroundColor: sheetTheme.opaqueItemBackgroundColor))
    }
    
    public func mediaAssetsPallete() -> TGMediaAssetsPallete! {
        let presentationTheme: PresentationTheme
        if let theme = self.theme {
            presentationTheme = theme
        } else {
            presentationTheme = defaultPresentationTheme
        }
        
        let theme = presentationTheme.list
        let navigationBar = presentationTheme.rootController.navigationBar
        let tabBar = presentationTheme.rootController.tabBar
        
        return TGMediaAssetsPallete(dark: presentationTheme.overallDarkAppearance, backgroundColor: theme.plainBackgroundColor, selectionColor: theme.itemHighlightedBackgroundColor, separatorColor: theme.itemPlainSeparatorColor, textColor: theme.itemPrimaryTextColor, secondaryTextColor: theme.controlSecondaryColor, accentColor: theme.itemAccentColor, destructiveColor: theme.itemDestructiveColor, barBackgroundColor: navigationBar.opaqueBackgroundColor, barSeparatorColor: tabBar.separatorColor, navigationTitleColor: navigationBar.primaryTextColor, badge: generateStretchableFilledCircleImage(diameter: 22.0, color: navigationBar.accentTextColor), badgeTextColor: navigationBar.opaqueBackgroundColor, sendIconImage: PresentationResourcesChat.chatInputPanelSendButtonImage(presentationTheme), doneIconImage: PresentationResourcesChat.chatInputPanelApplyButtonImage(presentationTheme), maybeAccentColor: navigationBar.accentTextColor)
    }
    
    public func checkButtonPallete() -> TGCheckButtonPallete! {
        let presentationTheme: PresentationTheme
        if let theme = self.theme {
            presentationTheme = theme
        } else {
            presentationTheme = defaultPresentationTheme
        }
        
        let theme = presentationTheme
        return TGCheckButtonPallete(defaultBackgroundColor: theme.chat.message.selectionControlColors.fillColor, accentBackgroundColor: theme.chat.message.selectionControlColors.fillColor, defaultBorderColor: theme.chat.message.selectionControlColors.strokeColor, mediaBorderColor: theme.chat.message.selectionControlColors.strokeColor, chatBorderColor: theme.chat.message.selectionControlColors.strokeColor, check: theme.chat.message.selectionControlColors.foregroundColor, blueColor: theme.chat.message.selectionControlColors.fillColor, barBackgroundColor: theme.chat.message.selectionControlColors.fillColor)
    }
}

open class LegacyController: ViewController, PresentableController, AttachmentContainable {
    public private(set) var legacyController: UIViewController!
    private let presentation: LegacyControllerPresentation
    
    private var controllerNode: LegacyControllerNode {
        return self.displayNode as! LegacyControllerNode
    }
    
    private var contextImpl: LegacyControllerContext!
    public var context: LegacyComponentsContext {
        return self.contextImpl!
    }
    
    fileprivate var validLayout: ContainerViewLayout?
    
    public var parentInsets: UIEdgeInsets = UIEdgeInsets()
    
    public var controllerLoaded: (() -> Void)?
    public var presentationCompleted: (() -> Void)?
    
    private let sizeClass: SVariable = SVariable()
    public var enableSizeClassSignal: Bool = false
    public var sizeClassSignal: SSignal {
        return self.sizeClass.signal()
    }
    private var enableContainerLayoutUpdates = false
    
    public var hasSparseContainerView = false {
        didSet {
            if self.isNodeLoaded {
                self.controllerNode.hasSparseContainerView = self.hasSparseContainerView
            }
        }
    }
    
    public var disposables = DisposableSet()
    
    open var requestAttachmentMenuExpansion: () -> Void = {}
    open var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
    open var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
    open var cancelPanGesture: () -> Void = { }
    open var isContainerPanning: () -> Bool = { return false }
    open var isContainerExpanded: () -> Bool = { return false }
    
    public var mediaPickerContext: AttachmentMediaPickerContext? {
        return nil
    }
    
    public init(presentation: LegacyControllerPresentation, theme: PresentationTheme? = nil, strings: PresentationStrings? = nil, initialLayout: ContainerViewLayout? = nil) {
        self.sizeClass.set(SSignal.single(UIUserInterfaceSizeClass.compact.rawValue as NSNumber))
        self.presentation = presentation
        self.validLayout = initialLayout
        
        let navigationBarPresentationData: NavigationBarPresentationData?
        if let theme = theme, let strings = strings, case .navigation = presentation {
            navigationBarPresentationData = NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: theme), strings: NavigationBarStrings(presentationStrings: strings))
        } else {
            navigationBarPresentationData = nil
        }
        super.init(navigationBarPresentationData: navigationBarPresentationData)
        
        if let theme = theme {
            self.statusBar.statusBarStyle = theme.rootController.statusBarStyle.style
        }
        
        let contextImpl = LegacyControllerContext(controller: self, theme: theme)
        self.contextImpl = contextImpl
    }
    
    deinit {
        self.disposables.dispose()
    }
    
    required public init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    public func bind(controller: UIViewController) {
        self.legacyController = controller
        if let controller = controller as? TGViewController {
            controller.customRemoveFromParentViewController = { [weak self] in
                self?.dismiss()
            }
        }
    }
    
    override open func loadDisplayNode() {
        self.displayNode = LegacyControllerNode()
        self.displayNodeDidLoad()
        
        self.controllerNode.hasSparseContainerView = self.hasSparseContainerView
    }
    
    override open func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        if self.ignoreAppearanceMethodInvocations() {
            return
        }
        
        if self.controllerNode.controllerView == nil {
            if self.controllerNode.frame.width == 0.0, let layout = self.validLayout {
                self.controllerNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width - self.parentInsets.left - self.parentInsets.right, height: layout.size.height))
            }
            
            self.controllerNode.controllerView = self.legacyController.view
            if let legacyController = self.legacyController as? TGViewController {
                legacyController.ignoreAppearEvents = true
            }
            self.controllerNode.view.insertSubview(self.legacyController.view, at: 0)
            if let legacyController = self.legacyController as? TGViewController {
                legacyController.ignoreAppearEvents = false
            }
            
            if let controllerLoaded = self.controllerLoaded {
                controllerLoaded()
            }
        }
        
        self.legacyController.viewWillAppear(animated && passControllerAppearanceAnimated(in: true, presentation: self.presentation))
    }
    
    override open func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        
        if self.ignoreAppearanceMethodInvocations() {
            return
        }
        
        self.legacyController.viewWillDisappear(animated && passControllerAppearanceAnimated(in: false, presentation: self.presentation))
    }
    
    private var viewDidAppearProcessed = false
    
    override open func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        self.enableContainerLayoutUpdates = true
        if self.ignoreAppearanceMethodInvocations() {
            return
        }
        self.viewDidAppear(animated: animated, completion: {})
    }
    
    public func viewDidAppear(completion: @escaping () -> Void) {
        self.viewDidAppear(animated: false, completion: completion)
    }
    
    private func viewDidAppear(animated: Bool, completion: @escaping () -> Void) {
        if self.viewDidAppearProcessed {
            completion()
            return
        }
        self.viewDidAppearProcessed = true
        switch self.presentation {
            case let .modal(animateIn):
                if animateIn {
                    self.controllerNode.animateModalIn(completion: { [weak self] in
                        self?.presentationCompleted?()
                        completion()
                    })
                } else {
                    self.presentationCompleted?()
                    completion()
                }
                self.legacyController.viewDidAppear(animated && animateIn)
            case .custom, .navigation:
                self.legacyController.viewDidAppear(animated)
                self.presentationCompleted?()
                completion()
        }
    }
    
    override open func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        self.viewDidAppearProcessed = false
        
        if self.ignoreAppearanceMethodInvocations() {
            return
        }
        
        self.legacyController.viewDidDisappear(animated && passControllerAppearanceAnimated(in: false, presentation: self.presentation))
    }
    
    override open func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
        let previousSizeClass: UIUserInterfaceSizeClass
        if let validLayout = self.validLayout, case .regular = validLayout.metrics.widthClass {
            previousSizeClass = .regular
        } else {
            previousSizeClass = .compact
        }
        self.validLayout = layout
        
        super.containerLayoutUpdated(layout, transition: transition)
        
        self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition)
        if let legacyTelegramController = self.legacyController as? TGViewController {
            var duration: TimeInterval = 0.0
            if case let .animated(transitionDuration, _) = transition {
                duration = transitionDuration
            }
            
            var orientation = UIInterfaceOrientation.portrait
            if layout.size.width > layout.size.height {
                orientation = .landscapeRight
            }
            
            let size = CGSize(width: layout.size.width - layout.intrinsicInsets.left - layout.intrinsicInsets.right, height: layout.size.height)
            
            legacyTelegramController.intrinsicSize = size
            legacyTelegramController._updateInset(for: orientation, force: false, notify: true)
            if self.enableContainerLayoutUpdates {
                legacyTelegramController.layoutController(for: size, duration: duration)
            }
        }
        let updatedSizeClass: UIUserInterfaceSizeClass
        if case .regular = layout.metrics.widthClass {
            updatedSizeClass = .regular
        } else {
            updatedSizeClass = .compact
        }
        if previousSizeClass != updatedSizeClass {
            self.sizeClass.set(SSignal.single(updatedSizeClass.rawValue as NSNumber))
        }
    }
    
    override open func dismiss(completion: (() -> Void)? = nil) {
        self.view.endEditing(true)
        switch self.presentation {
            case .modal:
                self.controllerNode.animateModalOut { [weak self] in
                    self?.presentingViewController?.dismiss(animated: false, completion: completion)
                }
            case .custom:
                self.presentingViewController?.dismiss(animated: false, completion: completion)
            case .navigation:
                (self.navigationController as? NavigationController)?.filterController(self, animated: true)
        }
    }
    
    public func dismissWithAnimation() {
        self.controllerNode.animateModalOut { [weak self] in
            self?.presentingViewController?.dismiss(animated: false, completion: nil)
        }
    }
}