#!/usr/bin/perl6

class Spawn {
	has &.cb;
	has @.args;
	has $.name;
	has $.in = Channel.new;

	method receive() {
		return $!in.receive;
	}

	multi method send(Str $name, \msg) {
		for @*scheduler {
			if .name eq $name {
				.send([$!name, msg]);
				last;
			}
		}
	}

	multi method send(\msg) {
		$!in.send(msg);
	}

	method loop {
		start {
			&!cb(self, |@!args);
		}
	}
}

my @*scheduler = [];

sub main-loop() {
	await Promise.allof(
		[ @*scheduler>>.loop ]
	);
}

sub spawn(&cb, @args, :$name) {
	@*scheduler.push(
		Spawn.new(
			cb => &cb,
			args => @args,
			name => $name
		)
	);
}

&spawn(&ping, ["pong", 3], name => "ping");
&spawn(&pong, [], name => "pong");
&main-loop();

sub ping(\s, Str $name, Int $x) {
	for ^$x {
		s.send($name, "pong");
		if s.receive -> ($ping, $msg) {
			given $msg {
				when /ping/ {
					say "{$msg} received from {$ping}";
				}
			}
		}
	}
	s.send($name, "finish");
	say "ping finished!";
}
sub pong(\s) {
	while (True) {
		if s.receive -> ($ping, $msg) {
			given $msg {
				when /finish/ {
					say "pong finished.";
					last;
				}
				when /pong/ {
					say "{$msg} received from {$ping}";
					s.send($ping, "ping");
				}
			}
		}
	}
}