package protocolsupport.protocol.pipeline.common;

import java.util.concurrent.TimeUnit;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.ReadTimeoutException;
import io.netty.util.concurrent.ScheduledFuture;
import protocolsupport.utils.Utils;

public class SimpleReadTimeoutHandler extends ChannelInboundHandlerAdapter {

	private ScheduledFuture<?> timeoutTask;
	private final long timeoutTime;
	private long lastReadTime;

	public SimpleReadTimeoutHandler(int timeout) {
		this(timeout, TimeUnit.SECONDS);
	}

	public SimpleReadTimeoutHandler(long timeout, TimeUnit tu) {
		this.timeoutTime = tu.toMillis(timeout);
	}

	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		this.initialize(ctx);
		super.channelActive(ctx);
	}

	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception {
		if (this.timeoutTask != null) {
			this.timeoutTask.cancel(false);
			this.timeoutTask = null;
		}
		super.channelInactive(ctx);
	}

	@Override
	public void channelRead(ChannelHandlerContext ctx, Object message) {
		setLastRead();
		ctx.fireChannelRead(message);
	}

	public void setLastRead() {
		this.lastReadTime = Utils.currentTimeMillisFromNanoTime();
	}

	private void initialize(final ChannelHandlerContext ctx) {
		this.lastReadTime = System.nanoTime();
		this.timeoutTask = ctx.executor().schedule(new Runnable() {
			@Override
			public void run() {
				if (ctx.channel().isOpen()) {
					long untilTimeout = timeoutTime - (Utils.currentTimeMillisFromNanoTime() - lastReadTime);
					if (untilTimeout <= 0) {
						ctx.fireExceptionCaught(ReadTimeoutException.INSTANCE);
					} else {
						ctx.executor().schedule(this, untilTimeout, TimeUnit.MILLISECONDS);
					}
				}
			}
		}, this.timeoutTime, TimeUnit.MILLISECONDS);
	}

}