// Copyright 1997 The Open Group Research Institute.  All rights reserved.

package krb5.lib;

import krb5.lib.asn1.*;
import java.net.InetAddress;
import java.net.UnknownHostException;

/**
 * Provides the host address information.
 */
public class HostAddress implements Cloneable {
	/**
	 * Address type
	 * @see krb5.lib.Krb5
	 */
	public int addrType;
	
	/**
	 * Address 
	 */	
	public byte[] address;
	
	/**
	 * Local Inet address
	 */	
	private static byte[] localInetAddress; //caches local inet address

    /** 
     * Class constructor
	 * @param dummy is of type int
	 */
	private HostAddress(int dummy) {
	}

	/**
	 * This clone a copy of the object
	 *
	 * @return Object is a result
	 * @see java.lang.Object
	 */
	public Object clone() {
		HostAddress new_hostAddress = new HostAddress(0);
		new_hostAddress.addrType = addrType;
		if (address != null) {
			new_hostAddress.address = new byte[address.length];
			System.arraycopy(address, 0, new_hostAddress.address,
				0, address.length);
		}
		return new_hostAddress;
	}

	/**
	 * Compares the host addresses
	 *
	 * @return boolean is a result
	 * @param h is of type HostAddress
	 * @see krb5.lib.HostAddress
	 */
	public boolean equals(HostAddress h) {
		if (addrType != h.addrType ||
			(address != null && h.address == null) ||
			(address == null && h.address != null))
			return false;
		if (address != null && h.address != null) {
			if (address.length != h.address.length)
				return false;
			for (int i = 0; i < address.length; i++)
				if (address[i] != h.address[i])
					return false;
		}
		return true;
	}
	
	/**
	 * Refreshes the local inet address
	 *
	 * @exception UnknownHostException an exception
	 * @see java.net.UnknownHostException
	 */
	public static void refreshLocalInetAddress() throws UnknownHostException {
		localInetAddress = InetAddress.getLocalHost().getAddress();
	}

	/**
	 * Gets the local inet address
	 *
	 * @return byte[] is a result
	 * @exception UnknownHostException an exception
	 * @see java.net.UnknownHostException
	 */
	public static byte[] getLocalInetAddress() throws UnknownHostException {
		if (localInetAddress == null)
			refreshLocalInetAddress();
		if (localInetAddress == null)
			//XXX should throw exception here
			return null;
		byte[] result = new byte[localInetAddress.length];
		System.arraycopy(localInetAddress, 0, result, 0,
			localInetAddress.length);
		return result;
	}

	//XXX implicit default not in Config.java
	/**
	 * Class constructor specifying the default address type to Krb5.ADDRTYPE_INET
	 *
	 * @exception UnknownHostException an exception
	 * @see krb5.lib.Krb5
	 * @see java.net.UnknownHostException
	 */
	public HostAddress() throws UnknownHostException {
		addrType = Krb5.ADDRTYPE_INET;
		address = getLocalInetAddress();
	}

	/**
	 * Class constructor specifying the address type and the address
	 *
	 * @param new_addrType is of type int
	 * @param new_address is of type byte[]
	 * @see krb5.lib.Krb5
	 */
	public HostAddress(int new_addrType, byte[] new_address) {
		//XXX need to validate addrType
		addrType = new_addrType;
		//XXX need to validate address length for addrType
		address = new_address;
	}

	/**
	 * Class constructor specifying the InetAddress
	 *
	 * @param inetAddress is of type InetAddress
	 * @see java.net.InetAddress
	 */
	public HostAddress(InetAddress inetAddress) {
		addrType = Krb5.ADDRTYPE_INET;
		byte[] new_address = inetAddress.getAddress();
		address = new byte[new_address.length];
		System.arraycopy(new_address, 0, address, 0, new_address.length);
	}

	/**
	 * Class constructor specifying the encoding reference
	 *
	 * @param ref is of type EncodeRef
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.asn1.EncodeRef
	 * @see krb5.lib.Asn1Exception
	 */
	public HostAddress(EncodeRef ref) throws Asn1Exception {
		EncodeRef subRef = decode.Sequence(ref);
		if (decode.Tag(subRef) == 0) {
			addrType = decode.Integer(subRef.startOfData());
			//XXX need to validate that the type is recognized
			subRef.next();
		}
		else
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);
		if (decode.Tag(subRef) == 1) {
			address = decode.OctetString(subRef.startOfData());
			//XXX need to validate address length is right for addrType
			subRef.next();
		}
		else
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);
		if (subRef.isMoreInSequence())
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);
	}

	/**
	 * Encodes the object in asn1
	 *
	 * @return byte[] is a result
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public byte[] asn1Encode() throws Asn1Exception {
		byte[][] host_address = {
			encode.prependExplicitTag(0, encode.Integer(addrType)),
			encode.prependExplicitTag(1, encode.OctetString(address))
		};
		return encode.Sequence(host_address);
	}

}
