package org.mcphoton.impl.network;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;

/**
 * The thread that manages all the network output. It writes outcoming packets.
 *
 * @author TheElectronWill
 *
 */
public final class NetworkOutputThread extends Thread {

	// TODO measurements to determine the best queue's capacity
	private final BlockingQueue<PacketSending> queue = new ArrayBlockingQueue<>(200, false);
	private final PacketSending POISON = new PacketSending(null, null);
	private final Selector selector;
	private final Object guard;
	private volatile boolean run = true;

	/**
	 * Creates a new <code>NetworkOutputThread</code> that registers channels to the specified
	 * <code>Selector</code> in case of incomplete writing.
	 *
	 * @param selector the selector to use in case of incomplete writing
	 * @param the guard for wakeup (normally it's the NetworkInputThread instance).
	 */
	public NetworkOutputThread(Selector selector, Object guard) {
		super("network-output");
		this.selector = selector;
		this.guard = guard;
	}

	/**
	 * Stops this Thread nicely, as soon as possible but without any forcing.
	 */
	public void stopNicely() {
		run = false;
		queue.offer(POISON);
	}

	@Override
	public void run() {
		while (run) {
			try {
				PacketSending sending = queue.take();
				if (sending == POISON) {
					return;
				}
				for (PhotonClient client : sending.clients) {
					try {
						BytesProtocolOutputStream out = new BytesProtocolOutputStream();
						sending.packet.writeTo(out);
						ByteBuffer data = out.asBuffer();
						ByteBuffer encodedData = client.encodeWithCodecs(data);
						boolean completeAndImmediateWrite = client.getMessageWriter().writeASAP(encodedData);
						if (!completeAndImmediateWrite) {
							synchronized (guard) {
								selector.wakeup();
								client.getChannel().register(selector, SelectionKey.OP_WRITE, client);
							}
						}
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * Adds a <code>PacketSending</code> to the sending queue, waiting if necessary for space to become
	 * available.
	 */
	public void enqueue(PacketSending sending) {
		try {
			queue.put(sending);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Adds a <code>PacketSending</code> to the sending queue, waiting up to the specified wait time if
	 * necessary for space to become available.
	 */
	public void enqueue(PacketSending sending, long timeout, TimeUnit unit) {
		try {
			queue.offer(sending, timeout, unit);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

}

@@ -61,36 +61,8 @@
 on;%0A
-import java.nio.ByteBuffer;%0A
 impo
@@ -131,16 +131,46 @@
 lector;%0A
+import java.util.Collections;%0A
 import j
@@ -255,46 +255,8 @@
 eue;
-%0Aimport java.util.concurrent.TimeUnit;
 %0A%0A/*
@@ -435,69 +435,8 @@
  %7B%0A%0A
-%09// TODO measurements to determine the best queue's capacity%0A
 %09pri
@@ -512,18 +512,11 @@
 e%3C%3E(
-2
+5
 00
-, false
 );%0A%09
@@ -576,20 +576,39 @@
 g(null, 
-null
+Collections.emptyList()
 );%0A%09priv
@@ -658,21 +658,28 @@
  Object 
-guard
+selectorLock
 ;%0A%09priva
@@ -954,28 +954,84 @@
 ram 
-the guard for wakeup
+selectorLock the objet to synchronize on when a selector's wakeup is needed.
  (no
@@ -1045,16 +1045,20 @@
 it's the
+%0A%09 *
  Network
@@ -1139,21 +1139,28 @@
  Object 
-guard
+selectorLock
 ) %7B%0A%09%09su
@@ -1221,21 +1221,35 @@
 his.
-guard = guard
+selectorLock = selectorLock
 ;%0A%09%7D
@@ -1548,24 +1548,25 @@
 turn;%0A%09%09%09%09%7D%0A
+%0A
 %09%09%09%09for (Pho
@@ -1618,216 +1618,8 @@
 y %7B%0A
-%09%09%09%09%09%09BytesProtocolOutputStream out = new BytesProtocolOutputStream();%0A%09%09%09%09%09%09sending.packet.writeTo(out);%0A%09%09%09%09%09%09ByteBuffer data = out.asBuffer();%0A%09%09%09%09%09%09ByteBuffer encodedData = client.encodeWithCodecs(data);%0A
 %09%09%09%09
@@ -1667,12 +1667,9 @@
 ent.
-getM
+m
 essa
@@ -1676,18 +1676,16 @@
 geWriter
-()
 .writeAS
@@ -1691,19 +1691,22 @@
 SAP(
+s
 en
-codedData
+ding.packet
 );%0A%09
@@ -1769,13 +1769,20 @@
 ed (
-guard
+selectorLock
 ) %7B%0A
@@ -1827,20 +1827,15 @@
 ent.
-getC
+c
 hannel
-()
 .reg
@@ -2050,134 +2050,8 @@
 %09%7D%0A%0A
-%09/**%0A%09 * Adds a %3Ccode%3EPacketSending%3C/code%3E to the sending queue, waiting if necessary for space to become%0A%09 * available.%0A%09 */%0A
 %09pub
@@ -2081,23 +2081,18 @@
 Sending 
+p
 s
-ending
 ) %7B%0A%09%09tr
@@ -2112,363 +2112,10 @@
 put(
+p
 s
-ending);%0A%09%09%7D catch (InterruptedException e) %7B%0A%09%09%09e.printStackTrace();%0A%09%09%7D%0A%09%7D%0A%0A%09/**%0A%09 * Adds a %3Ccode%3EPacketSending%3C/code%3E to the sending queue, waiting up to the specified wait time if%0A%09 * necessary for space to become available.%0A%09 */%0A%09public void enqueue(PacketSending sending, long timeout, TimeUnit unit) %7B%0A%09%09try %7B%0A%09%09%09queue.offer(sending, timeout, unit
 );%0A%09
@@ -2150,16 +2150,17 @@
 on e
+x
 ) %7B%0A%09%09%09e
 .pri
@@ -2151,24 +2151,25 @@
 n ex) %7B%0A%09%09%09e
+x
 .printStackT

Improve the NetworkOutputThread