package talk;

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

/**
   CtlMsg is a java representation of the CTL_MSG C structure used by
   the talk protocol for sending messages to a talk server.  It has
   all the same member variables, and has writeData and readData
   member functions to write and read a binary representation of
   itself to a DataStream. size == 84 bytes.

   For internal use by TalkTransact and TalkServer.  */

public class CtlMsg {
  // request types
  static final byte LEAVE_INVITE = 0;// leave invitation with server
  static final byte LOOK_UP = 1; // check for invitation by callee
  static final byte DELETE = 2; // delete invitation by caller
  static final byte ANNOUNCE = 3; // announce invitation by caller


  
  static final int length = 84;
  byte version = TalkInfo.TALK_VERSION; // talk protocol version
  byte type = 0;		// request type
  int idNum = 0;		// message id
  SockaddrIn addr;	// address for client to connect to
  SockaddrIn ctlAddr;	// address for talkd to reply to
  int pid = 0;		// caller's process id
  
  static final int NAME_SIZE = 12;
  String localName;	// caller's name
  String remoteName;	// callee's name
  
  static final int TTY_SIZE = 16;
  String remoteTTY;	// callee's tty

  public CtlMsg() {
    addr = new SockaddrIn();
    ctlAddr = new SockaddrIn();
    localName = remoteName = remoteTTY = "";
  }
  
  public void readData(byte[] data) {
    DataInputStream is =
      new DataInputStream(new ByteArrayInputStream(data));
    try {
      version = is.readByte();
      type = is.readByte();
      is.skipBytes(2);
      idNum = is.readInt();

      addr.readData(is);
      ctlAddr.readData(is);

      pid = is.readInt();

      byte[] name = new byte[NAME_SIZE];
      int ind;
    
      is.readFully(name);
      localName = new String(name);
      ind = localName.indexOf(0);
      if (ind >= 0)
	localName = localName.substring(0, ind);
    
      is.readFully(name);
      remoteName = new String(name);
      ind = remoteName.indexOf(0);
      if (ind >= 0)
	remoteName = remoteName.substring(0, ind);
    
      byte[] tty = new byte[TTY_SIZE];
      is.readFully(tty);
      remoteTTY = new String(tty);
      ind = remoteTTY.indexOf(0);
      if (ind >= 0)
	remoteTTY = remoteTTY.substring(0, ind);
    } catch (IOException e) {
      throw new TalkdException(e.toString());
    }
  }
    

  public byte[] writeData() {
    ByteArrayOutputStream buf = new ByteArrayOutputStream(length);
    DataOutputStream os = new DataOutputStream(buf);
    try {
      os.writeByte(version);
      os.writeByte(type);
      os.writeShort(0);		// padding
      os.writeInt(idNum);
    
      addr.writeData(os);
      ctlAddr.writeData(os);
    
      os.writeInt(pid);

      if (localName.length() > (NAME_SIZE - 1))
	localName = localName.substring(0, NAME_SIZE - 1);
      os.writeBytes(localName);
      for (int i = 0; i < NAME_SIZE - localName.length(); i++) // padding
	os.writeByte(0);

      if (remoteName.length() > (NAME_SIZE - 1))
	remoteName = remoteName.substring(0, NAME_SIZE - 1);
      os.writeBytes(remoteName);
      for (int i = 0; i < NAME_SIZE - remoteName.length(); i++) // padding
	os.writeByte(0);
    
      if (remoteTTY.length() > (TTY_SIZE - 1))
	remoteTTY = remoteTTY.substring(0, TTY_SIZE - 1);
      os.writeBytes(remoteTTY);
      for (int i = 0; i < TTY_SIZE - remoteTTY.length(); i++) // padding
	os.writeByte(0);
    } catch (IOException e) {
      throw new TalkdException(e.toString());
    }
    
    return buf.toByteArray();
  }

  public String toString() {
    return "CtlMsg@"+Integer.toHexString(hashCode())+":\n"+
      "version = "+version+"\n"+
      "type = "+type+"\n"+
      "idNum = "+idNum+"\n"+
      "addr = "+addr+"\n"+
      "ctlAddr = "+ctlAddr+"\n"+
      "pid = "+pid+"\n"+
      "localName = '"+localName+"'\n"+
      "remoteName = '"+remoteName+"'\n"+
      "remoteTTY = "+remoteTTY;
  }

  public static void testIO() {
    try {
      CtlMsg msg = new CtlMsg();
      msg.type = 1;
      msg.idNum = 3;
      msg.addr = new SockaddrIn(2000, InetAddress.getByName("kalgoorlie"));
      msg.ctlAddr = new SockaddrIn(145, InetAddress.getByName("wagga"));
      msg.pid = msg.hashCode();
      msg.localName = "ajenkins";
      msg.remoteName = "dhirvone";
      msg.remoteTTY = "ttyp1";

      System.out.println("msg = "+msg);
      System.out.println("saving msg to ctl_msg.dat");
      byte[] obits = msg.writeData();
      FileOutputStream outf = new FileOutputStream("ctl_msg.dat");
      outf.write(obits);
      outf.close();

      System.out.println("reading ctl_msg.dat");
      byte[] ibits = new byte[CtlMsg.length];
      FileInputStream inf = new FileInputStream("ctl_msg.dat");
      inf.read(ibits);
      inf.close();
      CtlMsg imsg = new CtlMsg();
      imsg.readData(ibits);
      System.out.println("imsg = " + msg);
      ibits = imsg.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);
    }
  }
}
