package org.skife.futures;

import EDU.oswego.cs.dl.util.concurrent.LinkedQueue;
import EDU.oswego.cs.dl.util.concurrent.PooledExecutor;
import EDU.oswego.cs.dl.util.concurrent.Slot;

public class AsyncEvaluator implements Evaluator
{
    private final PooledExecutor executor;

    public AsyncEvaluator(int size)
    {
        executor = new PooledExecutor(new LinkedQueue());
        executor.createThreads(size);
        executor.waitWhenBlocked();
        executor.setKeepAliveTime(-1);
    }

    public Future promise(final Expression exp)
    {
        final Slot result = new Slot();
        final boolean[] success = {false};
        try
        {
            executor.execute(new Runnable()
            {
                public void run()
                {
                    try
                    {
                        Object expr_result;
                        try
                        {
                            expr_result = exp.evaluate();
                            success[0] = true;
                        }
                        catch (Exception e)
                        {
                            expr_result = e;
                            success[0] = false;
                        }
                        result.put(expr_result);
                    }
                    catch (InterruptedException e)
                    {
                        throw new IllegalStateException("Interrupted exception thrown while putting result " +
                                                        "attempting to place a job");
                    }
                }
            });
        }
        catch (InterruptedException e)
        {
            throw new IllegalStateException("Interrupted exception thrown while executor " +
                                            "attempting to place a job");
        }

        return new Future()
        {
            public boolean isAvailable()
            {
                return result.peek() != null;
            }

            public Object getResult() throws Exception
            {
                final Object ret_val = result.take();

                if (success[0])
                {
                    return ret_val;
                }
                else
                {
                    throw (Exception) ret_val;
                }
            }
        };
    }
}
