//! Instance identification for instructions and their glue.
//!
//! Instructions as defined in [`crate::instruction::Kind`] and descendants are very useful,
//! but they cannot be directly used as vertices in the graph IR,
//! as there may easily be multiple instructions of the same kind in the same program.
//!
//! Instead, this module offers an alternative way to refer to specific instances:
//!
//! - [`Instruction`]s are effectively just a number floating in space,
//!   incremented each time a new instruction is referred to.
//! - [`Socket`]s contain
//!   - what [`Instruction`] they belong to
//!   - which index they occupy on it
//!
//!   The distinction between [`Input`] and [`Output`] is implemented
//!   as them being different types.

use std::fmt;

use serde::{Deserialize, Serialize};

/// One specific instruction.
///
/// It does **not** contain what kind of instruction this is.
/// Refer to [`crate::instruction::Kind`] for this instead.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct Instruction(pub(super) u64);

impl fmt::Debug for Instruction {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "InstrId {}", self.0)
    }
}

/// On an **instruction**, accepts incoming data.
///
/// An **instruction** cannot run if any of these are not connected.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct Input(pub(super) Socket);

impl Input {
    #[must_use]
    pub fn socket(&self) -> &Socket {
        &self.0
    }
}

/// On an **instruction**, returns outgoing data to be fed to [`Input`]s.
///
/// In contrast to [`Input`]s, [`Output`]s may be used or unused.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct Output(pub Socket); // TODO: Restrict publicness to super

impl Output {
    #[must_use]
    pub fn socket(&self) -> &Socket {
        &self.0
    }
}

/// An unspecified socket on a specific **instruction**,
/// and where it is on that **instruction**.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct Socket {
    pub belongs_to: Instruction,
    pub idx: SocketIdx,
}

/// Where a [`Socket`] is on one **instruction**.
///
/// Note that this does **not** identify a [`Socket`] globally.
/// There may be multiple [`Socket`]s sharing the same [`SocketIdx`],
/// but on different [`Instruction`]s.
///
/// This really only serves for denoting where a socket is,
/// when it's already clear which instruction is referred to.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct SocketIdx(pub u16); // TODO: Restrict publicness to super

impl fmt::Debug for SocketIdx {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.0.fmt(f)
    }
}
