public abstract class Future<T> {

    private LinkedList<FutureCallback<T>> callbacks = new LinkedList<FutureCallback<T>>();

    private boolean isCompleted = false;
    private boolean isCanceled = false;
    private boolean isError = false;
    private T result = null;
    private Throwable error = null;

    public synchronized boolean isCompleted() {
        return isCompleted;
    }

    public synchronized boolean isError() {
        return isError;
    }

    public synchronized boolean isCanceled() {
        return isCanceled;
    }

    public synchronized Throwable error() {
        return error;
    }

    public synchronized T get() {
        return result;
    }

    public synchronized boolean addListener(FutureCallback<T> callback) {
        callbacks.add(callback);
        if (isCompleted) {
            if (isCanceled) {
                return true;
            }
            if (isError) {
                callback.onError(error);
            } else {
                callback.onResult(result);
            }
        }
        return isCompleted;
    }

    public synchronized void removeListener(FutureCallback<T> callback) {
        callbacks.remove(callback);
    }

    protected synchronized void onCancel() {
        if (isCompleted) {
            return;
        }
        isCompleted = true;
        isError = false;
        isCanceled = true;
    }

    protected synchronized void onCompleted(T res) {
        if (isCompleted) {
            return;
        }
        isCompleted = true;
        isCanceled = false;
        isError = false;
        error = null;
        result = res;

        for (FutureCallback<T> callback : callbacks) {
            callback.onResult(res);
        }
    }

    protected synchronized void onError(Throwable throwable) {
        if (isCompleted) {
            return;
        }
        isCompleted = true;
        isCanceled = false;
        isError = true;
        error = throwable;
        result = null;

        for (FutureCallback callback : callbacks) {
            callback.onError(throwable);
        }
    }

    protected synchronized void onTimeout() {
        onError(new FutureTimeoutException());
    }
} public interface FutureCallback<T> {
    public void onResult(T result);

    public void onError(Throwable throwable);
} public interface ActorExtension {
    public void preStart();

    public boolean onReceive(Object message);

    public void postStop();
} public class CallbackExtension implements ActorExtension {

    private HashMap<Object, Object> proxyMap = new HashMap<Object, Object>();

    private ActorRef self;

    public CallbackExtension(ActorRef self) {
        this.self = self;
    }

    @Override
    public void preStart() {

    }

    @Override
    public boolean onReceive(Object message) {
        if (message instanceof PerformCallback) {
            PerformCallback p = (PerformCallback) message;
            if (!proxyMap.containsKey(p.dest)) {
                return true;
            }
            try {
                p.method.invoke(p.dest, p.args);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
            return true;
        }
        return false;
    }

    public <T> T proxy(final T src, Class<T> tClass) {
        if (proxyMap.containsKey(src)) {
            return (T) proxyMap.get(src);
        }
        Object res = Proxy.newProxyInstance(self.system().getClassLoader(), new Class[]{tClass}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                self.send(new PerformCallback(src, method, args));
                return null;
            }
        });
        proxyMap.put(src, res);
        return (T) res;
    }

    @Override
    public void postStop() {
        proxyMap.clear();
    }

    private static class PerformCallback {
        private Object dest;
        private Method method;
        private Object[] args;

        private PerformCallback(Object dest, Method method, Object[] args) {
            this.dest = dest;
            this.method = method;
            this.args = args;
        }
    }
} public class RunnableExtension implements ActorExtension {
    @Override
    public void preStart() {

    }

    @Override
    public boolean onReceive(Object message) {
        if (message instanceof Runnable) {
            ((Runnable) message).run();
            return true;
        }
        return false;
    }

    @Override
    public void postStop() {

    }
} public class Mailbox {
    private EnvelopeCollection envelopes;

    private final EnvelopeCollection.EnvelopeComparator comparator = new EnvelopeCollection.EnvelopeComparator() {
        @Override
        public boolean equals(Envelope a, Envelope b) {
            return isEqualEnvelope(a, b);
        }
    };

    /**
     * Creating mailbox
     *
     * @param queue MailboxesQueue
     */
    public Mailbox(MailboxesQueue queue) {
        this.envelopes = new EnvelopeCollection(queue.getEnvelopeRoot());
    }


    /**
     * Send envelope at time
     *
     * @param envelope envelope
     * @param time     time
     */
    public void schedule(Envelope envelope, long time) {
        if (envelope.getMailbox() != this) {
            throw new RuntimeException("envelope.mailbox != this mailbox");
        }

        envelopes.putEnvelope(envelope, time);
    }

    /**
     * Send envelope once at time
     *
     * @param envelope envelope
     * @param time     time
     */
    public void scheduleOnce(Envelope envelope, long time) {
        if (envelope.getMailbox() != this) {
            throw new RuntimeException("envelope.mailbox != this mailbox");
        }

        envelopes.putEnvelopeOnce(envelope, time, comparator);
    }

    /**
     * Removing of envelope from queue
     *
     * @param envelope envelope for remove
     */
    public void unschedule(Envelope envelope) {
        envelopes.removeEnvelope(envelope, comparator);
    }

    /**
     * Getting all envelopes in mailbox
     *
     * @return envelopes
     */
    public Envelope[] allEnvelopes() {
        return envelopes.allEnvelopes();
    }

    /**
     * Clearing mailbox
     */
    public void clear() {
        envelopes.clear();
    }

    /**
     * Override this if you need to change filtering for scheduleOnce behaviour.
     * By default it check equality only of class names.
     *
     * @param a
     * @param b
     * @return is equal
     */
    protected boolean isEqualEnvelope(Envelope a, Envelope b) {
        return a.getMessage().getClass() == b.getMessage().getClass();
    }

    EnvelopeCollection getEnvelopes() {
        return envelopes;
    }
} public class DeadLetter {
    private Object message;

    public DeadLetter(Object message) {
        this.message = message;
    }

    public Object getMessage() {
        return message;
    }

    @Override
    public String toString() {
        return "DeadLetter(" + message + ")";
    }
} public class ActorAskImpl implements ActorExtension {

    private HashMap<Integer, AskContainer> asks = new HashMap<Integer, AskContainer>();
    private int nextReqId = 1;
    private ActorRef self;

    public ActorAskImpl(ActorRef self) {
        this.self = self;
    }

    public <T> AskFuture<T[]> combine(AskFuture... futures) {
        final AskFuture resultFuture = new AskFuture(this, 0);
        final CombineContainer container = new CombineContainer(futures.length);
        for (int i = 0; i < futures.length; i++) {
            final int index = i;
            container.futures[index] = futures[index];
            container.callbacks[index] = new AskCallback() {
                @Override
                public void onResult(Object result) {
                    container.completed[index] = true;
                    container.results[index] = result;
                    boolean isCompleted = true;
                    for (boolean c : container.completed) {
                        if (!c) {
                            isCompleted = false;
                            break;
                        }
                    }

                    if (isCompleted && !container.isCompleted) {
                        container.isCompleted = true;
                        for (int i = 0; i < container.futures.length; i++) {
                            container.futures[i].removeListener(container.callbacks[i]);
                        }
                        resultFuture.onResult(container.results);
                    }
                }

                @Override
                public void onError(Throwable throwable) {
                    if (!container.isCompleted) {
                        container.isCompleted = true;
                        for (int i = 0; i < container.futures.length; i++) {
                            container.futures[i].removeListener(container.callbacks[i]);
                            container.futures[i].cancel();
                        }
                        resultFuture.onError(throwable);
                    }

                }
            };
            container.futures[index].addListener(container.callbacks[index]);
        }
        return resultFuture;
    }

    public <T> AskFuture<T> ask(ActorRef ref, long timeout, AskCallback<T> callback) {
        int reqId = nextReqId++;
        AskFuture<T> future = new AskFuture<T>(this, reqId);
        if (callback != null) {
            future.addListener(callback);
        }
        AskContainer container = new AskContainer(future, ref, reqId);
        asks.put(reqId, container);
        ref.send(new TaskRequest(reqId), self);
        if (timeout > 0) {
            self.send(new TaskTimeout(reqId), timeout);
        }
        return future;
    }


    @Override
    public void preStart() {

    }

    @Override
    public boolean onReceive(Object message) {
        if (message instanceof DeadLetter) {
            if (onDeadLetter((DeadLetter) message)) {
                return true;
            }
        } else if (message instanceof TaskResult) {
            if (onTaskResult((TaskResult) message)) {
                return true;
            }
        } else if (message instanceof TaskTimeout) {
            if (onTaskTimeout((TaskTimeout) message)) {
                return true;
            }
        } else if (message instanceof TaskError) {
            if (onTaskError((TaskError) message)) {
                return true;
            }
        } else if (message instanceof TaskProgress) {
            if (onTaskProgress((TaskProgress) message)) {
                return true;
            }
        }

        return false;
    }

    @Override
    public void postStop() {
        for (AskContainer container : asks.values().toArray(new AskContainer[0])) {
            container.future.cancel();
        }
    }

    private boolean onTaskResult(TaskResult result) {
        AskContainer container = asks.remove(result.getRequestId());
        if (container != null) {
            // Logger.d("AvatarActor", self.getPath() + "|Result");
            container.future.onResult(result.getRes());
            return true;
        }

        return false;
    }

    private boolean onTaskProgress(TaskProgress progress) {
        AskContainer container = asks.get(progress.getRequestId());
        if (container != null) {
            // Logger.d("AvatarActor", self.getPath() + "|Progress");
            container.future.onProgress(progress.getProgress());
            return true;
        }

        return false;
    }

    private boolean onTaskError(TaskError error) {
        AskContainer container = asks.remove(error.getRequestId());
        if (container != null) {
            // Logger.d("AvatarActor", self.getPath() + "|Error");
            container.future.onError(error.getThrowable());
            return true;
        }

        return false;
    }

    private boolean onTaskTimeout(TaskTimeout taskTimeout) {
        AskContainer container = asks.remove(taskTimeout.getRequestId());
        if (container != null) {
            // Logger.d("AvatarActor", self.getPath() + "|Timeout");
            container.future.onTimeout();
            return true;
        }

        return false;
    }

    public boolean onTaskCancelled(int reqId) {
        AskContainer container = asks.remove(reqId);
        if (container != null) {
            // Logger.d("AvatarActor", self.getPath() + "|Cancel");
            container.ref.send(new TaskCancel(reqId), self);
            return true;
        }

        return false;
    }

    private boolean onDeadLetter(DeadLetter letter) {
        if (letter.getMessage() instanceof TaskRequest) {
            TaskRequest request = (TaskRequest) letter.getMessage();
            AskContainer container = asks.get(request.getRequestId());
            if (container != null) {
                container.ref.send(request, self);
                return true;
                // Mimic dead letter with timeout exception
//                container.future.onError(new AskTimeoutException());
//                return true;
            }
        }

        return false;
    }

    private class AskContainer {
        public final AskFuture future;
        public final ActorRef ref;
        public final int requestId;

        private AskContainer(AskFuture future, ActorRef ref, int requestId) {
            this.future = future;
            this.ref = ref;
            this.requestId = requestId;
        }
    }

    private class CombineContainer {
        public boolean isCompleted = false;
        public Object[] results;
        public boolean[] completed;
        public AskFuture[] futures;
        public AskCallback[] callbacks;

        public CombineContainer(int count) {
            results = new Object[count];
            completed = new boolean[count];
            callbacks = new AskCallback[count];
            futures = new AskFuture[count];
        }
    }
} public interface AskCallback<T> {
    public void onResult(T result);

    public void onError(Throwable throwable);
} public class AskFuture<T> {

    private LinkedList<AskCallback> callbacks = new LinkedList<AskCallback>();

    private ActorAskImpl askImpl;
    private int reqId;

    private boolean isCompleted = false;
    private boolean isCanceled = false;
    private boolean isError = false;
    private T result = null;
    private Throwable error = null;

    AskFuture(ActorAskImpl askImpl, int reqId) {
        this.askImpl = askImpl;
        this.reqId = reqId;
    }

    public void addListener(AskCallback callback) {
        callbacks.add(callback);
    }

    public void removeListener(AskCallback callback) {
        callbacks.remove(callback);
    }

    public boolean isCompleted() {
        return isCompleted;
    }

    public boolean isError() {
        return isError;
    }

    public boolean isCanceled() {
        return isCanceled;
    }

    public Throwable error() {
        return error;
    }

    public T result() {
        return result;
    }

    public void cancel() {
        if (isCompleted) {
            return;
        }
        isCompleted = true;
        isError = false;
        isCanceled = true;

//        for (AskCallback callback : callbacks) {
//            callback.onError(new AskCancelledException());
//        }

        askImpl.onTaskCancelled(reqId);
    }

    void onError(Throwable throwable) {
        if (isCompleted) {
            return;
        }
        isCompleted = true;
        isCanceled = false;
        isError = true;
        error = throwable;
        result = null;

        for (AskCallback callback : callbacks) {
            callback.onError(throwable);
        }
    }

    void onResult(T res) {
        if (isCompleted) {
            return;
        }
        isCompleted = true;
        isCanceled = false;
        isError = false;
        error = null;
        result = res;

        for (AskCallback callback : callbacks) {
            callback.onResult(res);
        }
    }

    void onProgress(Object res) {
        if (isCompleted) {
            return;
        }

        for (AskCallback callback : callbacks) {
            if (callback instanceof AskProgressCallback) {
                ((AskProgressCallback) callback).onProgress(res);
            }
        }
    }

    void onTimeout() {
        onError(new AskTimeoutException());
    }
} public class TypedAskExtensions implements ActorExtension {

    private HashMap<Integer, AskContainer> containers = new HashMap<Integer, AskContainer>();

    private ActorRef self;

    private AtomicInteger askId = new AtomicInteger();

    public TypedAskExtensions(ActorRef self) {
        this.self = self;
    }

    @Override
    public void preStart() {

    }

    @Override
    public boolean onReceive(Object message) {
        if (message instanceof TypedFutureResult) {
            TypedFutureResult futureResult = (TypedFutureResult) message;
            AskContainer container = containers.remove(futureResult.getId());
            if (container != null) {
                if (container.callback != null) {
                    container.callback.onResult(futureResult.getRes());
                }
            }
            return true;
        } else if (message instanceof TypedFutureError) {
            TypedFutureError futureError = (TypedFutureError) message;
            AskContainer container = containers.remove(futureError.getId());
            if (container != null) {
                if (container.callback != null) {
                    container.callback.onError(futureError.getT());
                }
            }
            return true;
        }
        return false;
    }

    @Override
    public void postStop() {

    }

    public <T> void ask(Future<T> future, FutureCallback<T> callback) {
        final int id = askId.incrementAndGet();
        future.addListener(new FutureCallback<T>() {
            @Override
            public void onResult(T result) {
                self.send(new TypedFutureResult(id, result));
            }

            @Override
            public void onError(Throwable throwable) {
                self.send(new TypedFutureError(id, throwable));
            }
        });
        containers.put(id, new AskContainer(id, future, callback));
    }

    private class AskContainer {
        private int id;
        private Future future;
        private FutureCallback callback;

        private AskContainer(int id, Future future, FutureCallback callback) {
            this.id = id;
            this.future = future;
            this.callback = callback;
        }
    }
}
import com.droidkit.actors.concurrency.Future;
import com.droidkit.actors.concurrency.FutureCallback;
import com.droidkit.actors.extensions.ActorExtension;
import com.droidkit.actors.extensions.CallbackExtension;
import com.droidkit.actors.extensions.RunnableExtension;
import com.droidkit.actors.mailbox.Mailbox;
import com.droidkit.actors.messages.DeadLetter;
import com.droidkit.actors.tasks.ActorAskImpl;
import com.droidkit.actors.tasks.AskCallback;
import com.droidkit.actors.tasks.AskFuture;
import com.droidkit.actors.typed.TypedAskExtensions;
import java.util.ArrayList;
import java.util.UUID;
package com.droidkit.actors;



/**
 * Actor object
 *
 * @author Steve Ex3NDR Korshakov (steve@actor.im)
 */
public class Actor {

    private UUID uuid;
    private String path;

    private ActorContext context;
    private Mailbox mailbox;

    private ActorAskImpl askPattern;
    private TypedAskExtensions typedAsk;
    private CallbackExtension callbackExtension;
    private ArrayList<ActorExtension> extensions = new ArrayList<ActorExtension>();