/*
 * Aleph Toolkit
 *
 * Copyright 1998, Brown University, Providence, RI.
 * 
 *                         All Rights Reserved
 * 
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose other than its incorporation into a
 * commercial product is hereby granted without fee, provided that the
 * above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of Brown University not be used in
 * advertising or publicity pertaining to distribution of the software
 * without specific, written prior permission.
 * 
 * BROWN UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ANY
 * PARTICULAR PURPOSE.  IN NO EVENT SHALL BROWN UNIVERSITY BE LIABLE FOR
 * ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package aleph.comm.tcp;

import aleph.Aleph;
import aleph.AlephDebug;
import aleph.Message;
import java.io.IOException;
import java.io.NotSerializableException;
import java.io.EOFException;
import java.io.InterruptedIOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;

/**
 * A TCPConnection encapsulates a stream socket to a remote PE.
 * @see aleph.comm.tcp.Connection
 * @see aleph.comm.tcp.AutoConnection
 *
 * @author Maurice Herlihy
 * @date   February 1999
 **/
public class TCPConnection extends Connection {
  private Socket  socket;        // I/O here
  private Address remoteAddress; // my partner

  private static final boolean DEBUG = false; // Oy, va-voy!

  /**
   * Constructor for incoming connection.
   * @param socket use this socket
   **/
  public TCPConnection (Socket socket) {
    this.socket = socket;
    remoteAddress = new Address(socket.getInetAddress(), socket.getPort());
  }

  /**
   * Constructor for outgoing connection.
   * @param destination partner's Address
   * @param localHost my address
   **/
  public TCPConnection (Address destination) {
    try {
      remoteAddress = destination;
      socket = new Socket(remoteAddress.inetAddress, remoteAddress.port);
    } catch (Exception e) {
      Aleph.panic(e);
    }
  }

  /**
   * Close socket and let thread die naturally.
   **/
  public void close () {
    try {
      super.close();
      socket.close();
      if (DEBUG)
        AlephDebug.println("TCPConnection: closing socket " + socket);
    } catch (IOException e) {
      return;
    } catch (Exception e) {
      Aleph.panic(e);
    }
  }

  /**
   * Flush output stream.
   **/
  public void flush () {
    try {
      if (DEBUG)
        AlephDebug.println("tcp.Connection flushing socket " + socket);
      socket.getOutputStream().flush();
    } catch (IOException e) {
      return;
    } catch (Exception e) {
      Aleph.panic(e);
    }
  }

  /**
   * Ping output stream.
   **/
  public boolean ping()  {
    try {
      socket.getOutputStream().flush();
    } catch (IOException e) {
      return false;
    } catch (Exception e) {
      Aleph.panic("ping " + e);
    }
    return true;
  }

  /**
   * Send a message on this connection.
   * @param message what to send
   **/
  public synchronized void send (Message message) {
    /* Each message needs a new ObjectOutputStream to avoid unwanted sharing
       across messages. */
    try {
      if (DEBUG)
        AlephDebug.println("TCPConnection: sending " + message);
      messagesSent.inc();       // record message sent
      OutputStream outputStream = socket.getOutputStream();
      (new ObjectOutputStream(outputStream)).writeObject(message);
      socket.getOutputStream().flush();
    } catch (NotSerializableException e) {
      Aleph.panic(e);
    } catch (IOException e) {
      return;
    } catch (Exception e) {
      Aleph.panic(e);
    }
  }

  /**
   * Pull in next message.  Blocking method.
   * @returns next message
   **/
  protected Message receive () throws IOException {
    try {
      return (Message) (new ObjectInputStream(socket.getInputStream())).readObject();
    } catch (ClassNotFoundException e) {
      Aleph.panic(e);
      return null;              // not reached
    }
  }

}

