package nxm.sys.lib;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/**
  Resource for interacting with a UDP Stream.

  Data Block has 16 byte header with the following structure:
  short - Magic number
  short - bytes
  long  - byte offset

  @author Jeff Schoen
*/
public class UdpResource extends IOResource {

  private DatagramSocket dsock;
  private byte[] buffi,buffo,hb;
  private DatagramPacket dpacki,dpacko;
  private String rhost;
  private int port,rport;
  private InetAddress raddr;
  private int MAXPKTBUF=65536, MINPKTSIZE=128;
  private int woff=0, roff=0;

  /** UdpResource empty constructor */
  public UdpResource() { }

  /** test existence of the resource - assume exist only if output for input. */
  public boolean exists () { return (dir < DIR_INOUT); }

  public void setURL (String str) {
    super.setURL(str);
    // peel off various header formats
    if (str.startsWith("udp://")) str = str.substring(6);
    else if (str.startsWith("udp:/")) str = str.substring(5);
    else if (str.startsWith("udp:")) str = str.substring(4);
    int i = str.indexOf("/");
    if (i<0) {  		// input spec
      port = Convert.s2l(str);
    } else {  			// output spec
      port = Convert.s2l(str.substring(0,i));
      int j = str.indexOf(":");
      if (j<0) { rhost = "localhost"; j=i; }
      else rhost = str.substring(i+1,j);
      rport = Convert.s2l(str.substring(j+1));
    }
  }

  /** open the resource */
  public boolean open () {
    isOpen = false;
    try {
      dsock = new DatagramSocket(port);
      dsock.setSoTimeout(50); // in milliseconds
      if (dir > DIR_INOUT) {  // output only
        // allocate a packet buffer for output
        buffo  = new byte[8+MAXPKTBUF];
        dpacko = new DatagramPacket(buffo,buffo.length);
        raddr = InetAddress.getByName(rhost);
        dpacko.setAddress(raddr);
        dpacko.setPort(rport);
      } else if (dir <= DIR_INOUT) {
        // allocate a packet buffer for input
        buffi  = new byte[8+MAXPKTBUF];
        dpacki = new DatagramPacket(buffi,buffi.length);
      }
      isOpen = true;
    }
    catch (Exception e) { }
    roff = woff = 0;
    return isOpen;
  }

  /** close the resource */
  public boolean close() {
    if (dsock!=null) dsock.close();
    return true;
  }

  /** seek to the specified offset in socket */
  public boolean seek (long offset) {
    if (offset!=this.offset) return false;
    return true;
  }

  /** get currently available length of resource */
  public long avail() {
    return 0L;
  }
  /** read data from resource */
  public int read (byte[] buf, int boff, int bytes) {
    int status=0, off=boff;
    while (bytes>0) {
      try {
        dpacki.setLength(buffi.length);
        dsock.receive(dpacki);
        status = dpacki.getLength();
      }
      catch (IOException e) { status = -1; }
      if (status>0) {
        System.arraycopy(buffi,0, buf,off, status);
        bytes-=status; off+=status;
      }
      else break;
    } bytes = off-boff;
    if (status<0 && bytes==0) return status;
    offset += bytes;
    return bytes;
  }

  /** write data to resource */
  public int write (byte[] buf, int boff, int bytes) {
    System.arraycopy(buf,boff, buffo,woff, bytes);
    woff += bytes;
    if (woff>=MINPKTSIZE) try {
      dpacko.setLength(woff);
      dsock.send(dpacko);
      woff=0;
    }
    catch (IOException e) { return -1; }
    offset += bytes;
    return bytes;
  }

}
