//! Inline expansion of protocol call statements.

use super::super::error::{ErrorSpan, ParseError};
use super::super::types::{ChoiceBranch, Statement};
use std::collections::HashMap;

/// Inline all Call statements by replacing them with their definitions.
#[allow(clippy::too_many_lines)]
pub(crate) fn inline_calls(
    statements: &[Statement],
    protocol_defs: &HashMap<String, Vec<Statement>>,
    input: &str,
) -> std::result::Result<Vec<Statement>, ParseError> {
    let mut result = Vec::new();

    for statement in statements {
        match statement {
            Statement::Call { name } => {
                let key = name.to_string();
                let called =
                    protocol_defs
                        .get(&key)
                        .ok_or_else(|| ParseError::UndefinedProtocol {
                            protocol: key.clone(),
                            span: ErrorSpan::from_line_col(1, 1, input),
                        })?;
                result.extend(inline_calls(called, protocol_defs, input)?);
            }
            Statement::Choice { role, branches, .. } => {
                let mut new_branches = Vec::new();
                for b in branches {
                    new_branches.push(ChoiceBranch {
                        label: b.label.clone(),
                        guard: b.guard.clone(),
                        statements: inline_calls(&b.statements, protocol_defs, input)?,
                    });
                }
                result.push(Statement::Choice {
                    role: role.clone(),
                    branches: new_branches,
                    annotations: Vec::new(),
                });
            }
            Statement::Loop { condition, body } => {
                result.push(Statement::Loop {
                    condition: condition.clone(),
                    body: inline_calls(body, protocol_defs, input)?,
                });
            }
            Statement::Parallel { branches } => {
                let mut new_branches = Vec::new();
                for b in branches {
                    new_branches.push(inline_calls(b, protocol_defs, input)?);
                }
                result.push(Statement::Parallel {
                    branches: new_branches,
                });
            }
            Statement::Rec { label, body } => {
                result.push(Statement::Rec {
                    label: label.clone(),
                    body: inline_calls(body, protocol_defs, input)?,
                });
            }
            _ => {
                result.push(statement.clone());
            }
        }
    }

    Ok(result)
}
