package de.raysha.jsimpleshell.remote;

import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.nio.ByteBuffer;

import de.raysha.jsimpleshell.remote.model.Message;
import de.raysha.jsimpleshell.remote.serialize.MessageSerializer;

/**
 * This class is an abstraction level for make it easy to communicate between server and client over {@link Socket}s.
 *
 * @author rainu
 */
public class Connector {
	protected static final int BUFFER_SIZE = 8096;

	private final Socket socket;
	private final MessageSerializer serializer = new MessageSerializer();

	public Connector(Socket socket) {
		this.socket = socket;
	}

	/**
	 * Send a {@link Message} through my {@link Socket}.
	 *
	 * @param message The message to be send.
	 * @throws IOException If an error occurs while sending the message.
	 */
	public void send(Message message) throws IOException{
		final String toSend = serializer.serialize(message);

		byte[] length = ByteBuffer.allocate(4).putInt(toSend.length()).array();

		socket.getOutputStream().write(length);
		socket.getOutputStream().write(toSend.getBytes());
		socket.getOutputStream().flush();
	}

	/**
	 * Receive a message from my {@link Socket}. This is a blocking call! That
	 * means that this method blocks until a message was received or a {@link IOException} was
	 * thrown.
	 *
	 * @return The received {@link Message}.
	 * @throws IOException If an error occurs while receiving a message.
	 */
	public Message receive() throws IOException{
		byte[] buffer = new byte[BUFFER_SIZE];
		StringBuilder builder = new StringBuilder();

		InputStream in = socket.getInputStream();
		final int length = receiveLength(in);
		int totalRead = 0;

		while(length > totalRead){
			int read = in.read(buffer);
			if(read < 0) {
				break;
			}

			totalRead += read;

			builder.append(new String(buffer, 0, read));
		}

		final Message message = serializer.deserialize(builder.toString());
		return message;
	}

	private int receiveLength(InputStream in) throws IOException {
		byte[] length = new byte[4];

		int read = in.read(length);
		if(read != 4){
			throw new IOException("The protocol was not followed. No length is given!");
		}

		return ByteBuffer.wrap(length).getInt();
	}
}

@@ -882,16 +882,43 @@
 eption%7B%0A
+%09%09synchronized (socket) %7B%0A%09
 %09%09final 
@@ -967,16 +967,17 @@
 ge);%0A%0A%09%09
+%09
 byte%5B%5D l
@@ -1041,16 +1041,17 @@
 ray();%0A%0A
+%09
 %09%09socket
@@ -1082,24 +1082,25 @@
 (length);%0A%09%09
+%09
 socket.getOu
@@ -1138,16 +1138,17 @@
 tes());%0A
+%09
 %09%09socket
@@ -1175,16 +1175,20 @@
 lush();%0A
+%09%09%7D%0A
 %09%7D%0A%0A%09/**
@@ -1535,16 +1535,43 @@
 eption%7B%0A
+%09%09synchronized (socket) %7B%0A%09
 %09%09byte%5B%5D
@@ -1603,16 +1603,17 @@
 _SIZE%5D;%0A
+%09
 %09%09String
@@ -1652,16 +1652,17 @@
 der();%0A%0A
+%09
 %09%09InputS
@@ -1699,16 +1699,17 @@
 am();%0A%09%09
+%09
 final in
@@ -1740,16 +1740,17 @@
 (in);%0A%09%09
+%09
 int tota
@@ -1761,16 +1761,17 @@
 d = 0;%0A%0A
+%09
 %09%09while(
@@ -1790,24 +1790,25 @@
 alRead)%7B%0A%09%09%09
+%09
 int read = i
@@ -1823,16 +1823,17 @@
 uffer);%0A
+%09
 %09%09%09if(re
@@ -1846,16 +1846,17 @@
 ) %7B%0A%09%09%09%09
+%09
 break;%0A%09
@@ -1857,19 +1857,21 @@
 eak;%0A%09%09%09
+%09
 %7D%0A%0A
+%09
 %09%09%09total
@@ -1885,16 +1885,17 @@
  read;%0A%0A
+%09
 %09%09%09build
@@ -1936,19 +1936,21 @@
 ad));%0A%09%09
+%09
 %7D%0A%0A
+%09
 %09%09final 
@@ -2013,16 +2013,17 @@
 g());%0A%09%09
+%09
 return m
@@ -2030,16 +2030,20 @@
 essage;%0A
+%09%09%7D%0A
 %09%7D%0A%0A%09pri

make the send/receive method synchronsied