use std::{collections::{HashMap, HashSet}, rc::Rc};
use map_macro::hash_map;
use petricheck::model::{label::PetriTransitionLabel, net::PetriNet, transition::PetriTransition};
use crate::{model::{activity::ActivityType, diagram::{Diagram, ProcessContentRef}, event::EventType, id::BpmnId}, petri::error::BpmnToPetriTranslationError};
pub struct Bpmn2PetriSubProcessRetVal {
pub initial_places : HashSet<usize>,
pub final_places : HashSet<usize>,
}
impl Bpmn2PetriSubProcessRetVal {
fn new(initial_places: HashSet<usize>, final_places: HashSet<usize>) -> Self {
Self { initial_places, final_places }
}
}
pub fn sub_process_to_petri(
petri_net : &mut PetriNet,
bpmn_id_to_incoming_place : &mut HashMap<BpmnId,usize>,
bpmn_id_to_outgoing_place : &mut HashMap<BpmnId,usize>,
bpmn_id_to_transitions_labels : &mut HashMap<BpmnId,Rc<PetriTransitionLabel>>,
bpmn : &Diagram,
sub_process : &ProcessContentRef
) -> Result<Bpmn2PetriSubProcessRetVal,BpmnToPetriTranslationError> {
let mut initial_places = HashSet::new();
let mut final_places = HashSet::new();
for evt_id in &sub_process.direct_child_events {
let evt = bpmn.events.get(evt_id).unwrap();
let incoming = petri_net.add_place(None);
if evt.event_type == EventType::Start {
initial_places.insert(incoming);
}
let outgoing = petri_net.add_place(None);
if evt.event_type == EventType::End {
final_places.insert(outgoing);
}
let transition_label = Rc::new(PetriTransitionLabel::new(evt_id.id.clone()));
bpmn_id_to_transitions_labels.insert(evt_id.clone(),transition_label.clone());
let _ = petri_net.add_transition(
PetriTransition::new(
Some(transition_label),
hash_map!{incoming=>1},
hash_map!{outgoing=>1}
)
);
bpmn_id_to_incoming_place.insert(evt_id.clone(), incoming);
bpmn_id_to_outgoing_place.insert(evt_id.clone(), outgoing);
}
for act_id in &sub_process.direct_child_activities {
let act = bpmn.activities.get(act_id).unwrap();
if let ActivityType::SubProcess(sub_proc) = &act.activity_type {
let sub_proc_ret_val = sub_process_to_petri(
petri_net,
bpmn_id_to_incoming_place,
bpmn_id_to_outgoing_place,
bpmn_id_to_transitions_labels,
bpmn,
sub_proc
)?;
match sub_proc_ret_val.initial_places.len() {
0 => {
return Err(BpmnToPetriTranslationError::SubProcessMustHaveOneStartEvent);
},
1 => {
let initial_place_id = sub_proc_ret_val.initial_places.iter().next().unwrap();
bpmn_id_to_incoming_place.insert(act_id.clone(),*initial_place_id);
},
_ => {
let new_place = petri_net.add_place(None);
bpmn_id_to_incoming_place.insert(act_id.clone(),new_place);
let mut to_places = HashMap::new();
for p_id in sub_proc_ret_val.initial_places {
to_places.insert(p_id,1);
}
let tx = PetriTransition::new(None,hash_map!{new_place=>1}, to_places);
petri_net.add_transition(tx);
}
}
match sub_proc_ret_val.final_places.len() {
0 => {
return Err(BpmnToPetriTranslationError::SubProcessMustHaveOneEndEvent);
},
1 => {
let final_place_id = sub_proc_ret_val.final_places.iter().next().unwrap();
bpmn_id_to_outgoing_place.insert(act_id.clone(),*final_place_id);
},
_ => {
let new_place = petri_net.add_place(None);
bpmn_id_to_outgoing_place.insert(act_id.clone(),new_place);
let mut from_places = HashMap::new();
for p_id in sub_proc_ret_val.final_places {
from_places.insert(p_id,1);
}
let tx = PetriTransition::new(None,from_places, hash_map!{new_place=>1});
petri_net.add_transition(tx);
}
}
} else {
let incoming = petri_net.add_place(None);
let outgoing = petri_net.add_place(None);
let transition_label = Rc::new(PetriTransitionLabel::new(act_id.id.clone()));
bpmn_id_to_transitions_labels.insert(act_id.clone(),transition_label.clone());
let _: usize = petri_net.add_transition(
PetriTransition::new(
Some(transition_label),
hash_map!{incoming=>1},
hash_map!{outgoing=>1}
)
);
bpmn_id_to_incoming_place.insert(act_id.clone(), incoming);
bpmn_id_to_outgoing_place.insert(act_id.clone(), outgoing);
}
}
let ret_val = Bpmn2PetriSubProcessRetVal::new(
initial_places,
final_places
);
Ok(ret_val)
}