import Foundation
import UIKit
import Display
import SwiftSignalKit
import TelegramCore
import Postbox
import SSignalKit
import TelegramPresentationData
import AccountContext
import LegacyComponents
import LegacyUI
import LegacyMediaPickerUI
import Photos
import MediaAssetsContext

private func galleryFetchResultItems(fetchResult: PHFetchResult<PHAsset>, index: Int, reversed: Bool, selectionContext: TGMediaSelectionContext?, editingContext: TGMediaEditingContext, stickersContext: TGPhotoPaintStickersContext, immediateThumbnail: UIImage?) -> ([TGModernGalleryItem], TGModernGalleryItem?) {
    var focusItem: TGModernGalleryItem?
    var galleryItems: [TGModernGalleryItem] = []
    
    let legacyFetchResult = TGMediaAssetFetchResult(phFetchResult: fetchResult as? PHFetchResult<AnyObject>, reversed: reversed)
    
    for i in 0 ..< fetchResult.count {
        if let galleryItem = TGMediaPickerGalleryFetchResultItem(fetchResult: legacyFetchResult, index: UInt(i)) {
            galleryItem.selectionContext = selectionContext
            galleryItem.editingContext = editingContext
            galleryItem.stickersContext = stickersContext
            galleryItems.append(galleryItem)
            
            if i == index {
                galleryItem.immediateThumbnailImage = immediateThumbnail
                focusItem = galleryItem
            }
        }
    }
    return (galleryItems, focusItem)
}

private func gallerySelectionItems(item: TGMediaSelectableItem, selectionContext: TGMediaSelectionContext?, editingContext: TGMediaEditingContext, stickersContext: TGPhotoPaintStickersContext, immediateThumbnail: UIImage?) -> ([TGModernGalleryItem], TGModernGalleryItem?) {
    var focusItem: TGModernGalleryItem?
    var galleryItems: [TGModernGalleryItem] = []
    
    if let selectionContext = selectionContext {
        for case let selectedItem as TGMediaSelectableItem in selectionContext.selectedItems() {
            if let asset = selectedItem as? TGMediaAsset {
                let galleryItem: (TGModernGallerySelectableItem & TGModernGalleryEditableItem)
                switch asset.type {
                    case TGMediaAssetVideoType:
                        galleryItem = TGMediaPickerGalleryVideoItem(asset: asset)
                    case TGMediaAssetGifType:
                        let convertedAsset = TGCameraCapturedVideo(asset: asset, livePhoto: false)
                        galleryItem = TGMediaPickerGalleryVideoItem(asset: convertedAsset)
                    default:
                        galleryItem = TGMediaPickerGalleryPhotoItem(asset: asset)
                }
                galleryItem.selectionContext = selectionContext
                galleryItem.editingContext = editingContext
                galleryItem.stickersContext = stickersContext
                galleryItems.append(galleryItem)
                
                if selectedItem.uniqueIdentifier == item.uniqueIdentifier {
                    if let galleryItem = galleryItem as? TGMediaPickerGalleryItem {
                        galleryItem.immediateThumbnailImage = immediateThumbnail
                    }
                    focusItem = galleryItem
                }
            } else if let asset = selectedItem as? UIImage {
                let galleryItem: (TGModernGallerySelectableItem & TGModernGalleryEditableItem) = TGMediaPickerGalleryPhotoItem(asset: asset)
                galleryItem.selectionContext = selectionContext
                galleryItem.editingContext = editingContext
                galleryItem.stickersContext = stickersContext
                galleryItems.append(galleryItem)
                
                if selectedItem.uniqueIdentifier == item.uniqueIdentifier {
                    if let galleryItem = galleryItem as? TGMediaPickerGalleryItem {
                        galleryItem.immediateThumbnailImage = immediateThumbnail
                    }
                    focusItem = galleryItem
                }
            } else if let asset = selectedItem as? TGCameraCapturedVideo {
                let galleryItem: (TGModernGallerySelectableItem & TGModernGalleryEditableItem) = TGMediaPickerGalleryVideoItem(asset: asset)
                galleryItem.selectionContext = selectionContext
                galleryItem.editingContext = editingContext
                galleryItem.stickersContext = stickersContext
                galleryItems.append(galleryItem)
                
                if selectedItem.uniqueIdentifier == item.uniqueIdentifier {
                    if let galleryItem = galleryItem as? TGMediaPickerGalleryItem {
                        galleryItem.immediateThumbnailImage = immediateThumbnail
                    }
                    focusItem = galleryItem
                }
            }
        }
    }
    
    return (galleryItems, focusItem)
}

enum LegacyMediaPickerGallerySource {
    case fetchResult(fetchResult: PHFetchResult<PHAsset>, index: Int, reversed: Bool)
    case selection(item: TGMediaSelectableItem)
}

func presentLegacyMediaPickerGallery(context: AccountContext, peer: EnginePeer?, threadTitle: String?, chatLocation: ChatLocation?, isScheduledMessages: Bool, presentationData: PresentationData, source: LegacyMediaPickerGallerySource, immediateThumbnail: UIImage?, selectionContext: TGMediaSelectionContext?, editingContext: TGMediaEditingContext, hasSilentPosting: Bool, hasSchedule: Bool, hasTimer: Bool, updateHiddenMedia: @escaping (String?) -> Void, initialLayout: ContainerViewLayout?, transitionHostView: @escaping () -> UIView?, transitionView: @escaping (String) -> UIView?, completed: @escaping (TGMediaSelectableItem & TGMediaEditableItem, Bool, Int32?, @escaping () -> Void) -> Void, presentSchedulePicker: @escaping (Bool, @escaping (Int32) -> Void) -> Void, presentTimerPicker: @escaping (@escaping (Int32) -> Void) -> Void, getCaptionPanelView: @escaping () -> TGCaptionPanelView?, present: @escaping (ViewController, Any?) -> Void, finishedTransitionIn: @escaping () -> Void, willTransitionOut: @escaping () -> Void, dismissAll: @escaping () -> Void) -> TGModernGalleryController {
    let reminder = peer?.id == context.account.peerId
    let hasSilentPosting = hasSilentPosting && peer?.id != context.account.peerId
    
    let legacyController = LegacyController(presentation: .custom, theme: presentationData.theme, initialLayout: nil)
    legacyController.statusBar.statusBarStyle = presentationData.theme.rootController.statusBarStyle.style
    
    let paintStickersContext = LegacyPaintStickersContext(context: context)
    paintStickersContext.captionPanelView = {
        return getCaptionPanelView()
    }
    
    let controller = TGModernGalleryController(context: legacyController.context)!
    controller.asyncTransitionIn = true
    legacyController.bind(controller: controller)
    
    let (items, focusItem): ([TGModernGalleryItem], TGModernGalleryItem?)
    switch source {
        case let .fetchResult(fetchResult, index, reversed):
            (items, focusItem) = galleryFetchResultItems(fetchResult: fetchResult, index: index, reversed: reversed, selectionContext: selectionContext, editingContext: editingContext, stickersContext: paintStickersContext, immediateThumbnail: immediateThumbnail)
        case let .selection(item):
            (items, focusItem) = gallerySelectionItems(item: item, selectionContext: selectionContext, editingContext: editingContext, stickersContext: paintStickersContext, immediateThumbnail: immediateThumbnail)
    }
    
    let recipientName: String?
    if let threadTitle {
        recipientName = threadTitle
    } else {
        if peer?.id == context.account.peerId {
            recipientName = presentationData.strings.DialogList_SavedMessages
        } else {
            recipientName = peer?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
        }
    }
    
    let model = TGMediaPickerGalleryModel(context: legacyController.context, items: items, focus: focusItem, selectionContext: selectionContext, editingContext: editingContext, hasCaptions: true, allowCaptionEntities: true, hasTimer: hasTimer, onlyCrop: false, inhibitDocumentCaptions: false, hasSelectionPanel: true, hasCamera: false, recipientName: recipientName, isScheduledMessages: isScheduledMessages)!
    model.stickersContext = paintStickersContext
    controller.model = model
    model.controller = controller
    model.willFinishEditingItem = { item, adjustments, representation, hasChanges in
        if hasChanges {
            editingContext.setAdjustments(adjustments, for: item)
            editingContext.setTemporaryRep(representation, for: item)
        }
        
        if let selectionContext = selectionContext, adjustments != nil, let item = item as? TGMediaSelectableItem {
            selectionContext.setItem(item, selected: true)
        }
    }
    model.didFinishEditingItem = { item, adjustments, result, thumbnail in
        editingContext.setImage(result, thumbnailImage: thumbnail, for: item, synchronous: false)
    }
    model.saveItemCaption = { item, caption in
        editingContext.setCaption(caption, for: item)
        if let selectionContext = selectionContext, let caption = caption, caption.length > 0, let item = item as? TGMediaSelectableItem {
            selectionContext.setItem(item, selected: true)
        }
    }
    model.didFinishRenderingFullSizeImage = { item, result in
        editingContext.setFullSizeImage(result, for: item)
    }
    model.requestAdjustments = { item in
        return editingContext.adjustments(for: item)
    }
    if let selectionContext = selectionContext {
        model.interfaceView.updateSelectionInterface(selectionContext.count(), counterVisible: selectionContext.count() > 0, animated: false)
    }
    controller.transitionHost = {
        return transitionHostView()
    }
    var transitionedIn = false
    controller.itemFocused = { item in
        if let item = item as? TGMediaPickerGalleryItem, transitionedIn {
            updateHiddenMedia(item.asset.uniqueIdentifier)
        }
    }
    controller.beginTransitionIn = { item, itemView in
        if let item = item as? TGMediaPickerGalleryItem {
            if let itemView = itemView as? TGMediaPickerGalleryVideoItemView {
                itemView.setIsCurrent(true)
            }
            
            return transitionView(item.asset.uniqueIdentifier)
        } else {
            return nil
        }
    }
    
    controller.startedTransitionIn = {
        transitionedIn = true
        if let focusItem = focusItem as? TGModernGallerySelectableItem {
            updateHiddenMedia(focusItem.selectableMediaItem().uniqueIdentifier)
        }
    }
    controller.beginTransitionOut = { item, itemView in
        willTransitionOut()
        
        if let item = item as? TGMediaPickerGalleryItem {
            if let itemView = itemView as? TGMediaPickerGalleryVideoItemView {
                itemView.stop()
            }
            return transitionView(item.asset.uniqueIdentifier)
        } else {
            return nil
        }
    }
    controller.finishedTransitionIn = { [weak model] _, _ in
        model?.interfaceView.setSelectedItemsModel(model?.selectedItemsModel)
        
        finishedTransitionIn()
    }
    controller.completedTransitionOut = { [weak legacyController] in
        updateHiddenMedia(nil)
        legacyController?.dismiss()
    }

    model.interfaceView.donePressed = { [weak controller] item in
        if let item = item as? TGMediaPickerGalleryItem {
            completed(item.asset, false, nil, {
                controller?.dismissWhenReady(animated: true)
                dismissAll()
            })
        }
    }
    if !isScheduledMessages && peer != nil {
        model.interfaceView.doneLongPressed = { [weak selectionContext, weak editingContext, weak legacyController, weak model] item in
            if let legacyController = legacyController, let item = item as? TGMediaPickerGalleryItem, let model = model, let selectionContext = selectionContext {
                var effectiveHasSchedule = hasSchedule
                
                if let editingContext = editingContext {
                    if let timer = editingContext.timer(for: item.asset)?.intValue, timer > 0 {
                        effectiveHasSchedule = false
                    }
                    for item in selectionContext.selectedItems() {
                        if let editableItem = item as? TGMediaEditableItem, let timer = editingContext.timer(for: editableItem)?.intValue, timer > 0 {
                            effectiveHasSchedule = false
                            break
                        }
                    }
                }
                
                let sendWhenOnlineAvailable: Signal<Bool, NoError>
                if let peer {
                    if case .secretChat = peer {
                        effectiveHasSchedule = false
                    }
                    sendWhenOnlineAvailable = context.account.viewTracker.peerView(peer.id)
                    |> take(1)
                    |> map { peerView -> Bool in
                        guard let peer = peerViewMainPeer(peerView) else {
                            return false
                        }
                        var sendWhenOnlineAvailable = false
                        if let presence = peerView.peerPresences[peer.id] as? TelegramUserPresence, case let .present(until) = presence.status {
                            let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
                            if currentTime > until {
                                sendWhenOnlineAvailable = true
                            }
                        }
                        if peer.id.namespace == Namespaces.Peer.CloudUser && peer.id.id._internalGetInt64Value() == 777000 {
                            sendWhenOnlineAvailable = false
                        }
                        return sendWhenOnlineAvailable
                    }
                } else {
                    sendWhenOnlineAvailable = .single(false)
                }
                
                let _ = (sendWhenOnlineAvailable
                |> take(1)
                |> deliverOnMainQueue).start(next: { sendWhenOnlineAvailable in
                    let legacySheetController = LegacyController(presentation: .custom, theme: presentationData.theme, initialLayout: nil)
                    let sheetController = TGMediaPickerSendActionSheetController(context: legacyController.context, isDark: true, sendButtonFrame: model.interfaceView.doneButtonFrame, canSendSilently: hasSilentPosting, canSendWhenOnline: sendWhenOnlineAvailable && effectiveHasSchedule, canSchedule: effectiveHasSchedule, reminder: reminder, hasTimer: hasTimer)
                    let dismissImpl = { [weak model] in
                        model?.dismiss(true, false)
                        dismissAll()
                    }
                    sheetController.send = {
                        completed(item.asset, false, nil, {
                            dismissImpl()
                        })
                    }
                    sheetController.sendSilently = { [weak model] in
                        model?.interfaceView.onDismiss()
                        
                        completed(item.asset, true, nil, {
                            dismissImpl()
                        })
                    }
                    sheetController.sendWhenOnline = {
                        completed(item.asset, false, scheduleWhenOnlineTimestamp, {
                            dismissImpl()
                        })
                    }
                    sheetController.schedule = {
                        presentSchedulePicker(true, { time in
                            completed(item.asset, false, time, {
                                dismissImpl()
                            })
                        })
                    }
                    sheetController.sendWithTimer = {
                        presentTimerPicker { time in
                            var items = selectionContext.selectedItems() ?? []
                            items.append(item.asset as Any)
                            
                            for case let item as TGMediaEditableItem in items {
                                editingContext?.setTimer(time as NSNumber, for: item)
                            }
                            
                            completed(item.asset, false, nil, {
                                dismissImpl()
                            })
                        }
                    }
                    sheetController.customDismissBlock = { [weak legacySheetController] in
                        legacySheetController?.dismiss()
                    }
                    legacySheetController.bind(controller: sheetController)
                    present(legacySheetController, nil)
                    
                    let hapticFeedback = HapticFeedback()
                    hapticFeedback.impact()
                })
            }
        }
    }
    model.interfaceView.setThumbnailSignalForItem { item in
        let imageSignal = SSignal(generator: { subscriber in
            var asset: PHAsset?
            if let item = item as? TGCameraCapturedVideo, item.originalAsset != nil {
                asset = item.originalAsset.backingAsset
            } else if let item = item as? TGMediaAsset {
                asset = item.backingAsset
            }
            var disposable: Disposable?
            if let asset = asset {
                let scale = min(2.0, UIScreenScale)
                disposable = assetImage(asset: asset, targetSize: CGSize(width: 128.0 * scale, height: 128.0 * scale), exact: false).start(next: { image in
                    subscriber.putNext(image)
                }, completed: {
                    subscriber.putCompletion()
                })
            } else {
                subscriber.putCompletion()
            }
            return SBlockDisposable(block: {
                disposable?.dispose()
            })
        })
        if let item = item as? TGMediaEditableItem {
            return editingContext.thumbnailImageSignal(for: item).map(toSignal: { result in
                if let result = result {
                    return SSignal.single(result)
                } else {
                    return imageSignal
                }
            })
        } else {
            return imageSignal
        }
    }
    present(legacyController, nil)
    
    return controller
}