import Foundation
import UIKit
import AsyncDisplayKit
import Display
import Postbox
import TelegramCore
import SyncCore
import TelegramUIPreferences
import TelegramPresentationData
import AccountContext

enum ChatMessageBubbleContentBackgroundHiding {
    case never
    case emptyWallpaper
    case always
}

enum ChatMessageBubbleContentAlignment {
    case none
    case center
}

struct ChatMessageBubbleContentProperties {
    let hidesSimpleAuthorHeader: Bool
    let headerSpacing: CGFloat
    let hidesBackground: ChatMessageBubbleContentBackgroundHiding
    let forceFullCorners: Bool
    let forceAlignment: ChatMessageBubbleContentAlignment
}

enum ChatMessageBubbleNoneMergeStatus {
    case Incoming
    case Outgoing
    case None
}

enum ChatMessageBubbleMergeStatus {
    case None(ChatMessageBubbleNoneMergeStatus)
    case Left
    case Right
}

enum ChatMessageBubbleRelativePosition {
    enum NeighbourType {
        case media
        case freeform
    }
    
    enum NeighbourSpacing {
        case `default`
        case condensed
    }
    
    case None(ChatMessageBubbleMergeStatus)
    case BubbleNeighbour
    case Neighbour(Bool, NeighbourType, NeighbourSpacing)
}

enum ChatMessageBubbleContentMosaicNeighbor {
    case merged
    case mergedBubble
    case none(tail: Bool)
}

struct ChatMessageBubbleContentMosaicPosition {
    let topLeft: ChatMessageBubbleContentMosaicNeighbor
    let topRight: ChatMessageBubbleContentMosaicNeighbor
    let bottomLeft: ChatMessageBubbleContentMosaicNeighbor
    let bottomRight: ChatMessageBubbleContentMosaicNeighbor
}

enum ChatMessageBubbleContentPosition {
    case linear(top: ChatMessageBubbleRelativePosition, bottom: ChatMessageBubbleRelativePosition)
    case mosaic(position: ChatMessageBubbleContentMosaicPosition, wide: Bool)
}

enum ChatMessageBubblePreparePosition {
    case linear(top: ChatMessageBubbleRelativePosition, bottom: ChatMessageBubbleRelativePosition)
    case mosaic(top: ChatMessageBubbleRelativePosition, bottom: ChatMessageBubbleRelativePosition)
}

enum ChatMessageBubbleContentTapAction {
    case none
    case url(url: String, concealed: Bool)
    case textMention(String)
    case peerMention(PeerId, String)
    case botCommand(String)
    case hashtag(String?, String)
    case instantPage
    case wallpaper
    case theme
    case call(peerId: PeerId, isVideo: Bool)
    case openMessage
    case timecode(Double, String)
    case tooltip(String, ASDisplayNode?, CGRect?)
    case bankCard(String)
    case ignore
    case openPollResults(Data)
}

final class ChatMessageBubbleContentItem {
    let context: AccountContext
    let controllerInteraction: ChatControllerInteraction
    let message: Message
    let read: Bool
    let chatLocation: ChatLocation
    let presentationData: ChatPresentationData
    let associatedData: ChatMessageItemAssociatedData
    let attributes: ChatMessageEntryAttributes
    
    init(context: AccountContext, controllerInteraction: ChatControllerInteraction, message: Message, read: Bool, chatLocation: ChatLocation, presentationData: ChatPresentationData, associatedData: ChatMessageItemAssociatedData, attributes: ChatMessageEntryAttributes) {
        self.context = context
        self.controllerInteraction = controllerInteraction
        self.message = message
        self.read = read
        self.chatLocation = chatLocation
        self.presentationData = presentationData
        self.associatedData = associatedData
        self.attributes = attributes
    }
}

class ChatMessageBubbleContentNode: ASDisplayNode {
    var supportsMosaic: Bool {
        return false
    }
    
    var visibility: ListViewItemNodeVisibility = .none
    
    var item: ChatMessageBubbleContentItem?
    
    var updateIsTextSelectionActive: ((Bool) -> Void)?
    
    required override init() {
        super.init()
    }
    
    func asyncLayoutContent() -> (_ item: ChatMessageBubbleContentItem, _ layoutConstants: ChatMessageItemLayoutConstants, _ preparePosition: ChatMessageBubblePreparePosition, _ messageSelection: Bool?, _ constrainedSize: CGSize) -> (ChatMessageBubbleContentProperties, unboundSize: CGSize?, maxWidth: CGFloat, layout: (CGSize, ChatMessageBubbleContentPosition) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool) -> Void))) {
        preconditionFailure()
    }
    
    func animateInsertion(_ currentTimestamp: Double, duration: Double) {
    }
    
    func animateAdded(_ currentTimestamp: Double, duration: Double) {
    }
    
    func animateRemoved(_ currentTimestamp: Double, duration: Double) {
    }
    
    func animateInsertionIntoBubble(_ duration: Double) {
    }
    
    func animateRemovalFromBubble(_ duration: Double, completion: @escaping () -> Void) {
        self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { _ in
            completion()
        })
    }
    
    func transitionNode(messageId: MessageId, media: Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? {
        return nil
    }
    
    func peekPreviewContent(at point: CGPoint) -> (Message, ChatMessagePeekPreviewContent)? {
        return nil
    }
    
    func updateHiddenMedia(_ media: [Media]?) -> Bool {
        return false
    }
    
    func updateSearchTextHighlightState(text: String?, messages: [MessageIndex]?) {
    }
    
    func updateAutomaticMediaDownloadSettings(_ settings: MediaAutoDownloadSettings) {
    }
        
    func playMediaWithSound() -> ((Double?) -> Void, Bool, Bool, Bool, ASDisplayNode?)? {
        return nil
    }
    
    func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction {
        return .none
    }
    
    func updateTouchesAtPoint(_ point: CGPoint?) {
    }
    
    func updateHighlightedState(animated: Bool) -> Bool {
        return false
    }
    
    func willUpdateIsExtractedToContextPreview(_ value: Bool) {    
    }
    
    func updateIsExtractedToContextPreview(_ value: Bool) {
    }
    
    func reactionTargetNode(value: String) -> (ASDisplayNode, ASDisplayNode)? {
        return nil
    }
}