//
// Copyright (c) 2007, Brian Frank and Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
//   9 Feb 07  Brian Frank  Creation
//

**
** SocketOptions groups together all the socket options used to tune a
** TcpSocket, TcpListener, or UdpSocket.  See the options method of each
** of those classes for which options apply.  Accessing an unsupported
** option for a particular socket type will throw UnsupportedErr.
**
final class SocketOptions
{

//////////////////////////////////////////////////////////////////////////
// Construction
//////////////////////////////////////////////////////////////////////////

  **
  ** Attach this options instance to the specific socket (we just
  ** use an Obj because everything is dynamically typed)
  **
  internal new make(Obj socket)
  {
    this.socket = socket;
  }

//////////////////////////////////////////////////////////////////////////
// Copy From
//////////////////////////////////////////////////////////////////////////

  **
  ** Set all of this instance's options from the specified options.
  **
  Void copyFrom(SocketOptions options)
  {
    Type.of(this).fields.each |Field f|
    {
      try
      {
        v := f.get(options)

        // Setting traffic class to zero is unreliable across OS
        if (v == 0 && f.name == "trafficClass") return
        
        f.set(this, v)
      }
      catch (UnsupportedErr e)
        {}
    }
  }

//////////////////////////////////////////////////////////////////////////
// Streaming Options
//////////////////////////////////////////////////////////////////////////

  **
  ** The size in bytes for the sys::InStream buffer.  A value of 0 or
  ** null disables input stream buffing.  This field may only be set before
  ** the socket is connected otherwise Err is thrown.
  **
  Int? inBufferSize
  {
    get { return (Int?)wrap |->Obj?| { return socket->getInBufferSize } }
    set { newVal := it; wrap |->| { socket->setInBufferSize(newVal) } }
  }

  **
  ** The size in bytes for the sys::OutStream buffer.  A value of 0 or
  ** null disables output stream buffing.  This field may only be set before
  ** the socket is connected otherwise Err is thrown.
  **
  Int? outBufferSize
  {
    get { return (Int?)wrap |->Obj?| { return socket->getOutBufferSize } }
    set { newVal := it; wrap |->| { socket->setOutBufferSize(newVal) } }
  }

//////////////////////////////////////////////////////////////////////////
// Socket Options
//////////////////////////////////////////////////////////////////////////

  **
  ** SO_BROADCAST socket option.
  **
  Bool broadcast
  {
    get { return (Bool)wrap |->Obj| { return socket->getBroadcast } }
    set { newVal := it; wrap |->| { socket->setBroadcast(newVal) } }
  }

  **
  ** SO_KEEPALIVE socket option.
  **
  Bool keepAlive
  {
    get { return (Bool)wrap |->Obj| { return socket->getKeepAlive } }
    set { newVal := it; wrap |->| { socket->setKeepAlive(newVal) } }
  }

  **
  ** SO_RCVBUF option for the size in bytes of the IP stack buffers.
  **
  Int receiveBufferSize
  {
    get { return (Int)wrap |->Obj| { return socket->getReceiveBufferSize } }
    set { newVal := it; wrap |->| { socket->setReceiveBufferSize(newVal) } }
  }

  **
  ** SO_SNDBUF option for the size in bytes of the IP stack buffers.
  **
  Int sendBufferSize
  {
    get { return (Int)wrap |->Obj| { return socket->getSendBufferSize } }
    set { newVal := it; wrap |->| { socket->setSendBufferSize(newVal) } }
  }

  **
  ** SO_REUSEADDR socket option is used to control the time
  ** wait state of a closed socket.
  **
  Bool reuseAddr
  {
    get { return (Bool)wrap |->Obj| { return socket->getReuseAddr } }
    set { newVal := it; wrap |->| { socket->setReuseAddr(newVal) } }
  }

  **
  ** SO_LINGER socket option controls the linger time or set
  ** to null to disable linger.
  **
  Duration? linger
  {
    get { return (Duration?)wrap |->Obj?| { return socket->getLinger} }
    set { newVal := it; wrap |->| { socket->setLinger(newVal) } }
  }

  **
  ** Controls default timeout used by `TcpSocket.connect`.
  ** A null value indicates a system default timeout.
  **
  @Deprecated { msg = "Use SocketConfig.connectTimeout" }
  Duration? connectTimeout

  **
  ** SO_TIMEOUT socket option controls the amount of time this socket
  ** will block on a read call before throwing an IOErr timeout exception.
  ** Null is used to indicate an infinite timeout.
  **
  Duration? receiveTimeout
  {
    get { return (Duration?)wrap |->Obj?| { return socket->getReceiveTimeout } }
    set { newVal := it; wrap |->| { socket->setReceiveTimeout(newVal) } }
  }

  **
  ** TCP_NODELAY socket option specifies that send not be delayed
  ** to merge packets (Nagle's algorthm).
  **
  Bool noDelay
  {
    get { return (Bool)wrap |->Obj| { return socket->getNoDelay } }
    set { newVal := it; wrap |->| { socket->setNoDelay(newVal) } }
  }

  **
  ** The type-of-class byte in the IP packet header.
  **
  ** For IPv4 this value is detailed in RFC 1349 as the following bitset:
  **  - IPTOS_LOWCOST     (0x02)
  **  - IPTOS_RELIABILITY (0x04)
  **  - IPTOS_THROUGHPUT  (0x08)
  **  - IPTOS_LOWDELAY    (0x10)
  **
  ** For IPv6 this is the value placed into the sin6_flowinfo header field.
  **
  Int trafficClass
  {
    get { return (Int)wrap |->Obj| { return socket->getTrafficClass } }
    set { newVal := it; wrap |->| { socket->setTrafficClass(newVal) } }
  }

//////////////////////////////////////////////////////////////////////////
// Wrap
//////////////////////////////////////////////////////////////////////////

  internal Obj? wrap(|->Obj?| m)
  {
    try
    {
      return m()
    }
    catch (UnknownSlotErr e)
    {
      throw UnsupportedErr("Option not supported for ${Type.of(socket)}")
    }
  }

//////////////////////////////////////////////////////////////////////////
// Fields
//////////////////////////////////////////////////////////////////////////

  private Obj socket

}