import Foundation import TelegramCore public func decodeCodableDrawingEntities(data: Data) -> [CodableDrawingEntity] { if let codableEntities = try? JSONDecoder().decode([CodableDrawingEntity].self, from: data) { return codableEntities } return [] } public func decodeDrawingEntities(data: Data) -> [DrawingEntity] { return decodeCodableDrawingEntities(data: data).map { $0.entity } } public enum CodableDrawingEntity: Equatable { public static func == (lhs: CodableDrawingEntity, rhs: CodableDrawingEntity) -> Bool { return lhs.entity.isEqual(to: rhs.entity) } case sticker(DrawingStickerEntity) case text(DrawingTextEntity) case simpleShape(DrawingSimpleShapeEntity) case bubble(DrawingBubbleEntity) case vector(DrawingVectorEntity) case location(DrawingLocationEntity) public init?(entity: DrawingEntity) { if let entity = entity as? DrawingStickerEntity { self = .sticker(entity) } else if let entity = entity as? DrawingTextEntity { self = .text(entity) } else if let entity = entity as? DrawingSimpleShapeEntity { self = .simpleShape(entity) } else if let entity = entity as? DrawingBubbleEntity { self = .bubble(entity) } else if let entity = entity as? DrawingVectorEntity { self = .vector(entity) } else if let entity = entity as? DrawingLocationEntity { self = .location(entity) } else { return nil } } public var entity: DrawingEntity { switch self { case let .sticker(entity): return entity case let .text(entity): return entity case let .simpleShape(entity): return entity case let .bubble(entity): return entity case let .vector(entity): return entity case let .location(entity): return entity } } private var coordinates: MediaArea.Coordinates? { var position: CGPoint? var size: CGSize? var scale: CGFloat? var rotation: CGFloat? switch self { case let .location(entity): position = entity.position size = entity.renderImage?.size scale = entity.scale rotation = entity.rotation case let .sticker(entity): position = entity.position size = entity.baseSize scale = entity.scale rotation = entity.rotation default: return nil } guard let position, let size, let scale, let rotation else { return nil } return MediaArea.Coordinates( x: position.x / 1080.0 * 100.0, y: position.y / 1920.0 * 100.0, width: size.width * scale / 1080.0 * 100.0, height: size.height * scale / 1920.0 * 100.0, rotation: rotation / .pi * 180.0 ) } public var mediaArea: MediaArea? { guard let coordinates = self.coordinates else { return nil } switch self { case let .location(entity): return .venue( coordinates: coordinates, venue: MediaArea.Venue( latitude: entity.location.latitude, longitude: entity.location.longitude, venue: entity.location.venue, queryId: entity.queryId, resultId: entity.resultId ) ) case let .sticker(entity): if case let .file(_, type) = entity.content, case let .reaction(reaction, style) = type { var flags: MediaArea.ReactionFlags = [] if case .black = style { flags.insert(.isDark) } if entity.mirrored { flags.insert(.isFlipped) } return .reaction( coordinates: coordinates, reaction: reaction, flags: flags ) } else { return nil } default: return nil } } } extension CodableDrawingEntity: Codable { private enum CodingKeys: String, CodingKey { case type case entity } private enum EntityType: Int, Codable { case sticker case text case simpleShape case bubble case vector case location } public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) let type = try container.decode(EntityType.self, forKey: .type) switch type { case .sticker: self = .sticker(try container.decode(DrawingStickerEntity.self, forKey: .entity)) case .text: self = .text(try container.decode(DrawingTextEntity.self, forKey: .entity)) case .simpleShape: self = .simpleShape(try container.decode(DrawingSimpleShapeEntity.self, forKey: .entity)) case .bubble: self = .bubble(try container.decode(DrawingBubbleEntity.self, forKey: .entity)) case .vector: self = .vector(try container.decode(DrawingVectorEntity.self, forKey: .entity)) case .location: self = .location(try container.decode(DrawingLocationEntity.self, forKey: .entity)) } } public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) switch self { case let .sticker(payload): try container.encode(EntityType.sticker, forKey: .type) try container.encode(payload, forKey: .entity) case let .text(payload): try container.encode(EntityType.text, forKey: .type) try container.encode(payload, forKey: .entity) case let .simpleShape(payload): try container.encode(EntityType.simpleShape, forKey: .type) try container.encode(payload, forKey: .entity) case let .bubble(payload): try container.encode(EntityType.bubble, forKey: .type) try container.encode(payload, forKey: .entity) case let .vector(payload): try container.encode(EntityType.vector, forKey: .type) try container.encode(payload, forKey: .entity) case let .location(payload): try container.encode(EntityType.location, forKey: .type) try container.encode(payload, forKey: .entity) } } }