package control.structures.continuations.quasarFiberChannelSameThread;

import java.util.concurrent.ExecutorService;

import co.paralleluniverse.fibers.Fiber;
import co.paralleluniverse.fibers.FiberExecutorScheduler;
import co.paralleluniverse.fibers.FiberScheduler;
import co.paralleluniverse.fibers.SuspendExecution;
import co.paralleluniverse.strands.SuspendableCallable;
import co.paralleluniverse.strands.Strand.State;
import co.paralleluniverse.strands.channels.Channel;
import co.paralleluniverse.strands.channels.Channels;

public class Coroutine {
	
    private final Fiber<Void> fiber;
	private final Channel<Object> channel;
	
    public Coroutine(SuspendableCallable<Void> proto) {
        this(proto, Fiber.DEFAULT_STACK_SIZE);
    }
    
    public Coroutine(SuspendableCallable<Void> proto, int stackSize) {
        channel = Channels.newChannel(1);
        
        //ExecutorService myExecutor = Executors.newSingleThreadExecutor();
        ExecutorService myExecutor = new MyExecutor();
        FiberScheduler myFiberScheduler = new FiberExecutorScheduler("my-scheduler", myExecutor);
        fiber = new Fiber<>(myFiberScheduler, () -> { channel.receive(); proto.run(); channel.close(); });
        //fiber = new Fiber<>(() -> { channel.receive(); proto.run(); channel.close();});
        fiber.start();
    }
	
	
	protected void yield() throws SuspendExecution {
		try {
			channel.receive();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	// as long as I am single threaded and as long as I am sticking to the protocol of alternating calls to yield() and run()
	// I don't need to throw the SuspendExecution exception here, because this channel should never suspend.
	protected void run() {
		try {
			channel.send("something");
		} catch (SuspendExecution | InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

    public State getState() {
        return fiber.getState();
    }
	
}