use std::fmt::{self, Debug};
use std::num::NonZero;
use std::ops::RangeInclusive;

use serde::Serialize;

use crate::compiler_interface::with;
use crate::mir::FieldIdx;
use crate::target::{MachineInfo, MachineSize as Size};
use crate::ty::{Align, Ty, VariantIdx, index_impl};
use crate::{Error, Opaque, ThreadLocalIndex, error};

/// A function ABI definition.
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
pub struct FnAbi {
    /// The types of each argument.
    pub args: Vec<ArgAbi>,

    /// The expected return type.
    pub ret: ArgAbi,

    /// The count of non-variadic arguments.
    ///
    /// Should only be different from `args.len()` when a function is a C variadic function.
    pub fixed_count: u32,

    /// The ABI convention.
    pub conv: CallConvention,

    /// Whether this is a variadic C function,
    pub c_variadic: bool,
}

/// Information about the ABI of a function's argument, or return value.
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
pub struct ArgAbi {
    pub ty: Ty,
    pub layout: Layout,
    pub mode: PassMode,
}

/// How a function argument should be passed in to the target function.
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
pub enum PassMode {
    /// Ignore the argument.
    ///
    /// The argument is either uninhabited or a ZST.
    Ignore,
    /// Pass the argument directly.
    ///
    /// The argument has a layout abi of `Scalar` or `Vector`.
    Direct(Opaque),
    /// Pass a pair's elements directly in two arguments.
    ///
    /// The argument has a layout abi of `ScalarPair`.
    Pair(Opaque, Opaque),
    /// Pass the argument after casting it.
    Cast { pad_i32: bool, cast: Opaque },
    /// Pass the argument indirectly via a hidden pointer.
    Indirect { attrs: Opaque, meta_attrs: Opaque, on_stack: bool },
}

/// The layout of a type, alongside the type itself.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize)]
pub struct TyAndLayout {
    pub ty: Ty,
    pub layout: Layout,
}

/// The layout of a type in memory.
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
pub struct LayoutShape {
    /// The fields location within the layout
    pub fields: FieldsShape,

    /// Encodes information about multi-variant layouts.
    /// Even with `Multiple` variants, a layout still has its own fields! Those are then
    /// shared between all variants.
    ///
    /// To access all fields of this layout, both `fields` and the fields of the active variant
    /// must be taken into account.
    pub variants: VariantsShape,

    /// The `abi` defines how this data is passed between functions.
    pub abi: ValueAbi,

    /// The ABI mandated alignment in bytes.
    pub abi_align: Align,

    /// The size of this layout in bytes.
    pub size: Size,
}

impl LayoutShape {
    /// Returns `true` if the layout corresponds to an unsized type.
    #[inline]
    pub fn is_unsized(&self) -> bool {
        self.abi.is_unsized()
    }

    #[inline]
    pub fn is_sized(&self) -> bool {
        !self.abi.is_unsized()
    }

    /// Returns `true` if the type is sized and a 1-ZST (meaning it has size 0 and alignment 1).
    pub fn is_1zst(&self) -> bool {
        self.is_sized() && self.size.bits() == 0 && self.abi_align == 1
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct Layout(usize, ThreadLocalIndex);
index_impl!(Layout);

impl Layout {
    pub fn shape(self) -> LayoutShape {
        with(|cx| cx.layout_shape(self))
    }
}

/// Describes how the fields of a type are shaped in memory.
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
pub enum FieldsShape {
    /// Scalar primitives and `!`, which never have fields.
    Primitive,

    /// All fields start at no offset. The `usize` is the field count.
    Union(NonZero<usize>),

    /// Array/vector-like placement, with all fields of identical types.
    Array { stride: Size, count: u64 },

    /// Struct-like placement, with precomputed offsets.
    ///
    /// Fields are guaranteed to not overlap, but note that gaps
    /// before, between and after all the fields are NOT always
    /// padding, and as such their contents may not be discarded.
    /// For example, enum variants leave a gap at the start,
    /// where the discriminant field in the enum layout goes.
    Arbitrary {
        /// Offsets for the first byte of each field,
        /// ordered to match the source definition order.
        /// I.e.: It follows the same order as [super::ty::VariantDef::fields()].
        /// This vector does not go in increasing order.
        offsets: Vec<Size>,
    },
}

impl FieldsShape {
    pub fn fields_by_offset_order(&self) -> Vec<FieldIdx> {
        match self {
            FieldsShape::Primitive => vec![],
            FieldsShape::Union(_) | FieldsShape::Array { .. } => (0..self.count()).collect(),
            FieldsShape::Arbitrary { offsets, .. } => {
                let mut indices = (0..offsets.len()).collect::<Vec<_>>();
                indices.sort_by_key(|idx| offsets[*idx]);
                indices
            }
        }
    }

    pub fn count(&self) -> usize {
        match self {
            FieldsShape::Primitive => 0,
            FieldsShape::Union(count) => count.get(),
            FieldsShape::Array { count, .. } => *count as usize,
            FieldsShape::Arbitrary { offsets, .. } => offsets.len(),
        }
    }
}

#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
pub enum VariantsShape {
    /// A type with no valid variants. Must be uninhabited.
    Empty,

    /// Single enum variants, structs/tuples, unions, and all non-ADTs.
    Single { index: VariantIdx },

    /// Enum-likes with more than one inhabited variant: each variant comes with
    /// a *discriminant* (usually the same as the variant index but the user can
    /// assign explicit discriminant values). That discriminant is encoded
    /// as a *tag* on the machine. The layout of each variant is
    /// a struct, and they all have space reserved for the tag.
    /// For enums, the tag is the sole field of the layout.
    Multiple {
        tag: Scalar,
        tag_encoding: TagEncoding,
        tag_field: usize,
        variants: Vec<VariantFields>,
    },
}

#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
pub struct VariantFields {
    /// Offsets for the first byte of each field,
    /// ordered to match the source definition order.
    /// I.e.: It follows the same order as [super::ty::VariantDef::fields()].
    /// This vector does not go in increasing order.
    pub offsets: Vec<Size>,
}

impl VariantFields {
    pub fn fields_by_offset_order(&self) -> Vec<FieldIdx> {
        let mut indices = (0..self.offsets.len()).collect::<Vec<_>>();
        indices.sort_by_key(|idx| self.offsets[*idx]);
        indices
    }
}

#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
pub enum TagEncoding {
    /// The tag directly stores the discriminant, but possibly with a smaller layout
    /// (so converting the tag to the discriminant can require sign extension).
    Direct,

    /// Niche (values invalid for a type) encoding the discriminant:
    /// Discriminant and variant index coincide.
    /// The variant `untagged_variant` contains a niche at an arbitrary
    /// offset (field `tag_field` of the enum), which for a variant with
    /// discriminant `d` is set to
    /// `(d - niche_variants.start).wrapping_add(niche_start)`.
    ///
    /// For example, `Option<(usize, &T)>`  is represented such that
    /// `None` has a null pointer for the second tuple field, and
    /// `Some` is the identity function (with a non-null reference).
    Niche {
        untagged_variant: VariantIdx,
        niche_variants: RangeInclusive<VariantIdx>,
        niche_start: u128,
    },
}

/// How many scalable vectors are in a `ValueAbi::ScalableVector`?
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
pub struct NumScalableVectors(pub(crate) u8);

/// Describes how values of the type are passed by target ABIs,
/// in terms of categories of C types there are ABI rules for.
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
pub enum ValueAbi {
    Scalar(Scalar),
    ScalarPair(Scalar, Scalar),
    Vector {
        element: Scalar,
        count: u64,
    },
    ScalableVector {
        element: Scalar,
        count: u64,
        number_of_vectors: NumScalableVectors,
    },
    Aggregate {
        /// If true, the size is exact, otherwise it's only a lower bound.
        sized: bool,
    },
}

impl ValueAbi {
    /// Returns `true` if the layout corresponds to an unsized type.
    pub fn is_unsized(&self) -> bool {
        match *self {
            ValueAbi::Scalar(_)
            | ValueAbi::ScalarPair(..)
            | ValueAbi::Vector { .. }
            // FIXME(rustc_scalable_vector): Scalable vectors are `Sized` while the
            // `sized_hierarchy` feature is not yet fully implemented. After `sized_hierarchy` is
            // fully implemented, scalable vectors will remain `Sized`, they just won't be
            // `const Sized` - whether `is_unsized` continues to return `false` at that point will
            // need to be revisited and will depend on what `is_unsized` is used for.
            | ValueAbi::ScalableVector { .. } => false,
            ValueAbi::Aggregate { sized } => !sized,
        }
    }
}

/// Information about one scalar component of a Rust type.
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Serialize)]
pub enum Scalar {
    Initialized {
        /// The primitive type used to represent this value.
        value: Primitive,
        /// The range that represents valid values.
        /// The range must be valid for the `primitive` size.
        valid_range: WrappingRange,
    },
    Union {
        /// Unions never have niches, so there is no `valid_range`.
        /// Even for unions, we need to use the correct registers for the kind of
        /// values inside the union, so we keep the `Primitive` type around.
        /// It is also used to compute the size of the scalar.
        value: Primitive,
    },
}

impl Scalar {
    pub fn has_niche(&self, target: &MachineInfo) -> bool {
        match self {
            Scalar::Initialized { value, valid_range } => {
                !valid_range.is_full(value.size(target)).unwrap()
            }
            Scalar::Union { .. } => false,
        }
    }
}

/// Fundamental unit of memory access and layout.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Serialize)]
pub enum Primitive {
    /// The `bool` is the signedness of the `Integer` type.
    ///
    /// One would think we would not care about such details this low down,
    /// but some ABIs are described in terms of C types and ISAs where the
    /// integer arithmetic is done on {sign,zero}-extended registers, e.g.
    /// a negative integer passed by zero-extension will appear positive in
    /// the callee, and most operations on it will produce the wrong values.
    Int {
        length: IntegerLength,
        signed: bool,
    },
    Float {
        length: FloatLength,
    },
    Pointer(AddressSpace),
}

impl Primitive {
    pub fn size(self, target: &MachineInfo) -> Size {
        match self {
            Primitive::Int { length, .. } => Size::from_bits(length.bits()),
            Primitive::Float { length } => Size::from_bits(length.bits()),
            Primitive::Pointer(_) => target.pointer_width,
        }
    }
}

/// Enum representing the existing integer lengths.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize)]
pub enum IntegerLength {
    I8,
    I16,
    I32,
    I64,
    I128,
}

/// Enum representing the existing float lengths.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize)]
pub enum FloatLength {
    F16,
    F32,
    F64,
    F128,
}

impl IntegerLength {
    pub fn bits(self) -> usize {
        match self {
            IntegerLength::I8 => 8,
            IntegerLength::I16 => 16,
            IntegerLength::I32 => 32,
            IntegerLength::I64 => 64,
            IntegerLength::I128 => 128,
        }
    }
}

impl FloatLength {
    pub fn bits(self) -> usize {
        match self {
            FloatLength::F16 => 16,
            FloatLength::F32 => 32,
            FloatLength::F64 => 64,
            FloatLength::F128 => 128,
        }
    }
}

/// An identifier that specifies the address space that some operation
/// should operate on. Special address spaces have an effect on code generation,
/// depending on the target and the address spaces it implements.
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
pub struct AddressSpace(pub u32);

impl AddressSpace {
    /// The default address space, corresponding to data space.
    pub const DATA: Self = AddressSpace(0);
}

/// Inclusive wrap-around range of valid values (bitwise representation), that is, if
/// start > end, it represents `start..=MAX`, followed by `0..=end`.
///
/// That is, for an i8 primitive, a range of `254..=2` means following
/// sequence:
///
///    254 (-2), 255 (-1), 0, 1, 2
#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize)]
pub struct WrappingRange {
    pub start: u128,
    pub end: u128,
}

impl WrappingRange {
    /// Returns `true` if `size` completely fills the range.
    #[inline]
    pub fn is_full(&self, size: Size) -> Result<bool, Error> {
        let Some(max_value) = size.unsigned_int_max() else {
            return Err(error!("Expected size <= 128 bits, but found {} instead", size.bits()));
        };
        if self.start <= max_value && self.end <= max_value {
            Ok(self.start == (self.end.wrapping_add(1) & max_value))
        } else {
            Err(error!("Range `{self:?}` out of bounds for size `{}` bits.", size.bits()))
        }
    }

    /// Returns `true` if `v` is contained in the range.
    #[inline(always)]
    pub fn contains(&self, v: u128) -> bool {
        if self.wraps_around() {
            self.start <= v || v <= self.end
        } else {
            self.start <= v && v <= self.end
        }
    }

    /// Returns `true` if the range wraps around.
    /// I.e., the range represents the union of `self.start..=MAX` and `0..=self.end`.
    /// Returns `false` if this is a non-wrapping range, i.e.: `self.start..=self.end`.
    #[inline]
    pub fn wraps_around(&self) -> bool {
        self.start > self.end
    }
}

impl Debug for WrappingRange {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        if self.start > self.end {
            write!(fmt, "(..={}) | ({}..)", self.end, self.start)?;
        } else {
            write!(fmt, "{}..={}", self.start, self.end)?;
        }
        Ok(())
    }
}

/// General language calling conventions.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize)]
pub enum CallConvention {
    C,
    Rust,

    Cold,
    PreserveMost,
    PreserveAll,
    PreserveNone,

    Custom,

    // Target-specific calling conventions.
    ArmAapcs,
    CCmseNonSecureCall,
    CCmseNonSecureEntry,

    Msp430Intr,

    PtxKernel,

    GpuKernel,

    X86Fastcall,
    X86Intr,
    X86Stdcall,
    X86ThisCall,
    X86VectorCall,

    X86_64SysV,
    X86_64Win64,

    AvrInterrupt,
    AvrNonBlockingInterrupt,

    RiscvInterrupt,
}

#[non_exhaustive]
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize)]
pub struct ReprFlags {
    pub is_simd: bool,
    pub is_c: bool,
    pub is_transparent: bool,
    pub is_linear: bool,
}

#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize)]
pub enum IntegerType {
    /// Pointer-sized integer type, i.e. `isize` and `usize`.
    Pointer {
        /// Signedness. e.g. `true` for `isize`
        is_signed: bool,
    },
    /// Fixed-sized integer type, e.g. `i8`, `u32`, `i128`.
    Fixed {
        /// Length of this integer type. e.g. `IntegerLength::I8` for `u8`.
        length: IntegerLength,
        /// Signedness. e.g. `false` for `u8`
        is_signed: bool,
    },
}

/// Representation options provided by the user
#[non_exhaustive]
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize)]
pub struct ReprOptions {
    pub int: Option<IntegerType>,
    pub align: Option<Align>,
    pub pack: Option<Align>,
    pub flags: ReprFlags,
}
