/*
 *   Copyright 2005 The Apache Software Foundation
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 *
 */

package org.apache.directory.server.dhcp.options;


import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.apache.directory.server.dhcp.options.dhcp.BootfileName;
import org.apache.directory.server.dhcp.options.dhcp.ClientIdentifier;
import org.apache.directory.server.dhcp.options.dhcp.DhcpMessageType;
import org.apache.directory.server.dhcp.options.dhcp.IpAddressLeaseTime;
import org.apache.directory.server.dhcp.options.dhcp.MaximumDhcpMessageSize;
import org.apache.directory.server.dhcp.options.dhcp.OptionOverload;
import org.apache.directory.server.dhcp.options.dhcp.ParameterRequestList;
import org.apache.directory.server.dhcp.options.dhcp.RebindingTimeValue;
import org.apache.directory.server.dhcp.options.dhcp.RenewalTimeValue;
import org.apache.directory.server.dhcp.options.dhcp.RequestedIpAddress;
import org.apache.directory.server.dhcp.options.dhcp.ServerIdentifier;
import org.apache.directory.server.dhcp.options.dhcp.TftpServerName;
import org.apache.directory.server.dhcp.options.dhcp.UnrecognizedOption;
import org.apache.directory.server.dhcp.options.dhcp.VendorClassIdentifier;
import org.apache.directory.server.dhcp.options.misc.DefaultFingerServers;
import org.apache.directory.server.dhcp.options.misc.DefaultIrcServers;
import org.apache.directory.server.dhcp.options.misc.DefaultWwwServers;
import org.apache.directory.server.dhcp.options.misc.MobileIpHomeAgents;
import org.apache.directory.server.dhcp.options.misc.NbddServers;
import org.apache.directory.server.dhcp.options.misc.NetbiosNameServers;
import org.apache.directory.server.dhcp.options.misc.NetbiosNodeType;
import org.apache.directory.server.dhcp.options.misc.NetbiosScope;
import org.apache.directory.server.dhcp.options.misc.NisDomain;
import org.apache.directory.server.dhcp.options.misc.NisPlusDomain;
import org.apache.directory.server.dhcp.options.misc.NisPlusServers;
import org.apache.directory.server.dhcp.options.misc.NisServers;
import org.apache.directory.server.dhcp.options.misc.NntpServers;
import org.apache.directory.server.dhcp.options.misc.NtpServers;
import org.apache.directory.server.dhcp.options.misc.Pop3Servers;
import org.apache.directory.server.dhcp.options.misc.SmtpServers;
import org.apache.directory.server.dhcp.options.misc.StdaServers;
import org.apache.directory.server.dhcp.options.misc.StreetTalkServers;
import org.apache.directory.server.dhcp.options.misc.VendorSpecificInformation;
import org.apache.directory.server.dhcp.options.misc.XWindowDisplayManagers;
import org.apache.directory.server.dhcp.options.misc.XWindowFontServers;
import org.apache.directory.server.dhcp.options.perhost.DefaultIpTimeToLive;
import org.apache.directory.server.dhcp.options.perhost.IpForwarding;
import org.apache.directory.server.dhcp.options.perhost.MaximumDatagramSize;
import org.apache.directory.server.dhcp.options.perhost.NonLocalSourceRouting;
import org.apache.directory.server.dhcp.options.perhost.PathMtuAgingTimeout;
import org.apache.directory.server.dhcp.options.perhost.PathMtuPlateauTable;
import org.apache.directory.server.dhcp.options.perhost.PolicyFilter;
import org.apache.directory.server.dhcp.options.perinterface.AllSubnetsAreLocal;
import org.apache.directory.server.dhcp.options.perinterface.BroadcastAddress;
import org.apache.directory.server.dhcp.options.perinterface.InterfaceMtu;
import org.apache.directory.server.dhcp.options.perinterface.MaskSupplier;
import org.apache.directory.server.dhcp.options.perinterface.PerformMaskDiscovery;
import org.apache.directory.server.dhcp.options.perinterface.PerformRouterDiscovery;
import org.apache.directory.server.dhcp.options.perinterface.RouterSolicitationAddress;
import org.apache.directory.server.dhcp.options.perinterface.StaticRoute;
import org.apache.directory.server.dhcp.options.tcp.TcpDefaultTimeToLive;
import org.apache.directory.server.dhcp.options.tcp.TcpKeepaliveGarbage;
import org.apache.directory.server.dhcp.options.tcp.TcpKeepaliveInterval;
import org.apache.directory.server.dhcp.options.vendor.BootFileSize;
import org.apache.directory.server.dhcp.options.vendor.CookieServers;
import org.apache.directory.server.dhcp.options.vendor.DomainName;
import org.apache.directory.server.dhcp.options.vendor.DomainNameServers;
import org.apache.directory.server.dhcp.options.vendor.ExtensionsPath;
import org.apache.directory.server.dhcp.options.vendor.HostName;
import org.apache.directory.server.dhcp.options.vendor.ImpressServers;
import org.apache.directory.server.dhcp.options.vendor.LogServers;
import org.apache.directory.server.dhcp.options.vendor.LprServers;
import org.apache.directory.server.dhcp.options.vendor.MeritDumpFile;
import org.apache.directory.server.dhcp.options.vendor.NameServers;
import org.apache.directory.server.dhcp.options.vendor.ResourceLocationServers;
import org.apache.directory.server.dhcp.options.vendor.RootPath;
import org.apache.directory.server.dhcp.options.vendor.Routers;
import org.apache.directory.server.dhcp.options.vendor.SubnetMask;
import org.apache.directory.server.dhcp.options.vendor.SwapServer;
import org.apache.directory.server.dhcp.options.vendor.TimeOffset;
import org.apache.directory.server.dhcp.options.vendor.TimeServers;
import org.apache.directory.server.i18n.I18n;


/**
 * The Dynamic Host Configuration Protocol (DHCP) provides a framework
 * for passing configuration information to hosts on a TCP/IP network.  
 * Configuration parameters and other control information are carried in
 * tagged data items that are stored in the 'options' field of the DHCP
 * message.  The data items themselves are also called "options."
 * 
 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
 */
public abstract class DhcpOption
{
    /**
     * An array of concrete implementations of DhcpOption.
     */
    private static Class OPTION_CLASSES[] =
        { BootfileName.class, ClientIdentifier.class, DhcpMessageType.class, IpAddressLeaseTime.class,
            MaximumDhcpMessageSize.class, org.apache.directory.server.dhcp.options.dhcp.Message.class,
            OptionOverload.class, ParameterRequestList.class, RebindingTimeValue.class, RenewalTimeValue.class,
            RequestedIpAddress.class, ServerIdentifier.class, TftpServerName.class, VendorClassIdentifier.class,
            ClientIdentifier.class, DhcpMessageType.class, IpAddressLeaseTime.class, MaximumDhcpMessageSize.class,
            OptionOverload.class, ParameterRequestList.class, RebindingTimeValue.class, RenewalTimeValue.class,
            RequestedIpAddress.class, ServerIdentifier.class, TftpServerName.class, UnrecognizedOption.class,
            VendorClassIdentifier.class, DefaultFingerServers.class, DefaultIrcServers.class, DefaultWwwServers.class,
            MobileIpHomeAgents.class, NbddServers.class, NetbiosNameServers.class, NetbiosNodeType.class,
            NetbiosScope.class, NisDomain.class, NisPlusDomain.class, NisPlusServers.class, NisServers.class,
            NntpServers.class, NtpServers.class, Pop3Servers.class, SmtpServers.class, StdaServers.class,
            StreetTalkServers.class, VendorSpecificInformation.class, XWindowDisplayManagers.class,
            XWindowFontServers.class, DefaultIpTimeToLive.class, IpForwarding.class, MaximumDatagramSize.class,
            NonLocalSourceRouting.class, PathMtuAgingTimeout.class, PathMtuPlateauTable.class, PolicyFilter.class,
            AllSubnetsAreLocal.class, BroadcastAddress.class, InterfaceMtu.class, MaskSupplier.class,
            PerformMaskDiscovery.class, PerformRouterDiscovery.class, RouterSolicitationAddress.class,
            StaticRoute.class, TcpDefaultTimeToLive.class, TcpKeepaliveGarbage.class, TcpKeepaliveInterval.class,
            BootFileSize.class, CookieServers.class, DomainName.class, DomainNameServers.class, ExtensionsPath.class,
            HostName.class, ImpressServers.class, LogServers.class, LprServers.class, MeritDumpFile.class,
            NameServers.class, ResourceLocationServers.class, RootPath.class, Routers.class, SubnetMask.class,
            SwapServer.class, TimeOffset.class, TimeServers.class, };

    /**
     * A map of concrete implementations of DhcpOption indexed by tag code.
     */
    private static Map OPTION_CLASS_BY_CODE;

    /**
     * A map of tag codes indexed by OptionClass subclass.
     */
    private static Map CODE_BY_CLASS;

    static
    {
        try
        {
            // initialize the tag-to-class and class-to-tag map
            Map classByCode = new HashMap();
            Map codeByClass = new HashMap();
            for ( int i = 0; i < OPTION_CLASSES.length; i++ )
            {
                Class c = OPTION_CLASSES[i];

                if ( !DhcpOption.class.isAssignableFrom( c ) )
                {
                    throw new RuntimeException( I18n.err( I18n.ERR_639, c ) );
                }

                DhcpOption o = ( DhcpOption ) c.newInstance();

                Integer tagInt = Integer.valueOf( o.getTag() );
                classByCode.put( tagInt, c );
                codeByClass.put( c, tagInt );
            }

            OPTION_CLASS_BY_CODE = Collections.unmodifiableMap( classByCode );
            CODE_BY_CLASS = Collections.unmodifiableMap( codeByClass );
        }
        catch ( Exception e )
        {
            throw new RuntimeException( I18n.err( I18n.ERR_640 ), e );
        }
    }


    public static Class getClassByTag( int tag )
    {
        return ( Class ) OPTION_CLASS_BY_CODE.get( Integer.valueOf( tag ) );
    }


    public static int getTagByClass( Class c )
    {
        return ( ( Integer ) CODE_BY_CLASS.get( c ) ).intValue();
    }

    /**
     * The default data array used for simple (unparsed) options.
     */
    private byte[] data;


    /**
     * Get the option's code tag.
     * 
     * @return byte
     */
    public abstract byte getTag();


    /**
     * Set the data (wire format) from a byte array. The default implementation
     * just records the data as a byte array. Subclasses may parse the data into
     * something more meaningful.
     * 
     * @param data
     */
    public void setData( byte data[] )
    {
        this.data = data;
    }


    /**
     * Get the data (wire format) into a byte array. Subclasses must provide an
     * implementation which serializes the parsed data back into a byte array if
     * they override {@link #setData(byte[])}.
     * 
     * @return byte[]
     */
    public byte[] getData()
    {
        return data;
    }


    public final void writeTo( ByteBuffer out )
    {
        out.put( getTag() );

        // FIXME: handle continuation, i.e. options longer than 128 bytes?
        byte data[] = getData();

        if ( data.length > 255 )
        {
            throw new IllegalArgumentException( I18n.err( I18n.ERR_641 ) );
        }

        out.put( ( byte ) data.length );
        out.put( data );
    }
}
