use std::fmt;

pub trait Spv {
    fn encode(&self, out: &mut Vec<u32>);
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[repr(transparent)]
pub struct Id(pub u32);

impl fmt::Display for Id {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "%{}", self.0) }
}

impl From<u32> for Id  { fn from(v: u32) -> Self { Id(v) } }
impl From<Id>  for u32 { fn from(id: Id) -> Self { id.0 } }

impl Spv for Id { fn encode(&self, out: &mut Vec<u32>) { out.push(self.0); } }
impl Spv for u32 { fn encode(&self, out: &mut Vec<u32>) { out.push(*self); } }
impl Spv for ::std::string::String {
    fn encode(&self, out: &mut Vec<u32>) {
        let b = self.as_bytes();
        for i in 0..=(b.len() / 4) {
            let g = |j| b.get(i * 4 + j).copied().unwrap_or(0);
            out.push(u32::from_le_bytes([g(0), g(1), g(2), g(3)]));
        }
    }
}
impl<T: Spv> Spv for Option<T> {
    fn encode(&self, out: &mut Vec<u32>) { if let Some(v) = self { v.encode(out); } }
}
impl<T: Spv> Spv for Vec<T> {
    fn encode(&self, out: &mut Vec<u32>) { for v in self { v.encode(out); } }
}
impl Spv for (u32, Id) { fn encode(&self, out: &mut Vec<u32>) { out.push(self.0); self.1.encode(out); } }
impl Spv for (Id, u32) { fn encode(&self, out: &mut Vec<u32>) { self.0.encode(out); out.push(self.1); } }
impl Spv for (Id, Id)  { fn encode(&self, out: &mut Vec<u32>) { self.0.encode(out); self.1.encode(out); } }

pub trait Instruction: Spv {
    fn opcode(&self) -> Opcode;
    fn result_id(&self) -> Option<Id>;
    fn result_type(&self) -> Option<Id>;
}

include!(concat!(env!("OUT_DIR"), "/generated.rs"));
