use anyhow::Result;
use std::fmt;
use std::io::{Read, Write};

use crate::config::Config;
use crate::machine::MachineCode;

pub type CompiledFunc<T> = fn(*const T, *const &mut [T], usize, *const T) -> i32;

pub trait Compiled<T: Sized + Copy + Default> {
    fn exec(&mut self, params: &[T]);
    fn evaluate(&mut self, args: &[T], outs: &mut [T]);
    fn evaluate_single(&mut self, args: &[T]) -> T;
    fn mem(&self) -> &[T];
    fn mem_mut(&mut self) -> &mut [T];
    fn dump(&self, name: &str);
    fn dumps(&self) -> Vec<u8>;
    fn func(&self) -> CompiledFunc<T>;
    fn support_indirect(&self) -> bool;
    fn count_lanes(&self) -> usize;
    fn as_machine(&self) -> Option<&MachineCode<T>>;
}

pub trait Storage: Sized {
    fn save(&self, stream: &mut impl Write) -> Result<()>;
    fn load(stream: &mut impl Read, config: &Config) -> Result<Self>;
}

pub fn bool_to_f64(b: bool) -> f64 {
    const T: f64 = f64::from_bits(!0);
    const F: f64 = f64::from_bits(0);
    if b {
        T
    } else {
        F
    }
}

/// aligns at a multiple of 32 (to cover different ABIs)
pub fn align_stack(n: u32) -> u32 {
    n + 16 - (n & 15)
}

/*****************************************/

#[derive(PartialEq)]
pub enum DataType {
    F32,
    F64,
}

#[derive(Copy, Clone, PartialEq, Hash, Eq)]
pub enum Reg {
    Ret,
    Temp,
    Gen(u8),
    Left,
    Right,
    Static(u32),
}

impl fmt::Debug for Reg {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Reg::Ret => write!(f, "%$"),
            Reg::Left => write!(f, "%l"),
            Reg::Right => write!(f, "%r"),
            Reg::Temp => write!(f, "%t"),
            Reg::Gen(r) => write!(f, "%{}", r),
            Reg::Static(r) => write!(f, "σ{}", r),
        }
    }
}

pub fn reg(r: u8) -> Reg {
    Reg::Gen(r)
}

pub fn is_external_func(op: &str) -> bool {
    op.starts_with("$")
}
