package jp.toastkid.libs.utils;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;

/**
 * スレッドの実行を一定時間停止するメソッド集.
 * @author Toast kid
 * @see <a href="https://github.com/apache/storm/blob/master/storm-core/
 *src/jvm/backtype/storm/utils/Utils.java">Utils</a>
 * @see <a href="https://github.com/apache/storm/blob/master/storm-core/
 *src/jvm/backtype/storm/utils/Time.java">Time</a>
 *
 */
public final class TimeUtil {
    /** スレッド停止時間. */
    private static volatile Map<Thread, AtomicLong> threadSleepTimes;
    private static AtomicBoolean simulating = new AtomicBoolean(false);
    private static final Object sleepTimesLock = new Object();
    private static ThreadLocal<AtomicLong> simulatedCurrTimeMs
        = new ThreadLocal<AtomicLong>() {
        @Override
        public AtomicLong initialValue() {
            return new AtomicLong(0);
        }
    };

    public static void startSimulating() {
        synchronized(sleepTimesLock) {
            simulating.set(true);
            threadSleepTimes = new ConcurrentHashMap<Thread, AtomicLong>();
        }
    }

    public static void stopSimulating() {
        synchronized(sleepTimesLock) {
            simulating.set(false);
            threadSleepTimes = null;
        }
    }

    public static boolean isSimulating() {
        return simulating.get();
    }

    /**
     * Threadクラスのsleepメソッドを用いて、指定ミリ秒間プログラムの実行を停止する<BR>
     * 実行秒数はvariableWaitTime分の幅ができる
     * @param pWaitTime 停止する時間(単位:ミリ秒),推奨値10000
     * @param variableWaitTime ランダム停止幅(単位:ミリ秒),推奨値5000
     */
    public static final void waitRandomTime(
            final long pWaitTime,
            final long variableWaitTime
            ) {
        // 一定時間待機する
        long temp = pWaitTime + (long)(variableWaitTime * Math.random());
        //値が 0 以下になった場合は 0 にする
        if(temp < 0) {
            temp = 0;
        }
        TimeUtil.waitFixedTime(temp);
    }

    /**
     * Threadクラスのsleepメソッドを用いて指定ミリ秒間プログラムの実行を停止する<BR>
     * 実際には次の命令を実行するだけである
     * <pre>
     * Thread.sleep(pWaitTime);
     * </pre>
     * @param ms 停止する時間(単位:ミリ秒)
     */
    public static final void waitFixedTime(final long ms) {
        try{
            Thread.sleep(ms);
        } catch(final InterruptedException interuE) {
            interuE.printStackTrace();
        }
    }

    /**
     * Threadクラスのsleepメソッドを用いて30秒間プログラムの実行を停止する<BR>
     * 実際には次の命令を実行するだけである
     * <pre>
     * Thread.sleep(30000);
     * </pre>
     */
    public static final void waitHalfMinute() {
        try{
            Thread.sleep(30000);
        } catch(final InterruptedException interuE) {
            interuE.printStackTrace();
        }
    }

    /**
     * 指定ミリ秒後まで sleep する.
     * @param targetTimeMs sleepさせるミリ秒数
     */
    public static void sleep(final long ms) throws InterruptedException {
        sleepUntil(System.currentTimeMillis() + ms);
    }

    /**
     * 指定 timestamp ([ms]) まで sleep する.
     * @param targetTimeMs timestamp [ms]
     */
    public static void sleepUntil(final long targetTimeMs) throws InterruptedException {
        if(simulating.get()) {
            try {
                synchronized(sleepTimesLock) {
                    threadSleepTimes.put(Thread.currentThread(), new AtomicLong(targetTimeMs));
                }
                while(simulatedCurrTimeMs.get().get() < targetTimeMs) {
                    Thread.sleep(10);
                }
            } finally {
                synchronized(sleepTimesLock) {
                    if (simulating.get()) {
                        threadSleepTimes.remove(Thread.currentThread());
                    }
                }
            }
        } else {
            final long sleepTime = targetTimeMs - System.currentTimeMillis();
            if(sleepTime > 0) {
                Thread.sleep(sleepTime);
            }
        }
    }

    /**
     * タイムスタンプを秒で取得する.
     * @return 現在のタイムスタンプ(s)
     */
    public static final int currentTimeSecs() {
        return (int) (System.currentTimeMillis() / 1000);
    }
}

@@ -531,16 +531,17 @@
 eUtil %7B%0A
+%0A
     /** 
@@ -623,16 +623,17 @@
 pTimes;%0A
+%0A
     priv
@@ -692,24 +692,25 @@
 ean(false);%0A
+%0A
     private 
@@ -756,24 +756,25 @@
 w Object();%0A
+%0A
     private 
@@ -833,20 +833,16 @@
        =
- new
  ThreadL
@@ -849,104 +849,26 @@
 ocal
-%3CAtomicLong%3E() %7B%0A        @Override%0A        public AtomicLong initialValue() %7B%0A            return
+.withInitial(() -%3E
  new
@@ -881,33 +881,17 @@
 cLong(0)
-;%0A        %7D%0A    %7D
+)
 ;%0A%0A    p

use withInitial.