package com.limegroup.gnutella.messages.vendor;

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;

import com.limegroup.gnutella.GUID;
import com.limegroup.gnutella.messages.BadPacketException;
import com.limegroup.gnutella.messages.QueryReply;

/** In Vendor Message parlance, the "message type" of this VMP is "BEAR/7".
 *  Used to ask a host you connect to do a TCP ConnectBack.
 */
public final class PushProxyAcknowledgement extends VendorMessage {

    public static final int VERSION = 2;

    /** The payload has 4 bytes dedicated to the IP of the proxy.
     */
    private final InetAddress _addr;

    /** The payload has a 16-bit unsigned value - the port - at which one should
     *  connect back.
     */
    private final int _port;

    /**
     * Constructs a new PushProxyAcknowledgement message with data from the
     * network.
     */
    PushProxyAcknowledgement(byte[] guid, byte ttl, byte hops, int version, 
                             byte[] payload) 
        throws BadPacketException {
        super(guid, ttl, hops, F_LIME_VENDOR_ID, F_PUSH_PROXY_ACK, version,
              payload);

        if (getVersion() == 1)
            throw new BadPacketException("DEPRECATED VERSION");

        if (getVersion() > VERSION)
            throw new BadPacketException("UNSUPPORTED VERSION");

        if (getPayload().length != 6)
            throw new BadPacketException("UNSUPPORTED PAYLOAD LENGTH: " +
                                         payload.length);
        // get the ip and  port from the payload....
        QueryReply.IPPortCombo combo = 
            QueryReply.IPPortCombo.getCombo(getPayload());
        _addr = combo.getInetAddress();
        _port = combo.getPort();
    }

    /**
     * Constructs a new PushProxyAcknowledgement message to be sent out.
     * @param addr The address of the person to connect back to.
     * @param port The port you want people to connect back to.  If you give a
     *  bad port I don't check so check yourself!
     */
    public PushProxyAcknowledgement(InetAddress addr, int port) {
        super(F_LIME_VENDOR_ID, F_PUSH_PROXY_ACK, VERSION, 
              derivePayload(addr, port));
        _addr = addr;
        _port = port;
    }

    /**
     * Constructs a new PushProxyAcknowledgement message to be sent out.
     * @param addr The address of the person to connect back to.
     * @param port The port you want people to connect back to.  If you give a
     *  bad port I don't check so check yourself!
     *  @param guid In case you want to set the guid (the PushProxy protocol
     *  advises this).
     */
    public PushProxyAcknowledgement(InetAddress addr, int port,
                                    GUID guid) {
        super(F_LIME_VENDOR_ID, F_PUSH_PROXY_ACK, VERSION, 
              derivePayload(addr, port));
        _addr = addr;
        _port = port;
        setGUID(guid);
    }

    /** @return the port the PushProxy is listening on....
     */
    public int getListeningPort() {
        return _port;
    }

    /** @return the InetAddress the PushProxy is listening on....
     */
    public InetAddress getListeningAddress() {
        return _addr;
    }

    private static byte[] derivePayload(InetAddress addr, int port) {
        try {
            // i do it during construction....
            QueryReply.IPPortCombo combo = 
                new QueryReply.IPPortCombo(addr.getHostAddress(), port);
            return combo.toBytes();
        } catch (UnknownHostException uhe) {
            throw new IllegalArgumentException(uhe.getMessage());
        }
    }

    /** Overridden purely for stats handling.
     */
    protected void writePayload(OutputStream out) throws IOException {
        super.writePayload(out);
    }

    /** Overridden purely for stats handling.
     */
    public void recordDrop() {
        super.recordDrop();
    }


}
