import Postbox

private enum TelegramMediaWebpageAttributeTypes: Int32 {
    case unsupported
    case theme
}

public enum TelegramMediaWebpageAttribute: PostboxCoding, Equatable {
    case unsupported
    case theme(TelegraMediaWebpageThemeAttribute)
    
    public init(decoder: PostboxDecoder) {
        switch decoder.decodeInt32ForKey("r", orElse: 0) {
            case TelegramMediaWebpageAttributeTypes.theme.rawValue:
                self = .theme(decoder.decodeObjectForKey("a", decoder: { TelegraMediaWebpageThemeAttribute(decoder: $0) }) as! TelegraMediaWebpageThemeAttribute)
            default:
                self = .unsupported
        }
    }
    
    public func encode(_ encoder: PostboxEncoder) {
        switch self {
            case .unsupported:
                encoder.encodeInt32(TelegramMediaWebpageAttributeTypes.unsupported.rawValue, forKey: "r")
            case let .theme(attribute):
                encoder.encodeInt32(TelegramMediaWebpageAttributeTypes.theme.rawValue, forKey: "r")
                encoder.encodeObject(attribute, forKey: "a")
        }
    }
}

public final class TelegraMediaWebpageThemeAttribute: PostboxCoding, Equatable {
    public static func == (lhs: TelegraMediaWebpageThemeAttribute, rhs: TelegraMediaWebpageThemeAttribute) -> Bool {
        if lhs.settings != rhs.settings {
            return false
        }
        if lhs.files.count != rhs.files.count {
            return false
        } else {
            for i in 0 ..< lhs.files.count {
                if !lhs.files[i].isEqual(to: rhs.files[i]) {
                    return false
                }
            }
        }
        return true
    }
    
    public let files: [TelegramMediaFile]
    public let settings: TelegramThemeSettings?
    
    public init(files: [TelegramMediaFile], settings: TelegramThemeSettings?) {
        self.files = files
        self.settings = settings
    }
    
    public init(decoder: PostboxDecoder) {
        self.files = decoder.decodeObjectArrayForKey("files")
        self.settings = decoder.decodeObjectForKey("settings", decoder: { TelegramThemeSettings(decoder: $0) }) as? TelegramThemeSettings
    }
    
    public func encode(_ encoder: PostboxEncoder) {
        encoder.encodeObjectArray(self.files, forKey: "files")
        if let settings = self.settings {
            encoder.encodeObject(settings, forKey: "settings")
        } else {
            encoder.encodeNil(forKey: "settings")
        }
    }
}

public final class TelegramMediaWebpageLoadedContent: PostboxCoding, Equatable {
    public let url: String
    public let displayUrl: String
    public let hash: Int32
    public let type: String?
    public let websiteName: String?
    public let title: String?
    public let text: String?
    public let embedUrl: String?
    public let embedType: String?
    public let embedSize: PixelDimensions?
    public let duration: Int?
    public let author: String?
    
    public let image: TelegramMediaImage?
    public let file: TelegramMediaFile?
    public let attributes: [TelegramMediaWebpageAttribute]
    public let instantPage: InstantPage?
    
    public init(url: String, displayUrl: String, hash: Int32, type: String?, websiteName: String?, title: String?, text: String?, embedUrl: String?, embedType: String?, embedSize: PixelDimensions?, duration: Int?, author: String?, image: TelegramMediaImage?, file: TelegramMediaFile?, attributes: [TelegramMediaWebpageAttribute], instantPage: InstantPage?) {
        self.url = url
        self.displayUrl = displayUrl
        self.hash = hash
        self.type = type
        self.websiteName = websiteName
        self.title = title
        self.text = text
        self.embedUrl = embedUrl
        self.embedType = embedType
        self.embedSize = embedSize
        self.duration = duration
        self.author = author
        self.image = image
        self.file = file
        self.attributes = attributes
        self.instantPage = instantPage
    }
    
    public init(decoder: PostboxDecoder) {
        self.url = decoder.decodeStringForKey("u", orElse: "")
        self.displayUrl = decoder.decodeStringForKey("d", orElse: "")
        self.hash = decoder.decodeInt32ForKey("ha", orElse: 0)
        self.type = decoder.decodeOptionalStringForKey("ty")
        self.websiteName = decoder.decodeOptionalStringForKey("ws")
        self.title = decoder.decodeOptionalStringForKey("ti")
        self.text = decoder.decodeOptionalStringForKey("tx")
        self.embedUrl = decoder.decodeOptionalStringForKey("eu")
        self.embedType = decoder.decodeOptionalStringForKey("et")
        if let embedSizeWidth = decoder.decodeOptionalInt32ForKey("esw"), let embedSizeHeight = decoder.decodeOptionalInt32ForKey("esh") {
            self.embedSize = PixelDimensions(width: embedSizeWidth, height: embedSizeHeight)
        } else {
            self.embedSize = nil
        }
        if let duration = decoder.decodeOptionalInt32ForKey("du") {
            self.duration = Int(duration)
        } else {
            self.duration = nil
        }
        self.author = decoder.decodeOptionalStringForKey("au")
        
        if let image = decoder.decodeObjectForKey("im") as? TelegramMediaImage {
            self.image = image
        } else {
            self.image = nil
        }
        
        if let file = decoder.decodeObjectForKey("fi") as? TelegramMediaFile {
            self.file = file
        } else {
            self.file = nil
        }
        
        var effectiveAttributes: [TelegramMediaWebpageAttribute] = []
        if let attributes = decoder.decodeObjectArrayWithDecoderForKey("attr") as [TelegramMediaWebpageAttribute]? {
            effectiveAttributes.append(contentsOf: attributes)
        }
        if let legacyFiles = decoder.decodeOptionalObjectArrayWithDecoderForKey("fis") as [TelegramMediaFile]? {
            effectiveAttributes.append(.theme(TelegraMediaWebpageThemeAttribute(files: legacyFiles, settings: nil)))
        }
        self.attributes = effectiveAttributes
        
        if let instantPage = decoder.decodeObjectForKey("ip", decoder: { InstantPage(decoder: $0) }) as? InstantPage {
            self.instantPage = instantPage
        } else {
            self.instantPage = nil
        }
    }
    
    public func encode(_ encoder: PostboxEncoder) {
        encoder.encodeString(self.url, forKey: "u")
        encoder.encodeString(self.displayUrl, forKey: "d")
        encoder.encodeInt32(self.hash, forKey: "ha")
        if let type = self.type {
            encoder.encodeString(type, forKey: "ty")
        } else {
            encoder.encodeNil(forKey: "ty")
        }
        if let websiteName = self.websiteName {
            encoder.encodeString(websiteName, forKey: "ws")
        } else {
            encoder.encodeNil(forKey: "ws")
        }
        if let title = self.title {
            encoder.encodeString(title, forKey: "ti")
        } else {
            encoder.encodeNil(forKey: "ti")
        }
        if let text = self.text {
            encoder.encodeString(text, forKey: "tx")
        } else {
            encoder.encodeNil(forKey: "tx")
        }
        if let embedUrl = self.embedUrl {
            encoder.encodeString(embedUrl, forKey: "eu")
        } else {
            encoder.encodeNil(forKey: "eu")
        }
        if let embedType = self.embedType {
            encoder.encodeString(embedType, forKey: "et")
        } else {
            encoder.encodeNil(forKey: "et")
        }
        if let embedSize = self.embedSize {
            encoder.encodeInt32(Int32(embedSize.width), forKey: "esw")
            encoder.encodeInt32(Int32(embedSize.height), forKey: "esh")
        } else {
            encoder.encodeNil(forKey: "esw")
            encoder.encodeNil(forKey: "esh")
        }
        if let duration = self.duration {
            encoder.encodeInt32(Int32(duration), forKey: "du")
        } else {
            encoder.encodeNil(forKey: "du")
        }
        if let author = self.author {
            encoder.encodeString(author, forKey: "au")
        } else {
            encoder.encodeNil(forKey: "au")
        }
        if let image = self.image {
            encoder.encodeObject(image, forKey: "im")
        } else {
            encoder.encodeNil(forKey: "im")
        }
        if let file = self.file {
            encoder.encodeObject(file, forKey: "fi")
        } else {
            encoder.encodeNil(forKey: "fi")
        }
        
        encoder.encodeObjectArray(self.attributes, forKey: "attr")
        
        if let instantPage = self.instantPage {
            encoder.encodeObject(instantPage, forKey: "ip")
        } else {
            encoder.encodeNil(forKey: "ip")
        }
    }
}

public func ==(lhs: TelegramMediaWebpageLoadedContent, rhs: TelegramMediaWebpageLoadedContent) -> Bool {
    if lhs.url != rhs.url
    || lhs.displayUrl != rhs.displayUrl
    || lhs.hash != rhs.hash
    || lhs.type != rhs.type
    || lhs.websiteName != rhs.websiteName
    || lhs.title != rhs.title
    || lhs.text != rhs.text
    || lhs.embedUrl != rhs.embedUrl
    || lhs.embedType != rhs.embedType
    || lhs.embedSize != rhs.embedSize
    || lhs.duration != rhs.duration
    || lhs.author != rhs.author {
        return false
    }
    
    if let lhsImage = lhs.image, let rhsImage = rhs.image {
        if !lhsImage.isEqual(to: rhsImage) {
            return false
        }
    } else if (lhs.image == nil) != (rhs.image == nil) {
        return false
    }
    
    if let lhsFile = lhs.file, let rhsFile = rhs.file {
        if !lhsFile.isEqual(to: rhsFile) {
            return false
        }
    } else if (lhs.file == nil) != (rhs.file == nil) {
        return false
    }
    
    if lhs.attributes.count != rhs.attributes.count {
        return false
    } else {
        for i in 0 ..< lhs.attributes.count {
            if lhs.attributes[i] != rhs.attributes[i] {
                return false
            }
        }
    }
    
    if lhs.instantPage != rhs.instantPage {
        return false
    }
    
    return true
}

public enum TelegramMediaWebpageContent {
    case Pending(Int32, String?)
    case Loaded(TelegramMediaWebpageLoadedContent)
}

public final class TelegramMediaWebpage: Media, Equatable {
    public var id: MediaId? {
        return self.webpageId
    }
    public let peerIds: [PeerId] = []
    
    public let webpageId: MediaId
    public let content: TelegramMediaWebpageContent
    
    public init(webpageId: MediaId, content: TelegramMediaWebpageContent) {
        self.webpageId = webpageId
        self.content = content
    }
    
    public init(decoder: PostboxDecoder) {
        self.webpageId = MediaId(decoder.decodeBytesForKeyNoCopy("i")!)
        
        if decoder.decodeInt32ForKey("ct", orElse: 0) == 0 {
            self.content = .Pending(decoder.decodeInt32ForKey("pendingDate", orElse: 0), decoder.decodeOptionalStringForKey("pendingUrl"))
        } else {
            self.content = .Loaded(TelegramMediaWebpageLoadedContent(decoder: decoder))
        }
    }
    
    public func encode(_ encoder: PostboxEncoder) {
        let buffer = WriteBuffer()
        self.webpageId.encodeToBuffer(buffer)
        encoder.encodeBytes(buffer, forKey: "i")
        
        switch self.content {
            case let .Pending(date, url):
                encoder.encodeInt32(0, forKey: "ct")
                encoder.encodeInt32(date, forKey: "pendingDate")
                if let url = url {
                    encoder.encodeString(url, forKey: "pendingUrl")
                } else {
                    encoder.encodeNil(forKey: "pendingUrl")
                }
            case let .Loaded(loadedContent):
                encoder.encodeInt32(1, forKey: "ct")
                loadedContent.encode(encoder)
        }
    }
    
    public func isLikelyToBeUpdated() -> Bool {
        return true
    }
    
    public func isEqual(to other: Media) -> Bool {
        if let other = other as? TelegramMediaWebpage, self.webpageId == other.webpageId {
            return self == other
        }
        return false
    }
    
    public func isSemanticallyEqual(to other: Media) -> Bool {
        return self.isEqual(to: other)
    }
    
    public static func ==(lhs: TelegramMediaWebpage, rhs: TelegramMediaWebpage) -> Bool {
        if lhs.webpageId != rhs.webpageId {
            return false
        }
        
        switch lhs.content {
            case let .Pending(lhsDate, lhsUrl):
                switch rhs.content {
                    case let .Pending(rhsDate, rhsUrl):
                        if lhsDate == rhsDate, lhsUrl == rhsUrl {
                            return true
                        } else {
                            return false
                        }
                    default:
                        return false
                }
            case let .Loaded(lhsContent):
                switch rhs.content {
                    case let .Loaded(rhsContent) where lhsContent == rhsContent:
                        return true
                    default:
                        return false
                }
        }
    }
}