package com.github.xeranic.blacksmith.deferred;

import java.util.List;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

public abstract class AbstractPromise<T> implements Promise<T> {

	private static final Logger log = Logger.getLogger(AbstractPromise.class.getName());

	protected State state;

	private List<DoneCallback<T>> doneCallbacks;
	private List<FailCallback> failCallbacks;
	private List<AlwaysCallback> alwaysCallbacks;
	private List<ProgressCallback> progressCallbacks;

	private T result;
	private Throwable failure;
	private Progress progress;

	@Override
	public final synchronized State getState() {
		return state;
	}

	@Override
	public final synchronized boolean isPending() {
		return state == State.PENDING;
	}

	@Override
	public final synchronized boolean isResolved() {
		return state == State.RESOLVED;
	}

	@Override
	public final synchronized boolean isRejected() {
		return state == State.REJECTED;
	}

	@Override
	public final synchronized Promise<T> done(DoneCallback<T> doneCallback) {
		if (isPending()) {
			if (doneCallbacks == null) {
				doneCallbacks = new ArrayList<>();
			}
			doneCallbacks.add(doneCallback);
		} else if (isResolved()) {
			triggerDone(doneCallback);
		}
		return this;
	}

	@Override
	public final synchronized Promise<T> fail(FailCallback failCallback) {
		if (isPending()) {
			if (failCallbacks == null) {
				failCallbacks = new ArrayList<>();
			}
			failCallbacks.add(failCallback);
		} else if (isRejected()) {
			triggerFail(failCallback);
		}
		return this;
	}

	@Override
	public final synchronized Promise<T> progress(ProgressCallback progressCallback) {
		if (isPending()) {
			if (progressCallbacks == null) {
				progressCallbacks = new ArrayList();
			}
			progressCallbacks.add(progressCallback);
		}
		triggerProgress(progressCallback);
		return this;
	}

	protected final synchronized void triggerDone() {
		if (doneCallbacks != null) {
			for (int i=0, len=doneCallbacks.size(); i<len; i++) {
				triggerDone(doneCallbacks.get(i));
			}
		}
	}

	private void triggerDone(DoneCallback<T> doneCallback) {
		try {
			doneCallback.onDone(result);
		} catch (Throwable t) {
			log.log(Level.SEVERE, "unexpected exception", t);
		}
	}

	protected final synchronized void triggerFail() {
		if (failCallbacks != null) {
			for (int i=0, len=failCallbacks.size(); i<len; i++) {
				triggerFail(failCallbacks.get(i));
			}			
		}
	}

	private void triggerFail(FailCallback failCallback) {
		try {
			failCallback.onFail(failure);
		} catch (Throwable t) {
			log.log(Level.SEVERE, "unexpected exception", t);
		}
	}

	protected final synchronized void triggerAlways() {
		if (alwaysCallbacks != null) {
			for (int i=0, len=alwaysCallbacks.size(); i<len; i++) {
				triggerAlways(alwaysCallbacks.get(i));
			}			
		}
	}

	private void triggerAlways(AlwaysCallback alwaysCallback) {
		try {
			alwaysCallback.onAlways(state, result, failure);
		} catch (Throwable t) {
			log.log(Level.SEVERE, "unexpected exception", t);
		}
	}

	protected final synchronized void triggerProgress() {
		if (progressCallbacks != null) {
			for (int i=0, len=progressCallbacks.size(); i<len; i++) {
				triggerProgress(progressCallbacks.get(i));
			}
		}
	}

	private void triggerProgress(ProgressCallback progressCallback) {
		try {
			progressCallback.onProgress(progress);
		} catch (Throwable t) {
			log.log(Level.SEVERE, "unexpected exception", t);
		}
	}
}

@@ -1051,32 +1051,83 @@
 doneCallback) %7B%0A
+%09%09if (doneCallback == null) %7B%0A%09%09%09return this;%0A%09%09%7D%0A%0A
 %09%09if (isPending(
@@ -1127,24 +1127,24 @@
 ending()) %7B%0A
-
 %09%09%09if (doneC
@@ -1400,32 +1400,83 @@
 failCallback) %7B%0A
+%09%09if (failCallback == null) %7B%0A%09%09%09return this;%0A%09%09%7D%0A%0A
 %09%09if (isPending(
@@ -1666,32 +1666,383 @@
 eturn this;%0A%09%7D%0A%0A
+%09@Override%0A%09public final synchronized Promise%3CT%3E always(AlwaysCallback alwaysCallback) %7B%0A%09%09if (alwaysCallback == null) %7B%0A%09%09%09return this;%0A%09%09%7D%0A%0A%09%09if (isPending()) %7B%0A%09%09%09if (alwaysCallbacks == null) %7B%0A%09%09%09%09alwaysCallbacks = new ArrayList%3C%3E();%0A%09%09%09%7D%0A%09%09%09alwaysCallbacks.add(alwaysCallback);%0A%09%09%7D else %7B%0A%09%09%09triggerAlways(alwaysCallback);%0A%09%09%7D%0A%09%09return this;%0A%09%7D%0A%0A
 %09@Override%0A%09publ
@@ -2116,24 +2116,79 @@
 Callback) %7B%0A
+%09%09if (progressCallback == null) %7B%0A%09%09%09return this;%0A%09%09%7D%0A%0A
 %09%09if (isPend
@@ -3961,10 +3961,11 @@
 %0A%09%09%7D%0A%09%7D%0A
+%0A
 %7D%0A

add always implementation