package com.neofect.communicator.tcp;

import android.util.Log;

import com.neofect.communicator.Connection;
import com.neofect.communicator.ConnectionType;
import com.neofect.communicator.Controller;
import com.neofect.communicator.Device;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class TcpConnection extends Connection {

	private static final String LOG_TAG = "TcpConnection";

	private static final int BUFFER_SIZE = 1024;

	private String ip;
	private int port;
	private Socket socket;
	private OutputStream outputStream;
	private InputStream inputStream;
	private byte[] buffer = new byte[BUFFER_SIZE];

	public TcpConnection(String ip, int port, Controller<? extends Device> controller) {
		super(ConnectionType.TCP, controller);
		this.ip = ip;
		this.port = port;
	}

	@Override
	protected void connect() {
		handleConnecting();
		new Thread(() -> {
			try {
				socket = new Socket(ip, port);
				outputStream = socket.getOutputStream();
				inputStream = socket.getInputStream();
			} catch (IOException e) {
				clear();
				handleFailedToConnect(new RuntimeException("Failed to connect to '" + getDeviceIdentifier() + "'", e));
				return;
			}
			startReadThread();
			handleConnected();
		}).start();
	}

	@Override
	public void disconnect() {
		clear();
		handleDisconnected();
	}

	@Override
	public void write(byte[] data) {
		if (outputStream == null) {
			Log.e(LOG_TAG, "write: Not connected!");
			return;
		}
		try {
			outputStream.write(data);
		} catch (IOException e) {
			Log.e(LOG_TAG, "write: ", e);
		}
	}

	private void startReadThread() {
		new Thread(() -> {
			while (true) {
				try {
					int numberOfReadBytes = inputStream.read(buffer, 0, buffer.length);
					byte[] readData = new byte[numberOfReadBytes];
					System.arraycopy(buffer, 0, readData, 0, numberOfReadBytes);
					handleReadData(readData);
				} catch (IOException e) {
					Log.d(LOG_TAG, "run: IOException on read() '" + getDeviceIdentifier() + "'", e);
					clear();
					handleDisconnected();
					break;
				}
			}
		}).start();
	}

	private void clear() {
		if (socket != null) {
			try {
				socket.close();
			} catch (IOException e1) {
				Log.e(LOG_TAG, "clear: ", e1);
			}
			socket = null;
		}
		if (outputStream != null) {
			try {
				outputStream.close();
			} catch (IOException e1) {
				Log.e(LOG_TAG, "clear: ", e1);
			}
			outputStream = null;
		}
		if (inputStream != null) {
			try {
				inputStream.close();
			} catch (IOException e1) {
				Log.e(LOG_TAG, "clear: ", e1);
			}
			inputStream = null;
		}
	}

	@Override
	public String getDeviceIdentifier() {
		return ip + ":" + port;
	}

	@Override
	public String getDeviceName() {
		return getDeviceIdentifier();
	}

	@Override
	public String getDescription() {
		return getDeviceIdentifier();
	}

}

@@ -343,16 +343,93 @@
 .Socket;
+%0Aimport java.util.concurrent.Executor;%0Aimport java.util.concurrent.Executors;
 %0A%0Apublic
@@ -575,16 +575,82 @@
  1024;%0A%0A
+%09private Executor executor = Executors.newSingleThreadExecutor();%0A
 %09private
@@ -1552,24 +1552,52 @@
 e%5B%5D data) %7B%0A
+%09%09executor.execute(() -%3E %7B%0A%09
 %09%09if (output
@@ -1610,24 +1610,25 @@
  == null) %7B%0A
+%09
 %09%09%09Log.e(LOG
@@ -1662,16 +1662,17 @@
 !%22);%0A%09%09%09
+%09
 return;%0A
@@ -1673,18 +1673,20 @@
 turn;%0A%09%09
+%09
 %7D%0A
+%09
 %09%09try %7B%0A
@@ -1684,24 +1684,25 @@
 %09%09%09try %7B%0A%09%09%09
+%09
 outputStream
@@ -1715,16 +1715,17 @@
 (data);%0A
+%09
 %09%09%7D catc
@@ -1744,16 +1744,17 @@
 on e) %7B%0A
+%09
 %09%09%09Log.e
@@ -1776,25 +1776,32 @@
 e: %22, e);%0A%09%09
-%7D
+%09%7D%0A%09%09%7D);
 %0A%09%7D%0A%0A%09privat
@@ -1949,16 +1949,95 @@
 ength);%0A
+%09%09%09%09%09if (numberOfReadBytes %3C 0) %7B%0A%09%09%09%09%09%09onDisconnected();%0A%09%09%09%09%09%09return;%0A%09%09%09%09%09%7D%0A
 %09%09%09%09%09byt
@@ -2302,28 +2302,10 @@
 %09%09%09%09
-clear();%0A%09%09%09%09%09handle
+on
 Disc
@@ -2325,13 +2325,14 @@
 %09%09%09%09
-b
 re
-ak
+turn
 ;%0A%09%09
@@ -2354,24 +2354,96 @@
 tart();%0A%09%7D%0A%0A
+%09private void onDisconnected() %7B%0A%09%09clear();%0A%09%09handleDisconnected();%0A%09%7D%0A%0A
 %09private voi

Fix some errors in TcpConnection