/*
 * Classname: Timeout
 *
 * Version: 1.0
 *
 * Author: Alex Usun
 */
package com.jscape.util;

/**
 * The timeout abstraction.
 *
 * @author Alex Usun
 * @version 1.0
 */
public class Timeout {

    private final long value;
    private final long endTime;
    private volatile boolean canceled;
    private Thread currentThread;

    public static void waitUntilExceeded(long value) {
        new Timeout(value).waitUntilExceeded();
    }

    /**
     * Creates a new timeout instance.
     *
     * @param value the timeout value in milliseconds
     */
    public Timeout(long value) {
        Assert.greater(value, 0, "Bad timeout value.");
        this.value = value;

        this.endTime = Clock.now() + this.value;
    }

    /**
     * Returns current timeout value.
     *
     * @return current timeout value in millisends
     */
    public long getValue() {
        return this.value;
    }

    /**
     * Returns timeout remain time.
     *
     * @return timeout remain time in millisends
     */
    public long remainedTime() {
        return this.endTime - Clock.now();
    }

    /**
     * Checks if the timeout is occured.
     *
     * @return <code>true</code> if the current timeout is occured
     */
    public boolean isExceeded() {
        return Clock.now() > this.endTime;
    }

    public void cancel() {
        this.canceled = true;

        if (this.currentThread != null) {
            this.currentThread.interrupt();
        }
    }

    /**
     * Suspends the current calling thread until the timeout exceeded.
     */
    public synchronized void waitUntilExceeded() {
        this.currentThread = Thread.currentThread();

        while (!isExceeded() && !this.canceled) {
            long sleepTime = remainedTime();

            try {
                Thread.sleep(sleepTime > 0 ? sleepTime : 0);
            } catch (InterruptedException e) {
                // ignore
            }
        }
    }

}
