package com.limegroup.gnutella;

import java.util.Set;

import org.limewire.io.IpPort;

import com.limegroup.gnutella.http.HTTPHeaderValue;

/**
 * a class that represents an endpoint behind one or more PushProxies.
 * almost everything is immutable including the contents of the set.
 * 
 * the network format this is serialized to is:
 * byte 0 (from right-to-left): 
 *    - bits 0-2 how many push proxies we have (so max is 7)
 *    - bits 3-4 the version of the f2f transfer protocol this altloc supports
 *    - bits 5-6 other possible features.
 *    - bit  7   set if the TLS-capable push proxy indexes byte is included
 * bytes 1-16 : the guid
 * bytes 17-22: ip:port of the address (if FWT version > 0)
 * followed by a byte of TLS-capable PushProxy indexes (if bit 7 of features is set)
 * followed by 6 bytes per PushProxy 
 * 
 * the http format this is serialized to is an ascii string consisting of
 * ';'-delimited tokens.  The first token is the client GUID represented in hex
 * and is the only required token.  The other tokens can be addresses of push proxies
 * or various feature headers.  At most one of the tokens should be the external ip and port 
 * of the firewalled node in a port:ip format. Currently the only feature header we 
 * parse is the fwawt header that contains the version number of the firewall to 
 * firewall transfer protocol supported by the altloc.  In addition, the 'pptls=' field
 * can indicate which, if any, push proxies support TLS.  If the field is present, it 
 * must be immediately before the listing of the push proxies.  The hexadecimal string
 * after the '=' is a bit-representation of which push proxies are valid for TLS.
 * 
 * A PE does not need to know the actual external address of the firewalled host,
 * however without that knowledge we cannot do firewall-to-firewall transfer with 
 * the given host.  Also, the RemoteFileDesc objects requires a valid IP for construction,
 * so in the case we do not know the external address we return a BOGUS_IP.
 * 
 * Examples:
 * 
 *  //altloc with 2 proxies and supports firewall transfer 1 :
 * 
 * <ThisIsTheGUIDASDF>;fwt/1.0;20.30.40.50:60;1.2.3.4:5567
 * 
 *   //altloc with 1 proxy and doesn't support firewall transfer, with external address:
 * 
 * <ThisIsTHeGUIDasfdaa527>;1.2.3.4:5564;6346:2.3.4.5
 * 
 * //altloc with 1 proxy and supports two features we don't know/care about :
 * 
 * <ThisIsTHeGUIDasfdaa527>;someFeature/3.2;10.20.30.40:5564;otherFeature/0.4
 * 
 * //altloc with 1 proxy (the first) that's TLS capable, 1 that isn't:
 * 
 * <ThisIsTheGUIDASDF>;fwt/1.0;pptls=8;20.30.40.50:60;1.2.3.4:5567
 * 
 *  //altloc without any proxies and doesn't support any features
 *  // not very useful, but still valid  
 * 
 * <ThisIsTheGUIDasdf23457>
 */
public interface PushEndpoint extends HTTPHeaderValue, IpPort {

    public static final int HEADER_SIZE = 17; //guid+# of proxies, maybe other things too

    public static final int PROXY_SIZE = 6; //ip:port

    public static final byte PLAIN = 0x0; //no features for this PE

    public static final byte PPTLS_BINARY = (byte) 0x80;
    
    public static final byte SIZE_MASK=0x7; //0000 0111
    
    public static final byte FWT_VERSION_MASK=0x18; //0001 1000
    
    //the features mask does not clear the bits we do not understand
    //because we may pass on the altloc to someone who does.
    public static final byte FEATURES_MASK= (byte)0xE0;   //1110 0000
    
    /** The pptls portion constant. */
    public static final String PPTLS_HTTP = "pptls";

    /** The maximum number of proxies to use. */
    public static final int MAX_PROXIES = 4;

    /**
     * @return a byte-packed representation of this
     */
    byte[] toBytes(boolean includeTLS);

    /**
     * creates a byte packet representation of this
     * @param where the byte [] to serialize to 
     * @param offset the offset within that byte [] to serialize
     */
    void toBytes(byte[] where, int offset, boolean includeTLS);

    /**
     * Returns the GUID of the client that can be reached through the pushproxies.
     */
    byte[] getClientGUID();

    /**
     * @return a view of the current set of proxies, never returns null
     */
    Set<? extends IpPort> getProxies();

    /**
     * @return which version of F2F transfers this PE supports.
     * This always returns the most current version we know the PE supports
     * unless it has never been put in the map.
     */
    int getFWTVersion();

    /**
     * Should return the {@link GUID#hashCode()} of {@link #getClientGUID()}.
     */
    int hashCode();

    /**
     * Equality should be based on the equality of the value of {@link #getClientGUID()}.
     */
    boolean equals(Object other);
    
    /**
     * @return the various features this PE reports.  This always
     * returns the most current features, or the ones it was created with
     * if they have never been updated.
     */
    byte getFeatures();

    /**
     * @return true if this is the push endpoint for the local node
     */
    boolean isLocal();

    /**
     * Updates either the PushEndpoint or the GUID_PROXY_MAP to ensure
     * that GUID_PROXY_MAP has a reference to all live PE GUIDs and
     * all live PE's reference the same GUID object as in GUID_PROXY_MAP.
     * 
     * If this method is not called, the PE will know only about the set
     * of proxies the remote host had when it was created.  Otherwise it
     * will point to the most recent known set.
     */
    // TODO remove this in the long run and use the cache explicitly
    void updateProxies(boolean good);

    /**
     * Can return null if no valid push endpoint can be cloned. 
     */
    PushEndpoint createClone();
    
    /**
     * Returns an {@link IpPort} representing the valid external address of
     * this push endpoint if it is known, otherwise <code>null</code>. 
     */
    IpPort getValidExternalAddress();
    
    /**
     * @return the external address if known otherwise {@link RemoteFileDesc#BOGUS_IP}
     */
    String getAddress();
}