import Foundation
import UIKit
import Display
import AsyncDisplayKit
import SwiftSignalKit
import TelegramPresentationData
import TelegramUIPreferences
import AppBundle

private func generateArrowImage(color: UIColor) -> UIImage? {
    let smallRadius: CGFloat = 5.0
    let largeRadius: CGFloat = 14.0
    return generateImage(CGSize(width: smallRadius + largeRadius, height: smallRadius + largeRadius + 16.0), contextGenerator: { size, context in
        context.clear(CGRect(origin: CGPoint(), size: size))
        if let image = generateTintedImage(image: UIImage(bundleImageName: "Instant View/SettingsArrow"), color: color), let cgImage = image.cgImage {
            context.setFillColor(color.cgColor)
            context.fill(CGRect(origin: CGPoint(x: 0.0, y: size.height - image.size.height - 16.0), size: CGSize(width: size.width, height: 16.0)))
            context.draw(cgImage, in: CGRect(origin: CGPoint(x: size.width - image.size.width, y: size.height - image.size.height), size: image.size))
        }
    })
}

final class InstantPageSettingsNode: ASDisplayNode {
    private var settings: InstantPagePresentationSettings
    private var currentThemeType: (InstantPageThemeType, Bool)
    private var theme: InstantPageSettingsItemTheme
    
    private let applySettings: (InstantPagePresentationSettings) -> Void
    private let openInSafari: () -> Void
    
    private var sections: [[InstantPageSettingsItemNode]] = []
    private let sansFamilyNode: InstantPageSettingsFontFamilyNode
    private let serifFamilyNode: InstantPageSettingsFontFamilyNode
    private let themeItemNode: InstantPageSettingsThemeItemNode
    private let autoNightItemNode: InstantPageSettingsSwitchNode
    private let openInItemNode: InstantPageSettingsButtonItemNode
    
    private let arrowNode: ASImageNode
    private let itemContainerNode: ASDisplayNode
    
    init(strings: PresentationStrings, settings: InstantPagePresentationSettings, currentThemeType: (InstantPageThemeType, Bool), applySettings: @escaping (InstantPagePresentationSettings) -> Void, openInSafari: @escaping () -> Void) {
        self.settings = settings
        self.currentThemeType = currentThemeType
        self.theme = InstantPageSettingsItemTheme.themeFor(currentThemeType.0)
        
        self.applySettings = applySettings
        self.openInSafari = openInSafari
        
        self.arrowNode = ASImageNode()
        self.arrowNode.displayWithoutProcessing = true
        self.arrowNode.displaysAsynchronously = false
        self.arrowNode.image = generateArrowImage(color: self.theme.itemBackgroundColor)
        
        self.itemContainerNode = ASDisplayNode()
        self.itemContainerNode.layer.masksToBounds = true
        self.itemContainerNode.layer.cornerRadius = 16.0
        self.itemContainerNode.backgroundColor = self.theme.listBackgroundColor
        
        var updateSerifImpl: ((Bool) -> Void)?
        var updateThemeTypeImpl: ((InstantPageThemeType) -> Void)?
        var updateAutoNightImpl: ((Bool) -> Void)?
        var openInSafariImpl: (() -> Void)?
        
        self.sansFamilyNode = InstantPageSettingsFontFamilyNode(theme: self.theme, title: "San Francisco", family: nil, checked: !settings.forceSerif, tapped: {
            updateSerifImpl?(false)
        })
        self.serifFamilyNode = InstantPageSettingsFontFamilyNode(theme: self.theme, title: "Georgia", family: "Georgia", checked: settings.forceSerif, tapped: {
            updateSerifImpl?(true)
        })
        self.themeItemNode = InstantPageSettingsThemeItemNode(theme: theme, themeType: settings.themeType, update: { value in
            updateThemeTypeImpl?(value)
        })
        self.autoNightItemNode = InstantPageSettingsSwitchNode(theme: theme, title: strings.InstantPage_AutoNightTheme, isOn: settings.autoNightMode, isEnabled: settings.themeType != .dark, toggled: { value in
            updateAutoNightImpl?(value)
        })
        self.openInItemNode = InstantPageSettingsButtonItemNode(theme: theme, title: strings.Web_OpenExternal, tapped: {
            openInSafariImpl?()
        })
        super.init()
        
        self.addSubnode(self.arrowNode)
        self.addSubnode(self.itemContainerNode)
        
        self.sections = [
            [
                InstantPageSettingsBacklightItemNode(theme: self.theme)
            ],
            [
                InstantPageSettingsFontSizeItemNode(theme: self.theme, fontSizeVariant: Int(settings.fontSize.rawValue), updated: { [weak self] value in
                    if let strongSelf = self {
                        strongSelf.updateSettings {
                            let size: InstantPagePresentationFontSize = InstantPagePresentationFontSize(rawValue: Int32(value)) ?? .standard
                            return $0.withUpdatedFontSize(size)
                        }
                    }
                }),
                self.sansFamilyNode,
                self.serifFamilyNode
            ],
            [
                self.themeItemNode,
                self.autoNightItemNode
            ],
            [
                self.openInItemNode
            ]
        ]
        
        for section in self.sections {
            for item in section {
                self.itemContainerNode.addSubnode(item)
            }
        }
        
        updateSerifImpl = { [weak self] value in
            if let strongSelf = self {
                strongSelf.updateSettings {
                    return $0.withUpdatedForceSerif(value)
                }
            }
        }
        
        updateThemeTypeImpl = { [weak self] value in
            if let strongSelf = self {
                let disableAutoNightMode = strongSelf.currentThemeType.1
                strongSelf.updateSettings {
                    if disableAutoNightMode {
                        let currentTime: Int32 = 0
                        return $0.withUpdatedThemeType(value).withUpdatedIgnoreAutoNightModeUntil(currentTime)
                    } else {
                        return $0.withUpdatedThemeType(value)
                    }
                }
            }
        }
        
        updateAutoNightImpl = { [weak self] value in
            if let strongSelf = self {
                strongSelf.updateSettings {
                    return $0.withUpdatedAutoNightMode(value).withUpdatedIgnoreAutoNightModeUntil(0)
                }
            }
        }
        openInSafariImpl = { [weak self] in
            if let strongSelf = self {
                strongSelf.openInSafari()
            }
        }
    }
    
    func updateLayout(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
        let fixedWidth: CGFloat = 295.0
        let sectionSpacing: CGFloat = 4.0
        let sideInset: CGFloat = 11.0
        let topInset: CGFloat = layout.insets(options: [.statusBar]).top + 44.0 + 6.0
        
        var contentHeight: CGFloat = 0.0
        var itemSizes: [[CGFloat]] = []
        for sectionIndex in 0 ..< self.sections.count {
            itemSizes.append([])
            if sectionIndex != 0 {
                contentHeight += sectionSpacing
            }
            for itemIndex in 0 ..< self.sections[sectionIndex].count {
                let previousItem: InstantPageSettingsItemNodeStatus
                var previousItemNode: InstantPageSettingsItemNode?
                let nextItem: InstantPageSettingsItemNodeStatus
                var nextItemNode: InstantPageSettingsItemNode?
                if itemIndex == 0 {
                    if sectionIndex == 0 {
                        previousItem = .none
                    } else {
                        previousItem = .otherSection
                    }
                } else {
                    previousItem = .sameSection
                    previousItemNode = self.sections[sectionIndex][itemIndex - 1]
                }
                if itemIndex == self.sections[sectionIndex].count - 1 {
                    if sectionIndex == self.sections.count - 1 {
                        nextItem = .none
                    } else {
                        nextItem = .otherSection
                    }
                } else {
                    nextItem = .sameSection
                    nextItemNode = self.sections[sectionIndex][itemIndex + 1]
                }
                let itemHeight = self.sections[sectionIndex][itemIndex].updateLayout(width: fixedWidth, previousItem: (previousItem, previousItemNode), nextItem: (nextItem, nextItemNode))
                itemSizes[sectionIndex].append(itemHeight)
                contentHeight += itemHeight
            }
        }
        
        if let image = self.arrowNode.image {
            transition.updateFrame(node: self.arrowNode, frame: CGRect(origin: CGPoint(x: layout.size.width - sideInset - image.size.width, y: topInset - image.size.height + 16.0 + 8.0), size: image.size))
        }
        
        transition.updateFrame(node: self.itemContainerNode, frame: CGRect(origin: CGPoint(x: layout.size.width - sideInset - fixedWidth, y: topInset), size: CGSize(width: fixedWidth, height: contentHeight)))
        var nextItemOffset: CGFloat = 0.0
        for sectionIndex in 0 ..< self.sections.count {
            if sectionIndex != 0 {
                nextItemOffset += sectionSpacing
            }
            for itemIndex in 0 ..< self.sections[sectionIndex].count {
                let itemHeight = itemSizes[sectionIndex][itemIndex]
                transition.updateFrame(node: self.sections[sectionIndex][itemIndex], frame: CGRect(origin: CGPoint(x: 0.0, y: nextItemOffset), size: CGSize(width: fixedWidth, height: itemHeight)))
                nextItemOffset += itemHeight
            }
        }
    }
    
    func animateIn() {
        self.layer.allowsGroupOpacity = true
        self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3, completion: { [weak self] _ in
            self?.layer.allowsGroupOpacity = false
        })
    }
    
    func animateOut(completion: @escaping () -> Void) {
        self.layer.allowsGroupOpacity = true
        self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak self] _ in
            self?.layer.allowsGroupOpacity = false
            completion()
        })
    }
    
    private func updateSettings(_ f: (InstantPagePresentationSettings) -> InstantPagePresentationSettings) {
        let updated = f(self.settings)
        if updated != self.settings {
            self.settings = updated
            
            self.applySettings(settings)
        }
    }
    
    func updateSettingsAndCurrentThemeType(settings: InstantPagePresentationSettings, type: (InstantPageThemeType, Bool)) {
        self.currentThemeType = type
        
        self.sansFamilyNode.checked = !self.settings.forceSerif
        self.serifFamilyNode.checked = self.settings.forceSerif
        self.themeItemNode.themeType = self.settings.themeType
        self.autoNightItemNode.isEnabled = self.settings.themeType != .dark
        
        let theme = InstantPageSettingsItemTheme.themeFor(self.currentThemeType.0)
        if theme != self.theme {
            self.theme = theme
            
            if let snapshotView = self.view.snapshotView(afterScreenUpdates: false) {
                self.view.addSubview(snapshotView)
                snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak snapshotView] _ in
                    snapshotView?.removeFromSuperview()
                })
            }
            
            self.arrowNode.image = generateArrowImage(color: self.theme.itemBackgroundColor)
            self.itemContainerNode.backgroundColor = self.theme.listBackgroundColor
            for section in self.sections {
                for item in section {
                    item.updateTheme(self.theme)
                }
            }
        }
    }
    
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        if self.itemContainerNode.frame.contains(point) {
            return super.hitTest(point, with: event)
        } else {
            return nil
        }
    }
}