use serde::{Deserialize, Serialize};

pub mod read;
pub mod write;

#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub enum Kind {
    // TODO: `read::Read` and `write::Write` hold real values atm -- they should actually
    //       point to `Const` instructions instead (which are... yet to be done...)
    Read(read::Read),
    Write(write::Write),
    Math(Math),
    Blend(Blend),
    Noise(Noise),
    Filter(Filter),
}

#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub enum Math {
    Add,
    Subtract,
    Multiply,
    Divide,
}

#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub enum Blend {
    Normal,
    Multiply,
    Additive,
    Overlay,
    Screen,
    Subtractive,
    Difference,
    Darken,
    Lighten,
}

#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub enum Noise {
    Perlin,
    Simplex,
    Voronoi,
}

#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub enum Filter {
    Invert,
}

// TODO: given that this basically matches on all instructions, we may need to use
//       the visitor pattern in future here, or at least get them behind traits
//       which should allow far more nuanced description
impl Kind {
    /// Returns how many sockets this kind of instruction has.
    #[must_use]
    pub fn socket_count(&self) -> SocketCount {
        match self {
            Self::Read(_) => (0, 1),
            Self::Write(_) => (1, 0),
            Self::Math(_) | Self::Blend(_) => (2, 1),
            Self::Noise(_) => {
                todo!("how many arguments does noise take? how many outputs does it have?")
            }
            Self::Filter(Filter::Invert) => (1, 1),
        }
        .into()
    }
}

/// How many sockets are on an instruction?
pub struct SocketCount {
    pub inputs: u16,
    pub outputs: u16,
}

impl From<(u16, u16)> for SocketCount {
    fn from((inputs, outputs): (u16, u16)) -> Self {
        Self { inputs, outputs }
    }
}
