//! The midterm solution for source representation, until we've got a nice source frontend.
//!
//! Sacrifices type expressivity for the sake of typability in [RON] files.
//!
//! **If you want to construct a graph IR programmatically,
//! use [`crate::GraphIr`] directly instead,
//! as it gives you more control to specify where your instructions came from.**
//!
//! [RON]: https://docs.rs/ron/latest/ron/

use serde::{Deserialize, Serialize};

use crate::{id, instruction, Map, Set};

/// Semi-human-{read,writ}able [`crate::GraphIr`] with far less useful types.
///
/// **Do not use this if you want to programatically construct IR.**
/// Instead, directly use [`crate::GraphIr`].
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct GraphIr {
    /// See [`crate::GraphIr::instructions`], just that a simple number is used for the ID instead
    pub(crate) instructions: Map<u64, instruction::Kind>,
    /// See [`crate::GraphIr::edges`], the forward edges.
    /// RON wants you to type the set as if it were a list.
    pub(crate) edges: Map<Socket, Set<Socket>>,
}

#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
pub struct Socket {
    /// ID of the instruction this socket is on.
    pub(crate) on: u64,
    pub(crate) idx: u16,
}

impl From<Socket> for id::Socket {
    fn from(source: Socket) -> Self {
        Self {
            belongs_to: id::Instruction(source.on),
            idx: id::SocketIdx(source.idx),
        }
    }
}

impl From<GraphIr> for crate::GraphIr {
    fn from(source: GraphIr) -> Self {
        let edges = source.edges.clone();
        Self {
            instructions: source
                .instructions
                .into_iter()
                .map(|(id, kind)| (id::Instruction(id), kind))
                .collect(),
            edges: type_edges(source.edges),
            rev_edges: reverse_and_type_edges(edges),
        }
    }
}

fn type_edges(edges: Map<Socket, Set<Socket>>) -> Map<id::Output, Set<id::Input>> {
    edges
        .into_iter()
        .map(|(output, inputs)| {
            let output = id::Output(output.into());
            let inputs = inputs.into_iter().map(Into::into).map(id::Input).collect();
            (output, inputs)
        })
        .collect()
}

fn reverse_and_type_edges(edges: Map<Socket, Set<Socket>>) -> Map<id::Input, id::Output> {
    edges
        .into_iter()
        .fold(Map::new(), |mut rev_edges, (output, inputs)| {
            let output = id::Output(output.into());

            for input in inputs {
                let input = id::Input(input.into());
                let previous = rev_edges.insert(input, output.clone());
                if let Some(previous) = previous {
                    // TODO: handle this with a TryFrom impl
                    panic!("two or more outputs referred to the same input {previous:#?}");
                }
            }

            rev_edges
        })
}
