use std::{error::Error, fmt, mem::take};
use crossterm::event::{KeyEvent, MouseEvent};
use ratatui::{layout::Rect, Frame};
use crate::{
before_after::BeforeAfterMode,
compact_tree::CompactTree,
data_flow::{DataFlowConversionError, DataFlowDag, DataFlowMode},
deduction_tree::{CompleteProofDag, ProofDagConversionError, SemanticTableau},
effect_flow::{EffectFlowConversionError, EffectFlowDag, EffectFlowMode},
interactive::Mode,
protocol::{GenericDag, LineCharacterPosition, LocalBinding, RichText, TimingDisplay},
selection::Selection,
widgets::legend::LegendSection,
};
#[derive(Clone, Default)]
pub enum DagState {
#[default]
None,
Proof(CompleteProofDag),
Dataflow(DataFlowDag),
Effectflow(EffectFlowDag),
}
impl DagState {
pub const fn is_some(&self) -> bool {
!matches!(self, Self::None)
}
pub const fn style_name(&self) -> Option<&'static str> {
match self {
Self::None => None,
Self::Proof(_) => Some("proof"),
Self::Dataflow(_) => Some("dataflow"),
Self::Effectflow(_) => Some("effectflow"),
}
}
pub fn block_input(&self) -> &[LocalBinding] {
match self {
Self::Proof(dag) => &dag.block_input,
Self::Dataflow(dag) => &dag.block_input,
Self::Effectflow(dag) => &dag.block_input,
Self::None => &[],
}
}
pub fn block_output(&self) -> Option<&RichText> {
match self {
Self::Proof(dag) => dag.block_output.as_ref(),
Self::Dataflow(dag) => dag.block_output.as_ref(),
Self::Effectflow(dag) => dag.block_output.as_ref(),
Self::None => None,
}
}
pub fn from_generic(dag: GenericDag) -> Result<Self, DagConversionError> {
use crate::protocol::DisplayStyle;
match dag.display_style {
DisplayStyle::Proof => CompleteProofDag::try_from(dag)
.map(Self::Proof)
.map_err(DagConversionError::Proof),
DisplayStyle::Dataflow => DataFlowDag::try_from(dag)
.map(Self::Dataflow)
.map_err(DagConversionError::Dataflow),
DisplayStyle::Effectflow => EffectFlowDag::try_from(dag)
.map(Self::Effectflow)
.map_err(DagConversionError::Effectflow),
}
}
pub fn update_current_from_cursor(&mut self, cursor: LineCharacterPosition) -> bool {
match self {
Self::Proof(dag) => dag.update_current_from_cursor(cursor),
Self::Dataflow(dag) => dag.update_current_from_cursor(cursor),
Self::Effectflow(dag) => dag.update_current_from_cursor(cursor),
Self::None => false,
}
}
pub fn navigation_location(
&self,
selection: Option<Selection>,
current_uri: &str,
) -> Option<(String, LineCharacterPosition)> {
let sel = selection?;
match self {
Self::Proof(dag) => dag.navigation_location(sel, current_uri),
Self::Dataflow(dag) => dag.navigation_location(sel, current_uri),
Self::Effectflow(_) | Self::None => None,
}
}
}
#[derive(Debug)]
pub enum DagConversionError {
Proof(ProofDagConversionError),
Dataflow(DataFlowConversionError),
Effectflow(EffectFlowConversionError),
}
impl fmt::Display for DagConversionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Proof(e) => write!(f, "{e}"),
Self::Dataflow(e) => write!(f, "{e}"),
Self::Effectflow(e) => write!(f, "{e}"),
}
}
}
impl Error for DagConversionError {}
enum ModeVariant {
Compact(BeforeAfterMode),
CompactTree(CompactTree),
DeductionTree(SemanticTableau),
DataFlow(DataFlowMode),
EffectFlow(EffectFlowMode),
}
impl Default for ModeVariant {
fn default() -> Self {
Self::DeductionTree(SemanticTableau::default())
}
}
pub struct DisplayMode {
mode: ModeVariant,
pub dag: DagState,
pub timing_display: TimingDisplay,
}
impl Default for DisplayMode {
fn default() -> Self {
Self {
mode: ModeVariant::default(),
dag: DagState::None,
timing_display: TimingDisplay::Off,
}
}
}
impl DisplayMode {
pub fn switch_to_compatible_mode(&mut self, dag: &DagState) {
let is_compatible = matches!(
(&self.mode, dag),
(ModeVariant::DataFlow(_), DagState::Dataflow(_))
| (ModeVariant::EffectFlow(_), DagState::Effectflow(_))
| (
ModeVariant::Compact(_)
| ModeVariant::CompactTree(_)
| ModeVariant::DeductionTree(_),
DagState::Proof(_)
)
| (_, DagState::None)
);
if !is_compatible {
self.mode = match dag {
DagState::Proof(_) => ModeVariant::DeductionTree(SemanticTableau::default()),
DagState::Dataflow(_) => ModeVariant::DataFlow(DataFlowMode::default()),
DagState::Effectflow(_) => ModeVariant::EffectFlow(EffectFlowMode::default()),
DagState::None => return,
};
}
}
pub fn incompatibility_error(&self) -> Option<String> {
let dag_style = self.dag.style_name()?;
let expected_style = match &self.mode {
ModeVariant::Compact(_)
| ModeVariant::CompactTree(_)
| ModeVariant::DeductionTree(_) => "proof",
ModeVariant::DataFlow(_) => "dataflow",
ModeVariant::EffectFlow(_) => "effectflow",
};
if dag_style == expected_style {
None
} else {
Some(format!(
"Mode '{}' expects '{}' DAG, but received '{}' DAG",
self.name(),
expected_style,
dag_style
))
}
}
pub fn next(&mut self) {
self.mode = match take(&mut self.mode) {
ModeVariant::Compact(_) => ModeVariant::CompactTree(CompactTree::default()),
ModeVariant::CompactTree(_) => ModeVariant::DeductionTree(SemanticTableau::default()),
ModeVariant::DeductionTree(_) => ModeVariant::DataFlow(DataFlowMode::default()),
ModeVariant::DataFlow(_) => ModeVariant::EffectFlow(EffectFlowMode::default()),
ModeVariant::EffectFlow(_) => ModeVariant::Compact(BeforeAfterMode::default()),
};
}
pub fn prev(&mut self) {
self.mode = match take(&mut self.mode) {
ModeVariant::Compact(_) => ModeVariant::EffectFlow(EffectFlowMode::default()),
ModeVariant::CompactTree(_) => ModeVariant::Compact(BeforeAfterMode::default()),
ModeVariant::DeductionTree(_) => ModeVariant::CompactTree(CompactTree::default()),
ModeVariant::DataFlow(_) => ModeVariant::DeductionTree(SemanticTableau::default()),
ModeVariant::EffectFlow(_) => ModeVariant::DataFlow(DataFlowMode::default()),
};
}
pub fn name(&self) -> &'static str {
match &self.mode {
ModeVariant::Compact(m) => m.name(),
ModeVariant::CompactTree(m) => m.name(),
ModeVariant::DeductionTree(m) => m.name(),
ModeVariant::DataFlow(m) => m.name(),
ModeVariant::EffectFlow(m) => m.name(),
}
}
pub fn keybindings(&self) -> &'static [(&'static str, &'static str)] {
match &self.mode {
ModeVariant::Compact(m) => m.keybindings(),
ModeVariant::CompactTree(m) => m.keybindings(),
ModeVariant::DeductionTree(m) => m.keybindings(),
ModeVariant::DataFlow(m) => m.keybindings(),
ModeVariant::EffectFlow(m) => m.keybindings(),
}
}
pub fn current_selection(&self) -> Option<Selection> {
match &self.mode {
ModeVariant::Compact(m) => m.current_selection(),
ModeVariant::CompactTree(m) => m.current_selection(),
ModeVariant::DeductionTree(m) => m.current_selection(),
ModeVariant::DataFlow(m) => m.current_selection(),
ModeVariant::EffectFlow(m) => m.current_selection(),
}
}
pub fn legend_sections(&self) -> Vec<LegendSection> {
match &self.mode {
ModeVariant::Compact(m) => m.legend_sections(),
ModeVariant::CompactTree(m) => m.legend_sections(),
ModeVariant::DeductionTree(m) => m.legend_sections(),
ModeVariant::DataFlow(m) => m.legend_sections(),
ModeVariant::EffectFlow(m) => m.legend_sections(),
}
}
pub fn handle_key(&mut self, key: KeyEvent) -> bool {
match &mut self.mode {
ModeVariant::Compact(m) => m.handle_key(key),
ModeVariant::CompactTree(m) => m.handle_key(key),
ModeVariant::DeductionTree(m) => m.handle_key(key),
ModeVariant::DataFlow(m) => m.handle_key(key),
ModeVariant::EffectFlow(m) => m.handle_key(key),
}
}
pub fn handle_mouse(&mut self, mouse: MouseEvent) -> bool {
match &mut self.mode {
ModeVariant::Compact(m) => m.handle_mouse(mouse),
ModeVariant::CompactTree(m) => m.handle_mouse(mouse),
ModeVariant::DeductionTree(m) => m.handle_mouse(mouse),
ModeVariant::DataFlow(m) => m.handle_mouse(mouse),
ModeVariant::EffectFlow(m) => m.handle_mouse(mouse),
}
}
pub fn take_clicked_error(&mut self) -> Option<String> {
match &mut self.mode {
ModeVariant::Compact(m) => Mode::take_clicked_error(m),
ModeVariant::CompactTree(m) => Mode::take_clicked_error(m),
ModeVariant::DeductionTree(m) => Mode::take_clicked_error(m),
ModeVariant::DataFlow(m) => Mode::take_clicked_error(m),
ModeVariant::EffectFlow(m) => Mode::take_clicked_error(m),
}
}
pub fn update(&mut self, error: Option<String>) {
let error = error.or_else(|| self.incompatibility_error());
match (&mut self.mode, &self.dag) {
(ModeVariant::Compact(m), DagState::Proof(dag)) => {
m.update(Some(dag), error);
}
(ModeVariant::Compact(m), _) => {
m.update(None, error);
}
(ModeVariant::CompactTree(m), DagState::Proof(dag)) => {
m.update(Some(dag), error);
}
(ModeVariant::CompactTree(m), _) => {
m.update(None, error);
}
(ModeVariant::DeductionTree(m), DagState::Proof(dag)) => {
m.update(Some(dag), error);
}
(ModeVariant::DeductionTree(m), _) => {
m.update(None, error);
}
(ModeVariant::DataFlow(m), DagState::Dataflow(dag)) => {
m.update(Some(dag), error);
}
(ModeVariant::DataFlow(m), _) => {
m.update(None, error);
}
(ModeVariant::EffectFlow(m), DagState::Effectflow(dag)) => {
m.update(Some(dag), error);
}
(ModeVariant::EffectFlow(m), _) => {
m.update(None, error);
}
}
}
pub fn render(&mut self, frame: &mut Frame, area: Rect) {
self.sync_timing_display();
match &mut self.mode {
ModeVariant::Compact(m) => m.render(frame, area),
ModeVariant::CompactTree(m) => m.render(frame, area),
ModeVariant::DeductionTree(m) => m.render(frame, area),
ModeVariant::DataFlow(m) => m.render(frame, area),
ModeVariant::EffectFlow(m) => m.render(frame, area),
}
}
fn sync_timing_display(&mut self) {
let td = self.timing_display;
match &mut self.mode {
ModeVariant::DeductionTree(m) => m.viewport.timing_display = td,
ModeVariant::DataFlow(m) => m.viewport.timing_display = td,
ModeVariant::EffectFlow(m) => m.viewport.timing_display = td,
ModeVariant::Compact(_) | ModeVariant::CompactTree(_) => {}
}
}
}