// 
// Decompiled by Procyon v0.6.0
// 

package io.netty.util.concurrent;

import java.util.Objects;
import io.netty.util.internal.ObjectUtil;
import java.util.concurrent.TimeUnit;
import java.util.Map;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

final class DefaultMockTicker implements MockTicker
{
    private final ReentrantLock lock;
    private final Condition tickCondition;
    private final Condition sleeperCondition;
    private final AtomicLong nanoTime;
    private final Set<Thread> sleepers;
    
    DefaultMockTicker() {
        this.lock = new ReentrantLock(true);
        this.tickCondition = this.lock.newCondition();
        this.sleeperCondition = this.lock.newCondition();
        this.nanoTime = new AtomicLong();
        this.sleepers = Collections.newSetFromMap(new IdentityHashMap<Thread, Boolean>());
    }
    
    @Override
    public long nanoTime() {
        return this.nanoTime.get();
    }
    
    @Override
    public void sleep(final long delay, final TimeUnit unit) throws InterruptedException {
        ObjectUtil.checkPositiveOrZero(delay, "delay");
        Objects.requireNonNull(unit, "unit");
        if (delay == 0L) {
            return;
        }
        final long delayNanos = unit.toNanos(delay);
        this.lock.lockInterruptibly();
        try {
            final long startTimeNanos = this.nanoTime();
            this.sleepers.add(Thread.currentThread());
            this.sleeperCondition.signalAll();
            do {
                this.tickCondition.await();
            } while (this.nanoTime() - startTimeNanos < delayNanos);
        }
        finally {
            this.sleepers.remove(Thread.currentThread());
            this.lock.unlock();
        }
    }
    
    public void awaitSleepingThread(final Thread thread) throws InterruptedException {
        this.lock.lockInterruptibly();
        try {
            while (!this.sleepers.contains(thread)) {
                this.sleeperCondition.await();
            }
        }
        finally {
            this.lock.unlock();
        }
    }
    
    @Override
    public void advance(final long amount, final TimeUnit unit) {
        ObjectUtil.checkPositiveOrZero(amount, "amount");
        Objects.requireNonNull(unit, "unit");
        if (amount == 0L) {
            return;
        }
        final long amountNanos = unit.toNanos(amount);
        this.lock.lock();
        try {
            this.nanoTime.addAndGet(amountNanos);
            this.tickCondition.signalAll();
        }
        finally {
            this.lock.unlock();
        }
    }
}
