use std::fmt;

use rustc_span::Symbol;

use super::{InlineAsmArch, InlineAsmType, ModifierInfo};

def_reg_class! {
    Hexagon HexagonInlineAsmRegClass {
        reg,
        reg_pair,
        preg,
        vreg,
        vreg_pair,
        qreg,
    }
}

impl HexagonInlineAsmRegClass {
    pub fn valid_modifiers(self, _arch: super::InlineAsmArch) -> &'static [char] {
        &[]
    }

    pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option<Self> {
        None
    }

    pub fn suggest_modifier(
        self,
        _arch: InlineAsmArch,
        _ty: InlineAsmType,
    ) -> Option<ModifierInfo> {
        None
    }

    pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<ModifierInfo> {
        None
    }

    pub fn supported_types(
        self,
        _arch: InlineAsmArch,
    ) -> &'static [(InlineAsmType, Option<Symbol>)] {
        match self {
            Self::reg => types! { _: I8, I16, I32, F32; },
            Self::reg_pair => types! { _: I64, F64; },
            Self::preg => &[],
            Self::vreg => types! {
                hvx_length64b: VecI32(16);
                hvx_length128b: VecI32(32);
            },
            Self::vreg_pair => types! {
                hvx_length64b: VecI32(32);
                hvx_length128b: VecI32(64);
            },
            Self::qreg => &[],
        }
    }
}

def_regs! {
    Hexagon HexagonInlineAsmReg HexagonInlineAsmRegClass {
        r0: reg = ["r0"],
        r1: reg = ["r1"],
        r2: reg = ["r2"],
        r3: reg = ["r3"],
        r4: reg = ["r4"],
        r5: reg = ["r5"],
        r6: reg = ["r6"],
        r7: reg = ["r7"],
        r8: reg = ["r8"],
        r9: reg = ["r9"],
        r10: reg = ["r10"],
        r11: reg = ["r11"],
        r12: reg = ["r12"],
        r13: reg = ["r13"],
        r14: reg = ["r14"],
        r15: reg = ["r15"],
        r16: reg = ["r16"],
        r17: reg = ["r17"],
        r18: reg = ["r18"],
        r20: reg = ["r20"],
        r21: reg = ["r21"],
        r22: reg = ["r22"],
        r23: reg = ["r23"],
        r24: reg = ["r24"],
        r25: reg = ["r25"],
        r26: reg = ["r26"],
        r27: reg = ["r27"],
        r28: reg = ["r28"],
        r1_0: reg_pair = ["r1:0"],
        r3_2: reg_pair = ["r3:2"],
        r5_4: reg_pair = ["r5:4"],
        r7_6: reg_pair = ["r7:6"],
        r9_8: reg_pair = ["r9:8"],
        r11_10: reg_pair = ["r11:10"],
        r13_12: reg_pair = ["r13:12"],
        r15_14: reg_pair = ["r15:14"],
        r17_16: reg_pair = ["r17:16"],
        r21_20: reg_pair = ["r21:20"],
        r23_22: reg_pair = ["r23:22"],
        r25_24: reg_pair = ["r25:24"],
        r27_26: reg_pair = ["r27:26"],
        p0: preg = ["p0"],
        p1: preg = ["p1"],
        p2: preg = ["p2"],
        p3: preg = ["p3"],
        v0: vreg = ["v0"],
        v1: vreg = ["v1"],
        v2: vreg = ["v2"],
        v3: vreg = ["v3"],
        v4: vreg = ["v4"],
        v5: vreg = ["v5"],
        v6: vreg = ["v6"],
        v7: vreg = ["v7"],
        v8: vreg = ["v8"],
        v9: vreg = ["v9"],
        v10: vreg = ["v10"],
        v11: vreg = ["v11"],
        v12: vreg = ["v12"],
        v13: vreg = ["v13"],
        v14: vreg = ["v14"],
        v15: vreg = ["v15"],
        v16: vreg = ["v16"],
        v17: vreg = ["v17"],
        v18: vreg = ["v18"],
        v19: vreg = ["v19"],
        v20: vreg = ["v20"],
        v21: vreg = ["v21"],
        v22: vreg = ["v22"],
        v23: vreg = ["v23"],
        v24: vreg = ["v24"],
        v25: vreg = ["v25"],
        v26: vreg = ["v26"],
        v27: vreg = ["v27"],
        v28: vreg = ["v28"],
        v29: vreg = ["v29"],
        v30: vreg = ["v30"],
        v31: vreg = ["v31"],
        v1_0: vreg_pair = ["v1:0"],
        v3_2: vreg_pair = ["v3:2"],
        v5_4: vreg_pair = ["v5:4"],
        v7_6: vreg_pair = ["v7:6"],
        v9_8: vreg_pair = ["v9:8"],
        v11_10: vreg_pair = ["v11:10"],
        v13_12: vreg_pair = ["v13:12"],
        v15_14: vreg_pair = ["v15:14"],
        v17_16: vreg_pair = ["v17:16"],
        v19_18: vreg_pair = ["v19:18"],
        v21_20: vreg_pair = ["v21:20"],
        v23_22: vreg_pair = ["v23:22"],
        v25_24: vreg_pair = ["v25:24"],
        v27_26: vreg_pair = ["v27:26"],
        v29_28: vreg_pair = ["v29:28"],
        v31_30: vreg_pair = ["v31:30"],
        q0: qreg = ["q0"],
        q1: qreg = ["q1"],
        q2: qreg = ["q2"],
        q3: qreg = ["q3"],
        #error = ["r19"] =>
            "r19 is used internally by LLVM and cannot be used as an operand for inline asm",
        #error = ["r19:18"] =>
            "r19 is used internally by LLVM and cannot be used as an operand for inline asm",
        #error = ["r29", "sp"] =>
            "the stack pointer cannot be used as an operand for inline asm",
        #error = ["r29:28"] =>
            "the stack pointer cannot be used as an operand for inline asm",
        #error = ["r30", "fr"] =>
            "the frame register cannot be used as an operand for inline asm",
        #error = ["r31", "lr"] =>
            "the link register cannot be used as an operand for inline asm",
        #error = ["r31:30"] =>
            "the frame register and link register cannot be used as an operand for inline asm",
    }
}

impl HexagonInlineAsmReg {
    pub fn emit(
        self,
        out: &mut dyn fmt::Write,
        _arch: InlineAsmArch,
        _modifier: Option<char>,
    ) -> fmt::Result {
        out.write_str(self.name())
    }

    pub fn overlapping_regs(self, mut cb: impl FnMut(HexagonInlineAsmReg)) {
        cb(self);

        macro_rules! reg_pair_conflicts {
            (
                $(
                    $pair:ident : $hi:ident $lo:ident,
                )*
            ) => {
                match self {
                    $(
                        Self::$pair => {
                            cb(Self::$hi);
                            cb(Self::$lo);
                        }
                        Self::$hi => {
                            cb(Self::$pair);
                        }
                        Self::$lo => {
                            cb(Self::$pair);
                        }
                    )*
                    _ => {}
                }
            };
        }

        // GPR pair ↔ single GPR conflicts
        reg_pair_conflicts! {
            r1_0 : r1 r0,
            r3_2 : r3 r2,
            r5_4 : r5 r4,
            r7_6 : r7 r6,
            r9_8 : r9 r8,
            r11_10 : r11 r10,
            r13_12 : r13 r12,
            r15_14 : r15 r14,
            r17_16 : r17 r16,
            r21_20 : r21 r20,
            r23_22 : r23 r22,
            r25_24 : r25 r24,
            r27_26 : r27 r26,
        }

        // HVX vector pair ↔ single vector conflicts
        reg_pair_conflicts! {
            v1_0 : v1 v0,
            v3_2 : v3 v2,
            v5_4 : v5 v4,
            v7_6 : v7 v6,
            v9_8 : v9 v8,
            v11_10 : v11 v10,
            v13_12 : v13 v12,
            v15_14 : v15 v14,
            v17_16 : v17 v16,
            v19_18 : v19 v18,
            v21_20 : v21 v20,
            v23_22 : v23 v22,
            v25_24 : v25 v24,
            v27_26 : v27 v26,
            v29_28 : v29 v28,
            v31_30 : v31 v30,
        }
    }
}
