// actor_patterns.cm — Actor concurrency benchmarks
// Message passing, fan-out/fan-in, mailbox throughput.
// These use structured benchmarks with setup/run/teardown.

// Note: actor benchmarks are measured differently from pure compute.
// Each iteration sends messages and waits for results, so they're
// inherently slower but test real concurrency costs.

// Simple ping-pong: two actors sending messages back and forth
// Since we can't create real actors from a module, we simulate
// the message-passing patterns with function call overhead that
// mirrors what the actor dispatch does.

// Simulate message dispatch overhead
function make_mailbox() {
  return {
    queue: [],
    delivered: 0
  }
}

function send(mailbox, msg) {
  push(mailbox.queue, msg)
  return null
}

function receive(mailbox) {
  if (length(mailbox.queue) == 0) return null
  mailbox.delivered++
  return pop(mailbox.queue)
}

function drain(mailbox) {
  var count = 0
  while (length(mailbox.queue) > 0) {
    pop(mailbox.queue)
    count++
  }
  return count
}

// Ping-pong: simulate two actors exchanging messages
function ping_pong(rounds) {
  var box_a = make_mailbox()
  var box_b = make_mailbox()
  var i = 0
  var msg = null

  send(box_a, {type: "ping", val: 0})

  for (i = 0; i < rounds; i++) {
    // A receives and sends to B
    msg = receive(box_a)
    if (msg) {
      send(box_b, {type: "pong", val: msg.val + 1})
    }
    // B receives and sends to A
    msg = receive(box_b)
    if (msg) {
      send(box_a, {type: "ping", val: msg.val + 1})
    }
  }

  return box_a.delivered + box_b.delivered
}

// Fan-out: one sender, N receivers
function fan_out(n_receivers, messages_per) {
  var receivers = []
  var i = 0
  var j = 0
  for (i = 0; i < n_receivers; i++) {
    push(receivers, make_mailbox())
  }

  // Send messages to all receivers
  for (j = 0; j < messages_per; j++) {
    for (i = 0; i < n_receivers; i++) {
      send(receivers[i], {seq: j, data: j * 17})
    }
  }

  // All receivers drain
  var total = 0
  for (i = 0; i < n_receivers; i++) {
    total += drain(receivers[i])
  }

  return total
}

// Fan-in: N senders, one receiver
function fan_in(n_senders, messages_per) {
  var inbox = make_mailbox()
  var i = 0
  var j = 0

  // Each sender sends messages
  for (i = 0; i < n_senders; i++) {
    for (j = 0; j < messages_per; j++) {
      send(inbox, {sender: i, seq: j, data: i * 100 + j})
    }
  }

  // Receiver processes all
  var total = 0
  var msg = null
  msg = receive(inbox)
  while (msg) {
    total += msg.data
    msg = receive(inbox)
  }

  return total
}

// Pipeline: chain of processors
function pipeline(stages, items) {
  var boxes = []
  var i = 0
  var j = 0
  var msg = null

  for (i = 0; i <= stages; i++) {
    push(boxes, make_mailbox())
  }

  // Feed input
  for (i = 0; i < items; i++) {
    send(boxes[0], {val: i})
  }

  // Process each stage
  for (j = 0; j < stages; j++) {
    msg = receive(boxes[j])
    while (msg) {
      send(boxes[j + 1], {val: msg.val * 2 + 1})
      msg = receive(boxes[j])
    }
  }

  // Drain output
  var total = 0
  msg = receive(boxes[stages])
  while (msg) {
    total += msg.val
    msg = receive(boxes[stages])
  }

  return total
}

// Request-response pattern (simulate RPC)
function request_response(n_requests) {
  var client_box = make_mailbox()
  var server_box = make_mailbox()
  var i = 0
  var req = null
  var resp = null
  var total = 0

  for (i = 0; i < n_requests; i++) {
    // Client sends request
    send(server_box, {id: i, payload: i * 3, reply_to: client_box})

    // Server processes
    req = receive(server_box)
    if (req) {
      send(req.reply_to, {id: req.id, result: req.payload * 2 + 1})
    }

    // Client receives response
    resp = receive(client_box)
    if (resp) {
      total += resp.result
    }
  }

  return total
}

return {
  // Ping-pong: 10K rounds
  ping_pong_10k: function(n) {
    var i = 0
    var x = 0
    for (i = 0; i < n; i++) {
      x += ping_pong(10000)
    }
    return x
  },

  // Fan-out: 100 receivers, 100 messages each
  fan_out_100x100: function(n) {
    var i = 0
    var x = 0
    for (i = 0; i < n; i++) {
      x += fan_out(100, 100)
    }
    return x
  },

  // Fan-in: 100 senders, 100 messages each
  fan_in_100x100: function(n) {
    var i = 0
    var x = 0
    for (i = 0; i < n; i++) {
      x += fan_in(100, 100)
    }
    return x
  },

  // Pipeline: 10 stages, 1000 items
  pipeline_10x1k: function(n) {
    var i = 0
    var x = 0
    for (i = 0; i < n; i++) {
      x += pipeline(10, 1000)
    }
    return x
  },

  // Request-response: 5K requests
  rpc_5k: function(n) {
    var i = 0
    var x = 0
    for (i = 0; i < n; i++) {
      x += request_response(5000)
    }
    return x
  }
}
