use crate::cdsl::operands::{EnumValues, OperandKind, OperandKindFields};

use std::collections::HashMap;

pub(crate) struct Immediates {
    /// A 64-bit immediate integer operand.
    ///
    /// This type of immediate integer can interact with SSA values with any IntType type.
    pub imm64: OperandKind,

    /// An unsigned 8-bit immediate integer operand.
    ///
    /// This small operand is used to indicate lane indexes in SIMD vectors and immediate bit
    /// counts on shift instructions.
    pub uimm8: OperandKind,

    /// An unsigned 128-bit immediate integer operand.
    ///
    /// This operand is used to pass entire 128-bit vectors as immediates to instructions like
    /// const.
    pub uimm128: OperandKind,

    /// A constant stored in the constant pool.
    ///
    /// This operand is used to pass constants to instructions like vconst while storing the
    /// actual bytes in the constant pool.
    pub pool_constant: OperandKind,

    /// A 32-bit immediate signed offset.
    ///
    /// This is used to represent an immediate address offset in load/store instructions.
    pub offset32: OperandKind,

    /// A 32-bit immediate floating point operand.
    ///
    /// IEEE 754-2008 binary32 interchange format.
    pub ieee32: OperandKind,

    /// A 64-bit immediate floating point operand.
    ///
    /// IEEE 754-2008 binary64 interchange format.
    pub ieee64: OperandKind,

    /// A condition code for comparing integer values.
    ///
    /// This enumerated operand kind is used for the `icmp` instruction and corresponds to the
    /// condcodes::IntCC` Rust type.
    pub intcc: OperandKind,

    /// A condition code for comparing floating point values.
    ///
    /// This enumerated operand kind is used for the `fcmp` instruction and corresponds to the
    /// `condcodes::FloatCC` Rust type.
    pub floatcc: OperandKind,

    /// Flags for memory operations like `load` and `store`.
    pub memflags: OperandKind,

    /// A trap code indicating the reason for trapping.
    ///
    /// The Rust enum type also has a `User(u16)` variant for user-provided trap codes.
    pub trapcode: OperandKind,

    /// A code indicating the arithmetic operation to perform in an atomic_rmw memory access.
    pub atomic_rmw_op: OperandKind,
}

fn new_imm(
    format_field_name: &'static str,
    rust_type: &'static str,
    doc: &'static str,
) -> OperandKind {
    OperandKind::new(
        format_field_name,
        rust_type,
        OperandKindFields::ImmValue,
        doc,
    )
}
fn new_enum(
    format_field_name: &'static str,
    rust_type: &'static str,
    values: EnumValues,
    doc: &'static str,
) -> OperandKind {
    OperandKind::new(
        format_field_name,
        rust_type,
        OperandKindFields::ImmEnum(values),
        doc,
    )
}

impl Immediates {
    pub fn new() -> Self {
        Self {
            imm64: new_imm(
                "imm",
                "ir::immediates::Imm64",
                "A 64-bit immediate integer.",
            ),
            uimm8: new_imm(
                "imm",
                "ir::immediates::Uimm8",
                "An 8-bit immediate unsigned integer.",
            ),
            uimm128: new_imm(
                "imm",
                "ir::Immediate",
                "A 128-bit immediate unsigned integer.",
            ),
            pool_constant: new_imm(
                "constant_handle",
                "ir::Constant",
                "A constant stored in the constant pool.",
            ),
            offset32: new_imm(
                "offset",
                "ir::immediates::Offset32",
                "A 32-bit immediate signed offset.",
            ),
            ieee32: new_imm(
                "imm",
                "ir::immediates::Ieee32",
                "A 32-bit immediate floating point number.",
            ),
            ieee64: new_imm(
                "imm",
                "ir::immediates::Ieee64",
                "A 64-bit immediate floating point number.",
            ),
            intcc: {
                let mut intcc_values = HashMap::new();
                intcc_values.insert("eq", "Equal");
                intcc_values.insert("ne", "NotEqual");
                intcc_values.insert("sge", "SignedGreaterThanOrEqual");
                intcc_values.insert("sgt", "SignedGreaterThan");
                intcc_values.insert("sle", "SignedLessThanOrEqual");
                intcc_values.insert("slt", "SignedLessThan");
                intcc_values.insert("uge", "UnsignedGreaterThanOrEqual");
                intcc_values.insert("ugt", "UnsignedGreaterThan");
                intcc_values.insert("ule", "UnsignedLessThanOrEqual");
                intcc_values.insert("ult", "UnsignedLessThan");
                new_enum(
                    "cond",
                    "ir::condcodes::IntCC",
                    intcc_values,
                    "An integer comparison condition code.",
                )
            },

            floatcc: {
                let mut floatcc_values = HashMap::new();
                floatcc_values.insert("ord", "Ordered");
                floatcc_values.insert("uno", "Unordered");
                floatcc_values.insert("eq", "Equal");
                floatcc_values.insert("ne", "NotEqual");
                floatcc_values.insert("one", "OrderedNotEqual");
                floatcc_values.insert("ueq", "UnorderedOrEqual");
                floatcc_values.insert("lt", "LessThan");
                floatcc_values.insert("le", "LessThanOrEqual");
                floatcc_values.insert("gt", "GreaterThan");
                floatcc_values.insert("ge", "GreaterThanOrEqual");
                floatcc_values.insert("ult", "UnorderedOrLessThan");
                floatcc_values.insert("ule", "UnorderedOrLessThanOrEqual");
                floatcc_values.insert("ugt", "UnorderedOrGreaterThan");
                floatcc_values.insert("uge", "UnorderedOrGreaterThanOrEqual");
                new_enum(
                    "cond",
                    "ir::condcodes::FloatCC",
                    floatcc_values,
                    "A floating point comparison condition code",
                )
            },

            memflags: new_imm("flags", "ir::MemFlags", "Memory operation flags"),

            trapcode: {
                let mut trapcode_values = HashMap::new();
                trapcode_values.insert("stk_ovf", "StackOverflow");
                trapcode_values.insert("heap_oob", "HeapOutOfBounds");
                trapcode_values.insert("int_ovf", "IntegerOverflow");
                trapcode_values.insert("int_divz", "IntegerDivisionByZero");
                new_enum(
                    "code",
                    "ir::TrapCode",
                    trapcode_values,
                    "A trap reason code.",
                )
            },
            atomic_rmw_op: {
                let mut atomic_rmw_op_values = HashMap::new();
                atomic_rmw_op_values.insert("add", "Add");
                atomic_rmw_op_values.insert("sub", "Sub");
                atomic_rmw_op_values.insert("and", "And");
                atomic_rmw_op_values.insert("nand", "Nand");
                atomic_rmw_op_values.insert("or", "Or");
                atomic_rmw_op_values.insert("xor", "Xor");
                atomic_rmw_op_values.insert("xchg", "Xchg");
                atomic_rmw_op_values.insert("umin", "Umin");
                atomic_rmw_op_values.insert("umax", "Umax");
                atomic_rmw_op_values.insert("smin", "Smin");
                atomic_rmw_op_values.insert("smax", "Smax");
                new_enum(
                    "op",
                    "ir::AtomicRmwOp",
                    atomic_rmw_op_values,
                    "Atomic Read-Modify-Write Ops",
                )
            },
        }
    }
}
