package edu.nyu.cs9053.concurrency.chapter14;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

/**
 * User: blangel
 * Date: 7/26/14
 * Time: 2:51 PM
 */
public class ScheduledExecutors implements Runnable {

    private final ScheduledExecutorService executorService;

    private final AtomicInteger counter;

    private final AtomicInteger actual;

    private final AtomicLong start;

    private final AtomicLong duration;

    private ScheduledFuture<?> future;

    public ScheduledExecutors(ScheduledExecutorService executorService) {
        this.executorService = executorService;
        this.counter = new AtomicInteger(0);
        this.actual = new AtomicInteger(0);
        this.start = new AtomicLong();
        this.duration = new AtomicLong();
    }

    public synchronized void fixedRate() {
        if (future != null) {
            throw new UnsupportedOperationException("Already running");
        }
        future = executorService.scheduleAtFixedRate(this, 0L, 1L, TimeUnit.MILLISECONDS);
        start.set(System.currentTimeMillis());
    }

    public synchronized void fixedDelay() {
        if (future != null) {
            throw new UnsupportedOperationException("Already running");
        }
        future = executorService.scheduleWithFixedDelay(this, 0L, 1L, TimeUnit.MILLISECONDS);
        start.set(System.currentTimeMillis());
    }

    @Override public void run() {
        try {
            Thread.sleep(10);
        } catch (InterruptedException ie) {
            Thread.currentThread().interrupt();
            return;
        }
        actual.addAndGet(1);
        if (counter.addAndGet(1) == 10) {
            duration.set(System.currentTimeMillis() - start.get());
            future.cancel(false);
            future = null;
        }
    }

    public void waitForFuture() throws ExecutionException, InterruptedException {
        try {
            future.get();
        } catch (CancellationException ce) {
            // great, done
        }
    }

    public int getActualRuns() {
        return actual.get();
    }

    public long getDuration() {
        return duration.get();
    }
}
