(* TEST
 include systhreads;
 hassysthreads;
 {
   bytecode;
 }{
   native;
 }
*)

open Event

type 'a buffer_channel = {
  input: 'a channel;
  output: 'a channel;
  thread: Thread.t;
}

let new_buffer_channel() =
  let ic = new_channel() in
  let oc = new_channel() in
  let rec buffer_process front rear =
    match (front, rear) with
    | (["EOF"], []) -> raise Thread.Exit
    | ([], []) -> buffer_process [sync(receive ic)] []
    | (hd::tl, _) ->
        select [
          wrap (receive ic) (fun x -> buffer_process front (x::rear));
          wrap (send oc hd) (fun () -> buffer_process tl rear)
        ]
    | ([], _) -> buffer_process (List.rev rear) [] in
  let t = Thread.create (buffer_process []) [] in
  { input = ic; output = oc; thread = t }

let buffer_send bc data =
  sync(send bc.input data)

let buffer_receive bc =
  receive bc.output

(* Test *)

let box = new_buffer_channel()
let ch = new_channel()

let f () =
  buffer_send box "un";
  buffer_send box "deux";
  sync (send ch 3)

let g () =
  print_int (sync(receive ch)); print_newline();
  print_string (sync(buffer_receive box)); print_newline();
  print_string (sync(buffer_receive box)); print_newline()

let _ =
  let t = Thread.create f () in
  g();
  buffer_send box "EOF";
  Thread.join box.thread;
  Thread.join t
