package com.jscape.util;

import java.util.Calendar;
import java.util.TimeZone;
import java.util.concurrent.locks.LockSupport;

/**
 * @author Alex Usun
 */
public class Clock {

    private static Provider provider = new SystemTime();

    public static Provider provider() {
        return provider;
    }

    public static void provider(Provider provider) {
        Clock.provider = provider;
    }

    public static long now() {
        return provider.now();
    }

    public static boolean isBefore(long date) {
        return now() <= date;
    }

    public static boolean isAfter(long date) {
        return date <= now();
    }

    public static long after(long period) {
        return now() + period;
    }

    public static long before(long period) {
        return now() - period;
    }

    public static long elapsedSince(long date) {
        return now() - date;
    }

    public static long remainedUntil(long date) {
        return date - now();
    }

    public static long noon() {
        return noon(TimeZone.getDefault());
    }

    public static long noon(TimeZone zone) {
        return noon(now(), zone);
    }

    public static long noon(long millis, TimeZone zone) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(millis);
        calendar.set(Calendar.HOUR_OF_DAY, 12);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        calendar.setTimeZone(zone);

        return calendar.getTimeInMillis();
    }

    public static long midnight() {
        return midnight(TimeZone.getDefault());
    }

    public static long midnight(TimeZone zone) {
        return midnight(now(), zone);
    }

    public static long midnight(long millis, TimeZone zone) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(millis);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        calendar.setTimeZone(zone);

        return calendar.getTimeInMillis();
    }

    public static void await(long period) {
        if (period > 0L) {
            LockSupport.parkNanos(period * 1000000L);
        }
    }

    private Clock() {
        // empty
    }

    public interface Provider {

        long now();

    }

    public static class SystemTime
            implements Provider {

        @Override
        public long now() {
            return System.currentTimeMillis();
        }
    }

}
