package saccubus.worker;

import java.util.concurrent.Callable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 途中経過を報告できるCallableです.
 * 通常のcallableのように使用できる他, 途中経過を呼び出し側が受け取れるようリスナを使用することができます.
 * リスナを使用した場合, 本インスタンスの処理終了は{@link WorkerListener#done(java.lang.Object)}(通常の終了),
 * {@link WorkerListener#cancelled()}(キャンセルされた),
 * {@link WorkerListener#error(java.lang.Throwable) }(例外発生)
 * のいずれかで以て通知されます.
 *
 * @author yuki
 */
public abstract class Worker<T, V> implements Callable<T> {

    private static final Logger logger = LoggerFactory.getLogger(Worker.class);
    private static int serialNumber = 0;
    private final int id;
    private final WorkerListener<T, V> listener;

    public Worker(WorkerListener<T, V> listener) {
        this.id = ++serialNumber;
        this.listener = listener;
    }

    @Override
    public final T call() throws Exception {
        try {
            final T result = work();
            if (listener != null) {
                listener.done(result);
            }
            return result;
        } catch (InterruptedException ex) {
            if( listener != null){
                listener.cancelled();
            }
            throw ex;
        } catch (Throwable th) {
            logger.error("work error", th);
            if(listener != null){
                listener.error(th);
            }
            throw th;
        }
    }

    protected abstract T work() throws Exception;

    public final int getId() {
        return id;
    }

    protected final void publish(V value) {
        if (listener != null) {
            listener.process(value);
        }
    }
}
