package talk;

import java.io.*;
import java.net.*;

/**
   CtlResponse is a Java representation of the CTL_RESPONSE C struct
   defined in <protocols/talkd.h>.  It has all the same fields as the
   C struct, and has methods for writing a CTL_RESPONSE representation
   of itself to a stream and reading a CTL_RESPONSE representation of
   itself from a stream.

   For internal use by TalkTransact and TalkServer.
*/
   
public class CtlResponse {
  // response values
  public static final byte SUCCESS = 0;  // operation completed 
  public static final byte NOT_HERE = 1; // callee not logged in
  public static final byte FAILED = 2;	 // unexplained failure
  public static final byte MACHINE_UNKNOWN = 3;
  public static final byte PERMISSION_DENIED = 4;
  public static final byte UNKNOWN_REQUEST = 5;
  public static final byte BADVERSION = 6; // incompatible talk versions
  public static final byte BADADDR = 7;	 // request has invalid addr
  public static final byte BADCTLADDR = 8; // request has invalid ctl_addr
  
  static final int length = 24;
  byte version = -1;	// protocol version
  byte type = 0;		// request type
  byte answer = 0;	// answer code;
  int idNum = -1;	// message id
  SockaddrIn addr;	// return address, for LOOKUP message

  public CtlResponse() throws IOException {
    addr = new SockaddrIn();
  }

  /**
     Initializes this from the raw data in <code>data</code>. */
  public void readData(byte[] data) {
    DataInputStream is =
      new DataInputStream(new ByteArrayInputStream(data));
    try {
      version = is.readByte();
      type = is.readByte();
      answer = is.readByte();
      is.skipBytes(1);
      idNum = is.readInt();
      addr.readData(is);
    } catch (IOException e) {
      throw new TalkdException(e.toString());
    }
  }

  /**
     Returns a byte array containing this in raw form, in the same
     format as the CTL_MSG struct in C. */
  public byte[] writeData() {
    ByteArrayOutputStream buf = new ByteArrayOutputStream(length);    
    DataOutputStream os = new DataOutputStream(buf);
    try {
      os.writeByte(version);
      os.writeByte(type);
      os.writeByte(answer);
      os.writeByte(0);		// pad
      os.writeInt(idNum);
      addr.writeData(os);
    } catch (IOException e) {
      throw new TalkdException(e.toString());
    }
    return buf.toByteArray();
  }

  public String toString() {
    return "CtlResponse@"+Integer.toHexString(hashCode())+":\n"+
      "version = "+version+"\n"+
      "type = "+type+"\n"+
      "answer = "+answer+"\n"+
      "idNum =  "+idNum+"\n"+
      "addr = "+addr;
  }

  public static void testIO() {
    try {
      CtlResponse rsp = new CtlResponse();
      rsp.version = 1;
      rsp.type = 2;
      rsp.answer = 3;
      rsp.idNum = rsp.hashCode();
      rsp.addr = new SockaddrIn(55, InetAddress.getLocalHost());

      System.out.println("rsp = " + rsp);
      System.out.println("saving rsp to ctl_response.dat");
      byte[] obits = rsp.writeData();
      FileOutputStream outf = new FileOutputStream("ctl_response.dat");
      outf.write(obits);
      outf.close();
      
      System.out.println("reading ctl_response.dat");
      FileInputStream inf = new FileInputStream("ctl_response.dat");
      byte[] ibits = new byte[CtlResponse.length];
      inf.read(ibits);
      rsp.readData(ibits);
      System.out.println("rsp2 = " + rsp);
      ibits = rsp.writeData();

      boolean same = true;
      for (int i = 0; i < ibits.length; i++) {
	if (ibits[i] != obits[i]) {
	  System.out.println("bit " + i + " differs");
	  same = false;
	}
      }
      if (same)
	System.out.println("Test passed");
    } catch (Exception e) {
      System.out.println("Caught exception: "+e);
    }
  }
}
