# `ActorSimulation`
[🔗](https://github.com/d-led/gen_server_virtual_time/blob/v1.1.1/packages/actor_simulation/lib/actor_simulation.ex#L1)

A DSL for simulating actor systems with message rates and statistics.

This module provides a way to define actors, their message sending patterns,
and simulate their interactions using virtual time.

## Example

    simulation =
      ActorSimulation.new()
      |> ActorSimulation.add_actor(:producer,
          send_pattern: {:periodic, 100, {:data, :id}},
          targets: [:consumer])
      |> ActorSimulation.add_actor(:consumer,
          on_receive: fn msg, state ->
            # Process message and maybe send response
            {:ok, state}
          end)
      |> ActorSimulation.run(duration: 5000)

    stats = ActorSimulation.get_stats(simulation)
    IO.inspect(stats)

# `add_actor`

Adds an actor to the simulation.

Options:
- `:send_pattern` - How this actor sends messages:
  - `{:periodic, interval, message}` - Send message every interval ms
  - `{:rate, messages_per_second, message}` - Send at a specific rate
  - `{:burst, count, interval, message}` - Send count messages every interval
- `:targets` - List of actor names to send messages to
- `:on_receive` - Function called when receiving a message: `fn msg, state -> {:ok, new_state} | {:send, msgs, new_state} end`
- `:on_match` - Pattern matching responses: `[{pattern, response_fn}]`
- `:initial_state` - Initial state for the actor (default: %{})

# `add_process`

Adds a real GenServerVirtualTime process to the simulation ("Process in the Loop").

This allows you to test real GenServer implementations alongside simulated actors.

Options:
- `:module` - The GenServer module to start (required)
- `:args` - Arguments to pass to the module's init/1
- `:targets` - List of actor names this process can send to (optional)

## Example

    defmodule MyRealServer do
      use VirtualTimeGenServer

      def init(args), do: {:ok, args}
      def handle_call(:ping, _from, state), do: {:reply, :pong, state}
    end

    simulation =
      ActorSimulation.new()
      |> ActorSimulation.add_process(:my_server, module: MyRealServer, args: %{})
      |> ActorSimulation.add_actor(:pinger,
          send_pattern: {:periodic, 100, {:call, :my_server, :ping}})

# `collect_current_stats`

Helper to collect current stats during simulation (for termination conditions).
Can be called from terminate_when functions.

Note: This is a live snapshot and may be called multiple times during run/2.

# `enable_trace`

Enables message tracing for the simulation.

# `generate_flowchart_report`

Generates a Mermaid flowchart report showing actor topology and statistics.

This creates a visual report with:
- Actor topology as a flowchart
- Statistics embedded in nodes (message counts, rates)
- Activity-based styling (color coding by traffic)
- Complete HTML page with stats table

## Options

- `:title` - Report title (default: "Simulation Report")
- `:show_stats_on_nodes` - Show stats on nodes (default: true)
- `:show_message_labels` - Show message types on edges (default: true)
- `:layout` - Direction: "TB", "LR", "RL", "BT" (default: "TB")
- `:style_by_activity` - Color by activity level (default: true)

## Example

    simulation = ActorSimulation.new()
      |> add_actor(:producer, send_pattern: {:periodic, 100, :data}, targets: [:consumer])
      |> add_actor(:consumer)
      |> run(duration: 1000)

    html = ActorSimulation.generate_flowchart_report(simulation,
      title: "My System",
      layout: "LR")

    File.write!("report.html", html)

Based on [Mermaid Flowchart Syntax](https://mermaid.js.org/syntax/flowchart.html)

# `get_stats`

Gets statistics from the simulation.

# `get_trace`

Gets the message trace from the simulation.
Returns a list of trace events for building sequence diagrams.

Each event is a map with:
- `:timestamp` - Virtual time when message was sent
- `:from` - Sender actor name
- `:to` - Receiver actor name
- `:message` - The message sent
- `:type` - `:cast`, `:call`, or `:send`

# `new`

Creates a new actor simulation.

Options:
- `:trace` - Enable message tracing for sequence diagrams (default: false)

## Example

    iex> simulation = ActorSimulation.new()
    iex> is_pid(simulation.clock)
    true
    iex> simulation.actors
    %{}

# `run`

Runs the simulation for the specified duration (in milliseconds).

Options:
- `:duration` - Maximum duration in milliseconds (default: 10,000)
- `:terminate_when` - Function that takes simulation and returns true to stop
- `:check_interval` - How often to check termination condition in ms (default: 100)
- `:expected_messages` - Integer count of expected messages to receive before terminating

## Boundary Condition Handling

The simulation automatically handles boundary conditions where events are scheduled
at exactly the end time. For example, a rate pattern sending 50 messages per second
over 1000ms will send messages at times 0, 20, 40, ..., 980, 1000. The simulation
advances the clock one tick beyond the specified duration and waits for quiescence
to ensure all boundary events are processed.

## Example

    # Run for fixed duration (backward compatible)
    simulation = ActorSimulation.run(simulation, duration: 5000)

    # Run until condition met (new feature)
    simulation = ActorSimulation.run(simulation,
      max_duration: 10_000,
      terminate_when: fn sim ->
        # Stop when all actors have sent at least 10 messages
        stats = ActorSimulation.collect_current_stats(sim)
        Enum.all?(stats.actors, fn {_name, s} -> s.sent_count >= 10 end)
      end
    )

    # Run until expected number of messages (convenience)
    simulation = ActorSimulation.run(simulation,
      max_duration: 10_000,
      expected_messages: 1000
    )

# `stop`

Stops the simulation and cleans up resources.

# `trace_to_mermaid`

Formats the trace as a Mermaid sequence diagram with enhanced styling.

Mermaid is widely supported in GitHub, GitLab, and many markdown viewers.
Uses features from [Mermaid sequence diagrams](https://docs.mermaidchart.com/mermaid-oss/syntax/sequenceDiagram.html):
- Different arrow types for call/cast/send
- Activation boxes for processing
- Notes with timestamps
- Background highlighting for grouped interactions
- Termination indicators showing when simulation stopped

## Example

    iex> simulation = %ActorSimulation{trace: [
    ...>   %{from: :alice, to: :bob, message: :hello, type: :send, timestamp: 100},
    ...>   %{from: :bob, to: :alice, message: :hi, type: :send, timestamp: 200}
    ...> ]}
    iex> mermaid = ActorSimulation.trace_to_mermaid(simulation)
    iex> String.contains?(mermaid, "sequenceDiagram")
    true
    iex> String.contains?(mermaid, "alice->>bob")
    true

# `write_flowchart_report`

Writes a flowchart report directly to a file.

Same options as `generate_flowchart_report/2`.

## Example

    simulation = ActorSimulation.new()
      |> add_actor(:source, send_pattern: {:periodic, 100, :msg}, targets: [:sink])
      |> add_actor(:sink)
      |> run(duration: 500)

    {:ok, path} = ActorSimulation.write_flowchart_report(
      simulation,
      "my_report.html",
      title: "Production System")

---

*Consult [api-reference.md](api-reference.md) for complete listing*
