/**
* @license
* Copyright 2020 The FOAM Authors. All Rights Reserved.
* http://www.apache.org/licenses/LICENSE-2.0
*/
package foam.nanos.mrac;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;

import foam.box.Box;
import foam.box.Message;
import foam.core.AbstractFObject;
import foam.lib.NetworkPropertyPredicate;
import foam.lib.json.Outputter;

// Each Request generate its own box. So this class should be thread-safe in theory.
//TODO: need common close method for all box using SocketChannel?
public class TcpSocketChannelReturnBox extends AbstractFObject implements Box {
  
  SelectionKey key;
  SocketChannel socketChannel;
  protected int sizeBytes = 4;
  
  public void send(Message msg) {
    System.out.println("sendback");
    doSend(msg);
    // After sending back response, the SocketChannel should re-register READ in selector.
    // So that this SocketChannel can be re-use by client.
    resumeSelection();
  }

  public void doSend(Message msg) {
    String out = new Outputter(getX()).setPropertyPredicate(new NetworkPropertyPredicate()).stringify(msg);
    byte[] bytes = out.getBytes(Charset.forName("UTF-8"));
    send(bytes);
  }
  
  //Do not filp ByteBuffer before pass into this method.
  public void send(ByteBuffer byteBuffer) {
    try {
      getSocketChannel().write(byteBuffer);
    } catch ( IOException e ) {
      // If there is an IOException happen while writing into socket,
      // Which means that socket has some connection issues.
      // Force socket close, and client should be able to handle this error.
      handleFailure();
    }
  }

  public void send(byte[] bytes) {
    ByteBuffer byteBuffer = ByteBuffer.allocate(sizeBytes + bytes.length);
    byteBuffer.putInt(bytes.length);
    byteBuffer.put(bytes);
    byteBuffer.flip();
    send(byteBuffer);
    byteBuffer.clear();
  }
  
  private void resumeSelection() {
    if ( getSelectionKey().isValid() ) {
      getSelectionKey().interestOps(SelectionKey.OP_READ);
    }
  }
  
  public void setKey(SelectionKey key) {
    this.key = key;
  }
  
  public SelectionKey getSelectionKey() {
    if ( key == null ) {
      key = (SelectionKey) getX().get("selectionKey");
    }
    //TODO: handle key == null exception.
    return key;
  }
  
  public void setSocketChannel(SocketChannel socketChannel) {
    this.socketChannel = socketChannel;
  }
  
  public SocketChannel getSocketChannel() {  
    if ( socketChannel == null ) {
      socketChannel = (SocketChannel) getX().get("socketChannel");
    }
    return socketChannel;
  }
  
  public void handleFailure() {
    TCPNioServer.removeSelectionKey(getSelectionKey());
    TCPNioServer.hardCloseSocketChannel(getSocketChannel());
  }
  
}
@@ -613,16 +613,81 @@
 hannel?%0A
+// Do not catch IOException in this class. Let caller handle it.%0A
 public c
@@ -876,24 +876,36 @@
 sage msg) %7B%0A
+    try %7B%0A  
     System.o
@@ -932,16 +932,18 @@
 %22);%0A    
+  
 doSend(m
@@ -951,16 +951,18 @@
 g);%0A    
+  
 // After
@@ -1044,16 +1044,18 @@
 or.%0A    
+  
 // So th
@@ -1105,16 +1105,18 @@
 nt.%0A    
+  
 resumeSe
@@ -1122,24 +1122,130 @@
 election();%0A
+    %7D catch ( IOException e ) %7B%0A      //Re-throw IOException.%0A      throw new RuntimeException(e);%0A    %7D%0A%0A
   %7D%0A%0A  publi
@@ -1266,24 +1266,43 @@
 Message msg)
+ throws IOException
  %7B%0A    Strin
@@ -1586,77 +1586,14 @@
 er) 
-%7B%0A    try %7B%0A      getSocketChannel().write(byteBuffer);%0A    %7D catch (
+throws
  IOE
@@ -1604,251 +1604,52 @@
 tion
- e )
  %7B%0A    
-  // If there is an IOException happen while writing into socket,%0A      // Which means that s
+getS
 ocket
- has some connection issues.%0A      // Force socket close, and client should be able to handle this error.%0A      handleFailure();%0A    %7D
+Channel().write(byteBuffer);
 %0A  %7D
@@ -1682,16 +1682,35 @@
 %5D bytes)
+ throws IOException
  %7B%0A    B
@@ -2628,161 +2628,5 @@
 %0A  %0A
-  public void handleFailure() %7B%0A    TCPNioServer.removeSelectionKey(getSelectionKey());%0A    TCPNioServer.hardCloseSocketChannel(getSocketChannel());%0A  %7D%0A  %0A
 %7D

remove exception capture