// 
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import Foundation
import UIKit

@available (iOS 15.0, *)
struct PillAssetColor: Codable {
    var red: CGFloat = 0.0, green: CGFloat = 0.0, blue: CGFloat = 0.0, alpha: CGFloat = 0.0
    
    var uiColor: UIColor {
        return UIColor(red: red, green: green, blue: blue, alpha: alpha)
    }
    
    init(uiColor: UIColor) {
        uiColor.getRed(&red, green: &green, blue: &blue, alpha: &alpha)
    }
}

@available (iOS 15.0, *)
struct PillAssetParameter: Codable {
    var tintColor: PillAssetColor?
    var backgroundColor: PillAssetColor?
    var rawRenderingMode: Int = UIImage.RenderingMode.automatic.rawValue
    var padding: CGFloat = 2.0
}

@available (iOS 15.0, *)
enum PillTextAttachmentItem: Codable {
    case text(String)
    case avatar(url: String?, string: String?, matrixId: String)
    case spaceAvatar(url: String?, string: String?, matrixId: String)
    case asset(named: String, parameters: PillAssetParameter)
}

@available (iOS 15.0, *)
extension PillTextAttachmentItem {
    var string: String? {
        switch self {
        case .text(let text):
            return text
        default:
            return nil
        }
    }
}

/// Data associated with a Pill text attachment.
@available (iOS 15.0, *)
struct PillTextAttachmentData: Codable {
    // MARK: - Properties
    /// Pill type
    var pillType: PillType
    /// Items to render
    var items: [PillTextAttachmentItem]
    /// Wether the pill should be highlighted
    var isHighlighted: Bool
    /// Alpha for pill display
    var alpha: CGFloat
    /// Font for the display name
    var font: UIFont
    /// Max width
    var maxWidth: CGFloat

    /// Helper for preferred text to display.
    var displayText: String {
        return items.map { $0.string }
            .compactMap { $0 }
            .joined(separator: " ")
    }
        
    // MARK: - Init
    /// Init.
    ///
    /// - Parameters:
    ///   - pillType: Type for the pill
    ///   - items: Items to display
    ///   - isHighlighted: Wether the pill should be highlighted
    ///   - alpha: Alpha for pill display
    ///   - font: Font for the display name
    init(pillType: PillType,
         items: [PillTextAttachmentItem],
         isHighlighted: Bool,
         alpha: CGFloat,
         font: UIFont,
         maxWidth: CGFloat = .greatestFiniteMagnitude) {
        self.pillType = pillType
        self.items = items
        self.isHighlighted = isHighlighted
        self.alpha = alpha
        self.font = font
        self.maxWidth = maxWidth
    }
    
    // MARK: - Codable
    enum CodingKeys: String, CodingKey {
        case pillType
        case items
        case isHighlighted
        case alpha
        case font
    }

    enum PillTextAttachmentDataError: Error {
        case noFontData
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        pillType = try container.decode(PillType.self, forKey: .pillType)
        items = try container.decode([PillTextAttachmentItem].self, forKey: .items)
        isHighlighted = try container.decode(Bool.self, forKey: .isHighlighted)
        alpha = try container.decode(CGFloat.self, forKey: .alpha)
        let fontData = try container.decode(Data.self, forKey: .font)
        if let font = try NSKeyedUnarchiver.unarchivedObject(ofClass: UIFont.self, from: fontData) {
            self.font = font
        } else {
            throw PillTextAttachmentDataError.noFontData
        }
        maxWidth = .greatestFiniteMagnitude
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(pillType, forKey: .pillType)
        try container.encode(items, forKey: .items)
        try container.encode(isHighlighted, forKey: .isHighlighted)
        try container.encode(alpha, forKey: .alpha)
        let fontData = try NSKeyedArchiver.archivedData(withRootObject: font, requiringSecureCoding: false)
        try container.encode(fontData, forKey: .font)
    }
    
    // MARK: - Pill representations
    var pillIdentifier: String {
        switch pillType {
        case .user(let userId):
            return userId
        case .room(let roomId):
            return roomId
        case .message(let roomId, let messageId):
            return "\(roomId)/\(messageId)"
        }
    }
    
    var markdown: String {
        var permalink: String
        switch pillType {
        case .user(let userId):
            permalink = MXTools.permalinkToUser(withUserId: userId)
        case .room(let roomId):
            permalink = MXTools.permalink(toRoom: roomId)
        case .message(let roomId, let messageId):
            permalink = MXTools.permalink(toEvent: messageId, inRoom: roomId)
        }
        return "[\(displayText)](\(permalink))"
    }
}
