// Sequence diagram minimal grammar (phase 1).

grammar;

use crate::diagrams::sequence::{
  Action, Tok, LINETYPE_ALT_ELSE, LINETYPE_ALT_END, LINETYPE_ALT_START, LINETYPE_BREAK_END,
  LINETYPE_BREAK_START, LINETYPE_CRITICAL_END, LINETYPE_CRITICAL_OPTION, LINETYPE_CRITICAL_START,
  LINETYPE_LOOP_END, LINETYPE_LOOP_START, LINETYPE_OPT_END, LINETYPE_OPT_START, LINETYPE_PAR_AND,
  LINETYPE_PAR_END, LINETYPE_PAR_OVER_START, LINETYPE_PAR_START, LINETYPE_RECT_END,
  LINETYPE_RECT_START
};

extern {
  type Location = usize;
  type Error = crate::diagrams::sequence::LexError;

  enum Tok {
    Newline => Tok::Newline,

    "sequenceDiagram" => Tok::SequenceDiagram,
    "participant" => Tok::Participant,
    "actor" => Tok::ActorKw,
    "create" => Tok::Create,
    "destroy" => Tok::Destroy,
    "as" => Tok::As,

    "box" => Tok::Box,
    "end" => Tok::End,

    "loop" => Tok::Loop,
    "rect" => Tok::Rect,
    "opt" => Tok::Opt,
    "alt" => Tok::Alt,
    "else" => Tok::Else,
    "par" => Tok::Par,
    "par_over" => Tok::ParOver,
    "and" => Tok::And,
    "critical" => Tok::Critical,
    "option" => Tok::Option,
    "break" => Tok::Break,

    "note" => Tok::Note,
    "left_of" => Tok::LeftOf,
    "right_of" => Tok::RightOf,
    "over" => Tok::Over,

    "links" => Tok::Links,
    "link" => Tok::Link,
    "properties" => Tok::Properties,
    "details" => Tok::Details,

    "autonumber" => Tok::Autonumber,
    "off" => Tok::Off,

    "activate" => Tok::Activate,
    "deactivate" => Tok::Deactivate,

    Comma => Tok::Comma,
    Plus => Tok::Plus,
    Minus => Tok::Minus,

    Num => Tok::Num(<i64>),
    Actor => Tok::Actor(<String>),
    Text => Tok::Text(<String>),
    RestOfLine => Tok::RestOfLine(<String>),
    SignalType => Tok::SignalType(<i32>),
    Config => Tok::Config(<String>),

    Title => Tok::Title(<String>),
    CompatTitle => Tok::CompatTitle(<String>),
    AccTitle => Tok::AccTitle(<String>),
    AccDescr => Tok::AccDescr(<String>),
    AccDescrMultiline => Tok::AccDescrMultiline(<String>),
  }
}

pub Actions: Vec<Action> = {
  <_n:Newlines> "sequenceDiagram" <_n2:Newlines> <a:Statements> => a
};

Statements: Vec<Action> = {
  => Vec::new(),
  <s:Statement> <rest:StatementRest> => {
    let mut v = s;
    v.extend(rest);
    v
  }
};

StatementRest: Vec<Action> = {
  <_n:Newlines1> <s:Statement> <rest:StatementRest> => {
    let mut v = s;
    v.extend(rest);
    v
  },
  Newlines => Vec::new(),
};

Newlines: () = {
  => (),
  Newline Newlines => (),
};

Newlines1: () = {
  Newline Newlines => (),
};

Statement: Vec<Action> = {
  <p:ParticipantStatement> => p,
  <c:CreateStatement> => c,
  <d:DestroyStatement> => d,
  <b:BlockStatement> => b,
  <s:SignalStatement> => s,
  <n:NoteStatement> => n,
  <b:BoxStatement> => b,
  <l:LinksStatement> => l,
  <l:LinkStatement> => l,
  <p:PropertiesStatement> => p,
  <d:DetailsStatement> => d,
  <t:TitleStatement> => vec![Action::SetTitle(t)],
  <a:AccTitleStatement> => vec![Action::SetAccTitle(a)],
  <d:AccDescrStatement> => vec![Action::SetAccDescr(d)],
  <au:AutonumberStatement> => vec![au],
  <ac:ActivateStatement> => vec![ac],
  <de:DeactivateStatement> => vec![de],
};

Doc: Vec<Action> = {
  => Vec::new(),
  Newline <rest:Doc> => rest,
  <s:Statement> Newline <rest:Doc> => {
    let mut v = s;
    v.extend(rest);
    v
  },
};

TitleStatement: String = {
  <t:Title> => t,
  <t:CompatTitle> => t,
};

AccTitleStatement: String = {
  <t:AccTitle> => t,
};

AccDescrStatement: String = {
  <t:AccDescr> => t,
  <t:AccDescrMultiline> => t,
};

ActorId: String = {
  <a:Actor> => a,
  <n:Num> => n.to_string(),
};

ParticipantDecl: (String, Option<String>, String, Option<String>) = {
  "participant" <id:ActorId> "as" <desc:RestOfLine> => (id, Some(desc), "participant".to_string(), None),
  "participant" <id:ActorId> <cfg:Config> => (id, None, "participant".to_string(), Some(cfg)),
  "participant" <id:ActorId> => (id, None, "participant".to_string(), None),
  "actor" <id:ActorId> "as" <desc:RestOfLine> => (id, Some(desc), "actor".to_string(), None),
  "actor" <id:ActorId> => (id, None, "actor".to_string(), None),
};

ParticipantStatement: Vec<Action> = {
  <d:ParticipantDecl> => vec![
    Action::AddParticipant { id: d.0, description: d.1, draw: d.2, config: d.3 },
  ],
};

CreateStatement: Vec<Action> = {
  "create" <d:ParticipantDecl> => vec![
    Action::CreateParticipant { id: d.0, description: d.1, draw: d.2, config: d.3 },
  ],
};

DestroyStatement: Vec<Action> = {
  "destroy" <a:ActorId> => vec![Action::DestroyParticipant { id: a }],
};

BlockStatement: Vec<Action> = {
  <l:LoopBlock> => l,
  <o:OptBlock> => o,
  <a:AltBlock> => a,
  <p:ParBlock> => p,
  <p:ParOverBlock> => p,
  <c:CriticalBlock> => c,
  <b:BreakBlock> => b,
  <r:RectBlock> => r,
};

LoopBlock: Vec<Action> = {
  "loop" <h:RestOfLine> <body:Doc> "end" => {
    let mut v = vec![Action::ControlSignal { signal_type: LINETYPE_LOOP_START, text: Some(h) }];
    v.extend(body);
    v.push(Action::ControlSignal { signal_type: LINETYPE_LOOP_END, text: None });
    v
  }
};

OptBlock: Vec<Action> = {
  "opt" <h:RestOfLine> <body:Doc> "end" => {
    let mut v = vec![Action::ControlSignal { signal_type: LINETYPE_OPT_START, text: Some(h) }];
    v.extend(body);
    v.push(Action::ControlSignal { signal_type: LINETYPE_OPT_END, text: None });
    v
  }
};

RectBlock: Vec<Action> = {
  "rect" <h:RestOfLine> <body:Doc> "end" => {
    let mut v = vec![Action::ControlSignal { signal_type: LINETYPE_RECT_START, text: Some(h) }];
    v.extend(body);
    v.push(Action::ControlSignal { signal_type: LINETYPE_RECT_END, text: None });
    v
  }
};

AltBlock: Vec<Action> = {
  "alt" <h:RestOfLine> <body:Doc> <else_branches:AltElseBranch*> "end" => {
    let mut v = vec![Action::ControlSignal { signal_type: LINETYPE_ALT_START, text: Some(h) }];
    v.extend(body);
    for (label, doc) in else_branches {
      v.push(Action::ControlSignal { signal_type: LINETYPE_ALT_ELSE, text: Some(label) });
      v.extend(doc);
    }
    v.push(Action::ControlSignal { signal_type: LINETYPE_ALT_END, text: None });
    v
  }
};

AltElseBranch: (String, Vec<Action>) = {
  "else" <h:RestOfLine> <body:Doc> => (h, body),
};

ParBlock: Vec<Action> = {
  "par" <h:RestOfLine> <body:Doc> <branches:ParAndBranch*> "end" => {
    let mut v = vec![Action::ControlSignal { signal_type: LINETYPE_PAR_START, text: Some(h) }];
    v.extend(body);
    for (label, doc) in branches {
      v.push(Action::ControlSignal { signal_type: LINETYPE_PAR_AND, text: Some(label) });
      v.extend(doc);
    }
    v.push(Action::ControlSignal { signal_type: LINETYPE_PAR_END, text: None });
    v
  }
};

ParOverBlock: Vec<Action> = {
  "par_over" <h:RestOfLine> <body:Doc> "end" => {
    let mut v = vec![Action::ControlSignal { signal_type: LINETYPE_PAR_OVER_START, text: Some(h) }];
    v.extend(body);
    v.push(Action::ControlSignal { signal_type: LINETYPE_PAR_END, text: None });
    v
  }
};

ParAndBranch: (String, Vec<Action>) = {
  "and" <h:RestOfLine> <body:Doc> => (h, body),
};

CriticalBlock: Vec<Action> = {
  "critical" <h:RestOfLine> <body:Doc> <branches:CriticalOptionBranch*> "end" => {
    let mut v = vec![Action::ControlSignal { signal_type: LINETYPE_CRITICAL_START, text: Some(h) }];
    v.extend(body);
    for (label, doc) in branches {
      v.push(Action::ControlSignal { signal_type: LINETYPE_CRITICAL_OPTION, text: Some(label) });
      v.extend(doc);
    }
    v.push(Action::ControlSignal { signal_type: LINETYPE_CRITICAL_END, text: None });
    v
  }
};

CriticalOptionBranch: (String, Vec<Action>) = {
  "option" <h:RestOfLine> <body:Doc> => (h, body),
};

BreakBlock: Vec<Action> = {
  "break" <h:RestOfLine> <body:Doc> "end" => {
    let mut v = vec![Action::ControlSignal { signal_type: LINETYPE_BREAK_START, text: Some(h) }];
    v.extend(body);
    v.push(Action::ControlSignal { signal_type: LINETYPE_BREAK_END, text: None });
    v
  }
};

BoxStatement: Vec<Action> = {
  "box" <h:RestOfLine> <_n:Newlines1> <inner:BoxBody> "end" => {
    let mut v = vec![Action::BoxStart { header: h }];
    v.extend(inner);
    v.push(Action::BoxEnd);
    v
  },
};

BoxBody: Vec<Action> = {
  => Vec::new(),
  <p:ParticipantStatement> <_n:Newlines1> <rest:BoxBody> => {
    let mut v = p;
    v.extend(rest);
    v
  },
};

LinksStatement: Vec<Action> = {
  "links" <a:ActorId> <t:Text> => vec![
    Action::EnsureParticipant { id: a.clone() },
    Action::AddLinks { actor: a, text: t },
  ],
};

LinkStatement: Vec<Action> = {
  "link" <a:ActorId> <t:Text> => vec![
    Action::EnsureParticipant { id: a.clone() },
    Action::AddLink { actor: a, text: t },
  ],
};

PropertiesStatement: Vec<Action> = {
  "properties" <a:ActorId> <t:Text> => vec![
    Action::EnsureParticipant { id: a.clone() },
    Action::AddProperties { actor: a, text: t },
  ],
};

DetailsStatement: Vec<Action> = {
  "details" <a:ActorId> <t:Text> => vec![
    Action::EnsureParticipant { id: a.clone() },
    Action::AddDetails { actor: a, text: t },
  ],
};

SignalStatement: Vec<Action> = {
  <from:ActorId> <ty:SignalType> Plus <to:ActorId> <text:Text> => vec![
    Action::EnsureParticipant { id: from.clone() },
    Action::EnsureParticipant { id: to.clone() },
    Action::AddMessage { from: from.clone(), to: to.clone(), signal_type: ty, text, activate: true },
    Action::ActiveStart { actor: to },
  ],
  <from:ActorId> <ty:SignalType> Minus <to:ActorId> <text:Text> => vec![
    Action::EnsureParticipant { id: from.clone() },
    Action::EnsureParticipant { id: to.clone() },
    Action::AddMessage { from: from.clone(), to: to.clone(), signal_type: ty, text, activate: false },
    Action::ActiveEnd { actor: from },
  ],
  <from:ActorId> <ty:SignalType> <to:ActorId> <text:Text> => vec![
    Action::EnsureParticipant { id: from.clone() },
    Action::EnsureParticipant { id: to.clone() },
    Action::AddMessage { from, to, signal_type: ty, text, activate: false },
  ],
};

NoteStatement: Vec<Action> = {
  "note" <p:Placement> <a:ActorId> <t:Text> => vec![
    Action::EnsureParticipant { id: a.clone() },
    Action::AddNote { actors: vec![a], placement: p, text: t },
  ],
  "note" "over" <a:ActorId> Comma <b:ActorId> <t:Text> => vec![
    Action::EnsureParticipant { id: a.clone() },
    Action::EnsureParticipant { id: b.clone() },
    Action::AddNote { actors: vec![a, b], placement: 2, text: t },
  ],
  "note" "over" <a:ActorId> <t:Text> => vec![
    Action::EnsureParticipant { id: a.clone() },
    Action::AddNote { actors: vec![a.clone(), a], placement: 2, text: t },
  ],
};

Placement: i32 = {
  "left_of" => 0,
  "right_of" => 1,
};

AutonumberStatement: Action = {
  "autonumber" <a:Num> <b:Num> => Action::Autonumber { start: Some(a), step: Some(b), visible: true },
  "autonumber" <a:Num> => Action::Autonumber { start: Some(a), step: Some(1), visible: true },
  "autonumber" "off" => Action::Autonumber { start: None, step: None, visible: false },
  "autonumber" => Action::Autonumber { start: None, step: None, visible: true },
};

ActivateStatement: Action = {
  "activate" <a:ActorId> => Action::ActiveStart { actor: a },
};

DeactivateStatement: Action = {
  "deactivate" <a:ActorId> => Action::ActiveEnd { actor: a },
};
