//! Code generation APIs, used to actually define functions and generate code.
use crate::{Symbol, Type};

/// Implementations of codegen features
pub mod impls;

/// A variable ID.
/// Variables are the only thing that can store information
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Variable(pub usize);

/// A variable or symbol with projection (aka field access, dereferences, etc.)
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Place {
    /// Just variable access
    Variable(Variable),
    /// Global symbol access
    Global(Symbol),
    /// Pointer dereference
    Deref(Box<Place>),
    /// Field access, using 0-based field index
    Field(Box<Place>, usize),
}

/// An operand
#[derive(Clone, Debug, PartialEq, PartialOrd)]
pub enum Operand {
    /// A place, see [Place]
    Place(Place),
    /// A signed integer constant
    IConst(i128, crate::types::IntegerSize),
    /// An unsigned integer constant
    UConst(u128, crate::types::IntegerSize),
    /// A floating point constant (value, size) where size is specified in bits
    FConst(f64, u16),
}

/// Trait for generating code within a function
pub trait BodyCodegen {
    /// Declare a variable, see [Variable]
    fn declare_var(&mut self, ty: Type) -> Variable;
    /// Get the variable representing an argument
    fn arg_var(&self, idx: usize) -> Variable;

    /// Assign a value to a variable
    fn assign(&mut self, value: Operand, destination: Place);

    /// Call a function and put return value into `destination`
    fn call(&mut self, function: Operand, args: Vec<Operand>, destination: Place);

    /// Return a value from this function.
    /// Use [`Operand::Unit`] if no return value is required.
    fn return_(&mut self, value: Operand);

    /// Get arbitrary control flow instructions, see [ACFCodegen]
    fn acf(&mut self) -> &mut impl ACFCodegen {
        Box::leak(Box::new(impls::Unsupported))
    }
}

/// A label ID. See [`ACFCodegen::label`]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Label(pub usize);

/// Arbitrary control flow instructions, such as jumps and
/// Warning: Not all codegens implement arbitrary control flow
pub trait ACFCodegen {
    /// Puts a said label in the current position.
    /// Note: Labels can be used before placing. Frontend decides on IDs
    fn label(&mut self, label: Label);

    /// Jump to a label.
    /// See [`ACFCodegen::label`]
    fn jump(&mut self, label: Label);

    /// Jumps if value equals (or not if equal is false).
    /// See [`ACFCodegen::label`]
    fn cjump(&mut self, lhs: Operand, rhs: u128, equal: bool, label: Label);
}

/// Interface for generating actual code.
/// All the items defined must be declared using [DeclarationBackend] first.
pub trait CodegenBackend: Sync {
    /// Define a function
    fn function(&self, name: Symbol) -> impl BodyCodegen;
}
