// Path: client/src/main/java/lt/emasina/esj/model/RequestOperation.java
// public abstract class RequestOperation<F extends Message> extends ResponseOperation {
// 
//     private final TcpConnection connection;
//     protected F request;
// 
//     public RequestOperation(TcpConnection connection, F request) {
//         this.connection = connection;
//         this.request = request;
//     }
// 
//     //private final Semaphore processing = new Semaphore(0);
// 
//     public TcpPackage getRequestPackage() {
//         return request.getTcpPackage(connection.getSettings());
//     }
// 
//     public UUID getCorrelationId() {
//         return request.getCorrelationId();
//     }
// 
//     public void doneProcessing() {
//         //processing.release();
//     }
// 
//     public void send() {
//         sendAsync();
//         /*try {
//             processing.acquire();
//         } catch (InterruptedException ex) {
//             log.warn("Processing was interrpupted", ex);
//         }*/
//     }
// 
//     public void sendAsync() {
//         connection.send(this);
//     }
// 
//     /**
//      * @return the connection
//      */
//     public TcpConnection getConnection() {
//         return connection;
//     }
// 
//     /**
//      * @return the request
//      */
//     public F getRequest() {
//         return request;
//     }
// 
// }
// 
// Path: client/src/main/java/lt/emasina/esj/model/ResponseOperation.java
// public abstract class ResponseOperation {
// 
//     public boolean hasSingleResponse = true;
// 
//     public abstract void setResponsePackage(TcpPackage pckg);
// 
// }
// 
// Path: client/src/main/java/lt/emasina/esj/operation/HeartBeatResponseOperation.java
// public class HeartBeatResponseOperation extends RequestOperation<HeartBeatResponse> {
// 
//     public HeartBeatResponseOperation(TcpConnection connection, UUID correlationId) {
//         super(connection, new HeartBeatResponse());
//         this.getRequest().setCorrelationId(correlationId);
//     }
// 
//     @Override
//     public void setResponsePackage(TcpPackage pckg) {
// 
//     }
// 
// }


import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.Semaphore;
import lt.emasina.esj.model.RequestOperation;
import lt.emasina.esj.model.ResponseOperation;
import lt.emasina.esj.operation.HeartBeatResponseOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
package lt.emasina.esj.tcp;




/**
 * TcpProcessor
 *
 * @author Stasys
 */
public class TcpSocketManager implements Runnable {

    private static final Logger log = LoggerFactory.getLogger(TcpSocketManager.class);
    
    private final TcpConnection connection;
    private final InetAddress host;
    private final int port;
    private Socket socket;
    
    public final ExecutorService executor;
    private Future<?> senderTask;
    private Future<?> receiverTask;

    private final Semaphore running = new Semaphore(0);

    private final BlockingQueue<RequestOperation> sending = new LinkedBlockingDeque<>();

    private final Map<UUID, ResponseOperation> receiving = new ConcurrentHashMap<>();

    /**
     * Constructor with mandatory fields.
     * 
     * @param connection
     * @param host
     * @param port
     * @param executor
     */
// Path: client/src/main/java/lt/emasina/esj/model/RequestOperation.java
// public abstract class RequestOperation<F extends Message> extends ResponseOperation {
// 
//     private final TcpConnection connection;
//     protected F request;
// 
//     public RequestOperation(TcpConnection connection, F request) {
//         this.connection = connection;
//         this.request = request;
//     }
// 
//     //private final Semaphore processing = new Semaphore(0);
// 
//     public TcpPackage getRequestPackage() {
//         return request.getTcpPackage(connection.getSettings());
//     }
// 
//     public UUID getCorrelationId() {
//         return request.getCorrelationId();
//     }
// 
//     public void doneProcessing() {
//         //processing.release();
//     }
// 
//     public void send() {
//         sendAsync();
//         /*try {
//             processing.acquire();
//         } catch (InterruptedException ex) {
//             log.warn("Processing was interrpupted", ex);
//         }*/
//     }
// 
//     public void sendAsync() {
//         connection.send(this);
//     }
// 
//     /**
//      * @return the connection
//      */
//     public TcpConnection getConnection() {
//         return connection;
//     }
// 
//     /**
//      * @return the request
//      */
//     public F getRequest() {
//         return request;
//     }
// 
// }
// 
// Path: client/src/main/java/lt/emasina/esj/model/ResponseOperation.java
// public abstract class ResponseOperation {
// 
//     public boolean hasSingleResponse = true;
// 
//     public abstract void setResponsePackage(TcpPackage pckg);
// 
// }
// 
// Path: client/src/main/java/lt/emasina/esj/operation/HeartBeatResponseOperation.java
// public class HeartBeatResponseOperation extends RequestOperation<HeartBeatResponse> {
// 
//     public HeartBeatResponseOperation(TcpConnection connection, UUID correlationId) {
//         super(connection, new HeartBeatResponse());
//         this.getRequest().setCorrelationId(correlationId);
//     }
// 
//     @Override
//     public void setResponsePackage(TcpPackage pckg) {
// 
//     }
// 
// }


// Path: client/src/main/java/lt/emasina/esj/tcp/TcpSocketManager.java
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.Semaphore;
import lt.emasina.esj.model.RequestOperation;
import lt.emasina.esj.model.ResponseOperation;
import lt.emasina.esj.operation.HeartBeatResponseOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

package lt.emasina.esj.tcp;




/**
 * TcpProcessor
 *
 * @author Stasys
 */
public class TcpSocketManager implements Runnable {

    private static final Logger log = LoggerFactory.getLogger(TcpSocketManager.class);
    
    private final TcpConnection connection;
    private final InetAddress host;
    private final int port;
    private Socket socket;
    
    public final ExecutorService executor;
    private Future<?> senderTask;
    private Future<?> receiverTask;

    private final Semaphore running = new Semaphore(0);

    private final BlockingQueue<RequestOperation> sending = new LinkedBlockingDeque<>();

    private final Map<UUID, ResponseOperation> receiving = new ConcurrentHashMap<>();

    /**
     * Constructor with mandatory fields.
     * 
     * @param connection
     * @param host
     * @param port
     * @param executor
     */
    public TcpSocketManager(TcpConnection connection, InetAddress host, int port, ExecutorService executor) {