use petgraph::visit::EdgeRef;
use petgraph::Outgoing;
use crate::ImplementationResult;
use std::{rc::Rc, hash::{Hash, Hasher}, format};
use petgraph::{Graph, graph::{DefaultIx, NodeIndex}, Directed};
use crate::{state::GLLState, value::Value, AttributeMap, AttributeKey};
use super::{GrammarSlot, sppf::SPPFNodeIndex};
pub(crate) type GSSNodeIndex = NodeIndex<GSSIx>;
type GSSIx = DefaultIx;
pub type GSSGraph<'a> = Graph<Rc<GSSNode<'a>>, SPPFNodeIndex, Directed, GSSIx>;
#[derive(Debug, Clone)]
pub struct GSS<'a>(GSSGraph<'a>);
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct GSSNode<'a> {
pub(crate) slot: Rc<GrammarSlot<'a>>,
inp_pointer: usize,
attributes: AttributeMap<'a>
}
impl<'a> GSSNode<'a> {
#[must_use]
pub fn new(slot: Rc<GrammarSlot<'a>>, inp_pointer: usize, attributes: AttributeMap<'a>) -> Self {
Self {slot, inp_pointer, attributes}
}
#[must_use]
pub fn to_string(&self, state: &GLLState<'a>, math_mode: bool) -> String {
format!("[{},{}]", self.slot.to_string(state, math_mode), self.inp_pointer)
}
#[must_use]
pub fn get_attribute(&self, i: AttributeKey) -> Option<&Value<'a>> {
self.attributes.get(i)
}
#[must_use]
pub const fn get_slot(&self) -> &Rc<GrammarSlot<'a>> {
&self.slot
}
pub(crate) fn cmp_attributes(orig: &Self, other: &Self) -> bool {
orig.attributes == other.attributes
}
pub(crate) fn hash_attributes<H: Hasher>(cand: &Self, state: &mut H) {
cand.attributes.hash(state);
}
}
impl<'a> std::ops::Deref for GSS<'a> { type Target = GSSGraph<'a>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<'a> std::ops::DerefMut for GSS<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<'a> Default for GSS<'a> {
fn default() -> Self {
Self(GSSGraph::new())
}
}
impl<'a> GSS<'a> {
pub fn to_dot(&self, state: &GLLState<'a>, math_mode: bool) -> ImplementationResult<'a, String> {
let mut res = String::new();
res.push_str("digraph {\n");
for ix in self.0.node_indices() {
#[allow(clippy::expect_used)]
let node = self.0.node_weight(ix).expect("Getting node from graph by index returned by graph itself. Should be impossible to fail");
res.push_str(&format!("{} [label=\"{}\"]\n", ix.index(), node.to_string(state, math_mode)));
for edge in self.0.edges_directed(ix, Outgoing) {
let child = edge.target();
res.push_str(&format!("{} -> {}", ix.index(), child.index()));
let sppf_node = edge.weight();
let label = if let Ok(n) = state.get_sppf_node(*sppf_node) {
n.to_string(state, math_mode)?
} else {
"PRUNED".to_string()
};
res.push_str(&format!(" [label=\"{label}\"]"));
res.push('\n');
}
}
res.push('}');
Ok(res)
}
}