/* **************************************************************************

  $Archive: /njcl_v2/src/com/novell/service/session/xplat/Address.java $
  $Revision: 20 $
  $Modtime: 5/16/00 4:05p $

  Copyright (c) 1997 Novell, Inc.  All Rights Reserved.

  THIS WORK IS  SUBJECT  TO  U.S.  AND  INTERNATIONAL  COPYRIGHT  LAWS  AND
  TREATIES.   NO  PART  OF  THIS  WORK MAY BE  USED,  PRACTICED,  PERFORMED
  COPIED, DISTRIBUTED, REVISED, MODIFIED, TRANSLATED,  ABRIDGED, CONDENSED,
  EXPANDED,  COLLECTED,  COMPILED,  LINKED,  RECAST, TRANSFORMED OR ADAPTED
  WITHOUT THE PRIOR WRITTEN CONSENT OF NOVELL, INC. ANY USE OR EXPLOITATION
  OF THIS WORK WITHOUT AUTHORIZATION COULD SUBJECT THE PERPETRATOR TO
  CRIMINAL AND CIVIL LIABILITY.

****************************************************************************/
package com.novell.service.session.xplat;

import com.novell.service.jncpv2.clx.*;
import com.novell.service.toolkit.jcl.NWLong;
import java.util.StringTokenizer;
import java.net.InetAddress;
import com.novell.service.session.*;
import com.novell.service.session.util.Debug;

/**
 * Provides NetWare address types, constructors for creating an
 * addess, and methods for manipulating addresses.
 */
public class Address implements java.io.Serializable
{
   // Always access debug via final static...hopefully a final static of
   // false will be optimized out by the compiler
   final static private boolean DEBUG = false;
   final static private boolean S_DEBUG = false; // May have side effects

   // NT requester requires ip port first in UDP address
   static private boolean NT_PORT_KLUDGE = true;

   private int type;
   private byte[] address = null;

  /**
   * No NetWare address tupe provided.
   *
   * <p>(TYPE_NONE   = 0x00000000)</p>
   */

   public static final int TYPE_NONE   = 0x00000000;
  /**
   * NetWare address tupe is IPX.
   *
   * <p>(TYPE_NONE   = 0x00000001)</p>
   *
   * Format of address:  nn nn nn nn aa aa aa aa aa aa pp pp
   *    where nn is a network address byte, aa is a node address byte
   *    and pp is a port
   */
   public static final int TYPE_IPX    = 0x00000001;

  /**
   * NetWare address tupe is DDP.
   *
   * <p>(TYPE_IPX   = 0x00000003)</p>
   */
   public static final int TYPE_DDP    = 0x00000003;

  /**
   * NetWare address tupe is ASP.
   *
   * <p>(TYPE_ASP   = 0x00000004)</p>
   */
   public static final int TYPE_ASP    = 0x00000004;

  /**
   * NetWare address tupe is UDP.
   *
   * <p>(TYPE_UDP   = 0x00000008)</p>
   *
   * Format of address: pp pp aa aa aa aa
   *    where pp is a port byte and aa is an address byte.
   */
   public static final int TYPE_UDP    = 0x00000008;

  /**
   * NetWare address tupe is TCP.
   *
   * <p>(TYPE_TCP   = 0x00000009)</p>
   */
   public static final int TYPE_TCP    = 0x00000009;

  /**
   * NetWare address tupe is UDP6.
   *
   * <p>(TYPE_UDP6   = 0x0000000A)</p>
   */
   public static final int TYPE_UDP6   = 0x0000000A;

  /**
   * NetWare address tupe is TCP6.
   *
   * <p>(TYPE_TCP6   = 0x0000000B)</p>
   */
   public static final int TYPE_TCP6   = 0x0000000B;

  /**
   * NetWare address tupe is WILD.
   *
   * <p>(TYPE_WILD   = 0x00008000)</p>
   */
   public static final int TYPE_WILD   = 0x00008000;

   /**
    * Constructs an address consisting of a type and a byte array.
    *
    * @param type    The address type as an int.
    * @param address The raw address as a byte array.
    */
   public Address
   (
      int type,
      byte[] address
   )
   {
      setAddress(type, address);
   }

   /**
    * Construct an Address using an InetAddress.
    *
    * @param address The InetAddress used in constructing the Address.
    */
   public Address
   (
      InetAddress address
   )
   {
      setAddress(address);
   }

   /**
    * Constructs an address from a String consisting of a type and a byte array.
    *
    * @param String  The address type in the form: type address
    *                where:
    *                   type is ipx, ddp, asp, udp, tcp, udp6 or tcp6
    *                   address is a sequence of byte values.
    */
   public Address
   (
      String addressString
   )
   {
      StringTokenizer st = new StringTokenizer(addressString, " =");
      int type = Address.TYPE_WILD;
      byte address[] = new byte[0];

      if (st.hasMoreTokens())
      {
         String typeString = st.nextToken();

         int radix = 10;
         if      (typeString.equals("ipx"))
         {
            type = Address.TYPE_IPX;
            radix = 16;
         }
         else if (typeString.equals("ddp"))
            type = Address.TYPE_DDP;
         else if (typeString.equals("asp"))
            type = Address.TYPE_ASP;
         else if (typeString.equals("udp"))
            type = Address.TYPE_UDP;
         else if (typeString.equals("tcp"))
            type = Address.TYPE_TCP;
         else if (typeString.equals("udp6"))
            type = Address.TYPE_UDP6;
         else if (typeString.equals("tcp6"))
            type = Address.TYPE_TCP6;

         address = new byte[st.countTokens()];
         int i = 0;
         while (st.hasMoreTokens())
         {
            address[i++]= (byte)Short.parseShort(st.nextToken(), radix);
         }
      }

      setAddress(type, address);
   }

   /**
    * Returns the raw address as a byte array.
    *
    * @return A byte array containing the raw address.
    */
   synchronized public byte[] getAddress
   (
   )
   {
      byte[] outAddr;

       // Validate our internal state before doing something stupid
      if (null == this.address)
         return (null);          // We just don't have anything to work with

       // We have something so copy it out and return it
      outAddr = new byte[this.address.length];
      for (int i = 0; i < this.address.length; i++)
         outAddr[i] = this.address[i];
      return (outAddr);
   }

   /**
    * Returns the address type.
    *
    * @return The address type as an int.
    */
   synchronized public int getType
   (
   )
   {
      return (this.type);
   }

   /**
    * Sets the NetWare address and it's type.
    *
    * @param type The address type as an int.
    * @param address  The raw address as a byte array.
    */
   synchronized public void setAddress
   (
      int type,
      byte[] address
   )
   {
      this.type = type;

      if (null == address)
      {
         if (DEBUG)
         {
            Debug.println("Address.setAddress(): address is NULL");
         }
         if (TYPE_IPX == this.type)
            this.address = new byte [12];  // 12 byte IPX address
         else if (TYPE_TCP == this.type)
            this.address = new byte [6];   // 4 byte TCP address
         else if (TYPE_UDP == this.type)
            this.address = new byte [6];   // 6 byte UDP address
         else
         {
            this.address = null;
            return;
         }
      }
      else
      {
         if (DEBUG)
         {
            Debug.println("Address.setAddress(): Address is NOT null");
         }
         this.address = new byte[address.length];
      }

      for (int i = 0; i < address.length; i++)
         this.address[i] = address[i];
   }

   /**
    * Sets the address as an InetAddress.
    *
    * @param address The address an an InetAddress.
    */
   synchronized public void setAddress
   (
      InetAddress address
   )
   {
      if (null == address)
         throw new NullPointerException("newAddress");

      this.type = TYPE_UDP;

      byte[] tempAddr = address.getAddress();

      // Put port first...required for NT requester
      if (NT_PORT_KLUDGE)
      {
         this.address[0] = tempAddr[4];
         this.address[1] = tempAddr[5];
         this.address[2] = tempAddr[0];
         this.address[3] = tempAddr[1];
         this.address[4] = tempAddr[2];
         this.address[5] = tempAddr[3];
      }
      else
      {
         this.address = tempAddr;
      }
   }

   /**
    * Returns a string representation of this address.
    *
    * @return A string representation of the address.
    */
   synchronized public String toString
   (
   )
   {
      StringBuffer s = new StringBuffer();
      long l;

      if (TYPE_IPX == this.type)
      {
         s.append ("IPX = ");
         if (null != this.address)
            for (int i = 0; i < this.address.length; i++)
            {
               l = NWLong.byteToLong(this.address[i]);
               s.append (padWithZero (Long.toString (l, 16), 2));
               if (3 == i || 9 == i)      // colons after 4th and 10th bytes
                  s.append (':');
            }
      }
      else
      {
         int radix = 10;
         switch (this.type)
         {
            case TYPE_DDP:
               s.append ("DDP ");
               break;
            case TYPE_ASP:
               s.append ("ASP ");
               break;
            case TYPE_UDP:
               s.append ("UDP ");
               break;
            case TYPE_TCP:
               s.append ("TCP ");
               break;
            case TYPE_UDP6:
               s.append ("UDP6 ");
               break;
            case TYPE_TCP6:
               s.append ("TCP6 ");
               break;
            default:
               s.append ("UNKNOWN(" + Integer.toString(type) + ") ");
               radix = 16;
         }
         if (null != this.address)
         {
            for (int i = 0; i < this.address.length; i++)
            {
               l = NWLong.byteToLong (this.address[i]);
               if (i > 0)
                  s.append('.');
               s.append (Long.toString (l, radix));
//               s.append (padWithZero (Long.toString (l, 10), 3));
            }
//            for (int i = 0; i < this.address.length; i++)
//            {
//               s.append(" ");
//               s.append(Byte.toString(this.address[i]));
//            }
         }
         else
            s.append("(null)");
      }

      return (new String (s));
//      return (getClass().getName() + ": " + new String (s));
   }

   private String padWithZero
   (
      String s,
      int length
   )
   {
      StringBuffer buf = new StringBuffer();

      for (int i = 0; i < (length - s.length()); i++)
      {
         buf.append('0');
      }
      buf.append(s);

      return (new String(buf));
   }
}

