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

package io.netty.channel.local;

import io.netty.channel.IoOps;
import java.util.concurrent.atomic.AtomicBoolean;
import io.netty.channel.IoRegistration;
import java.util.Iterator;
import java.util.concurrent.locks.LockSupport;
import io.netty.channel.IoHandlerContext;
import io.netty.util.internal.StringUtil;
import io.netty.channel.IoHandle;
import io.netty.channel.IoHandlerFactory;
import java.util.Objects;
import java.util.HashSet;
import io.netty.util.concurrent.ThreadAwareExecutor;
import java.util.Set;
import io.netty.channel.IoHandler;

public final class LocalIoHandler implements IoHandler
{
    private final Set<LocalIoHandle> registeredChannels;
    private final ThreadAwareExecutor executor;
    private volatile Thread executionThread;
    
    private LocalIoHandler(final ThreadAwareExecutor executor) {
        this.registeredChannels = new HashSet<LocalIoHandle>(64);
        this.executor = Objects.requireNonNull(executor, "executor");
    }
    
    public static IoHandlerFactory newFactory() {
        return LocalIoHandler::new;
    }
    
    private static LocalIoHandle cast(final IoHandle handle) {
        if (handle instanceof LocalIoHandle) {
            return (LocalIoHandle)handle;
        }
        throw new IllegalArgumentException("IoHandle of type " + StringUtil.simpleClassName(handle) + " not supported");
    }
    
    @Override
    public int run(final IoHandlerContext context) {
        if (this.executionThread == null) {
            this.executionThread = Thread.currentThread();
        }
        if (context.canBlock()) {
            LockSupport.parkNanos(this, context.delayNanos(System.nanoTime()));
        }
        if (context.shouldReportActiveIoTime()) {
            context.reportActiveIoTime(0L);
        }
        return 0;
    }
    
    @Override
    public void wakeup() {
        if (!this.executor.isExecutorThread(Thread.currentThread())) {
            final Thread thread = this.executionThread;
            if (thread != null) {
                LockSupport.unpark(thread);
            }
        }
    }
    
    @Override
    public void prepareToDestroy() {
        for (final LocalIoHandle handle : this.registeredChannels) {
            handle.closeNow();
        }
        this.registeredChannels.clear();
    }
    
    @Override
    public void destroy() {
    }
    
    @Override
    public IoRegistration register(final IoHandle handle) {
        final LocalIoHandle localHandle = cast(handle);
        if (this.registeredChannels.add(localHandle)) {
            final LocalIoRegistration registration = new LocalIoRegistration(this.executor, localHandle);
            localHandle.registered();
            return registration;
        }
        throw new IllegalStateException();
    }
    
    @Override
    public boolean isCompatible(final Class<? extends IoHandle> handleType) {
        return LocalIoHandle.class.isAssignableFrom(handleType);
    }
    
    private final class LocalIoRegistration implements IoRegistration
    {
        private final AtomicBoolean canceled;
        private final ThreadAwareExecutor executor;
        private final LocalIoHandle handle;
        
        LocalIoRegistration(final ThreadAwareExecutor executor, final LocalIoHandle handle) {
            this.canceled = new AtomicBoolean();
            this.executor = executor;
            this.handle = handle;
        }
        
        @Override
        public <T> T attachment() {
            return null;
        }
        
        @Override
        public long submit(final IoOps ops) {
            throw new UnsupportedOperationException();
        }
        
        @Override
        public boolean isValid() {
            return !this.canceled.get();
        }
        
        @Override
        public boolean cancel() {
            if (!this.canceled.compareAndSet(false, true)) {
                return false;
            }
            if (this.executor.isExecutorThread(Thread.currentThread())) {
                this.cancel0();
            }
            else {
                this.executor.execute(this::cancel0);
            }
            return true;
        }
        
        private void cancel0() {
            if (LocalIoHandler.this.registeredChannels.remove(this.handle)) {
                this.handle.unregistered();
            }
        }
    }
}
