package com.limegroup.gnutella.messages.vendor;

import java.util.Set;

import org.limewire.collection.IntervalSet;
import org.limewire.io.IpPort;

import com.limegroup.gnutella.PushEndpoint;
import com.limegroup.gnutella.RemoteFileDesc;
import com.limegroup.gnutella.downloader.RemoteFileDescFactory;

/**
 * a response to an HeadPing.  It is a trimmed down version of the standard HEAD response,
 * since we are trying to keep the sizes of the udp packets small.
 * 
 * This message can also be used for punching firewalls if the ping requests so. 
 * Feature like this can be used to allow firewalled nodes to participate more 
 * in download meshes.
 * 
 * Since headpings will be sent by clients who have started to download a file whose download
 * mesh contains  this host, it needs to contain information that will help those clients whether 
 * this host is a good bet to start an http download from.  Therefore, the following information should
 * be included in the response:
 * 
 *  - available ranges of the file 
 *  - queue status
 *  - some altlocs (if space permits)
 * 
 * the queue status can be an integer representing how many people are waiting in the queue.  If 
 * nobody is waiting in the queue and we have slots available, the integer can be negative.  So if
 * we have 3 people on the queue we'd send the integer 3.  If we have nobody on the queue and 
 * two upload slots available we would send -2.  A value of 0 means all upload slots are taken but 
 * the queue is empty.  This information can be used by the downloaders to better judge chances of
 * successful start of the download. 
 * 
 * NEW GGEP FORMAT:
 *   A GGEP block containing:
 *    F: features (optional)
 *     supported features:
 *      0x1 = TLS_CAPABLE
 *    C: response code (required)
 *    V: vendor id (required if not 404)
 *    Q: queue status (required if not 404)
 *    R: ranges (optional, shouldn't be if complete file)
 *    P: push locations (optional)
 *    A: direct locations (optional)
 *    T: indexes of which direct locations support TLS
 * 
 * OLD BINARY FORMAT:
 *   1 byte - features byte
 *   2 byte - response code
 *   4 bytes - vendor id
 *   1 byte - queue status
 *   n*8 bytes - n intervals (if requested && file partial && fits in packet)
 *   the rest - altlocs (if requested) 
 */

public interface HeadPong extends VendorMessage {

    public static final int BINARY_VERSION = 1;

    public static final int GGEP_VERSION = 2;

    public static final int VERSION = 2;
    
    /** GGEP fields in the ggep format of the pong. */ 
    static final String FEATURES  = "F";
    static final String CODE      = "C";
    static final String VENDOR    = "V";
    static final String QUEUE     = "Q";
    static final String RANGES    = "R";
    static final String RANGES5    = "R5";
    static final String PUSH_LOCS = "P";
    static final String LOCS      = "A";
    static final String TLS_LOCS  = "T";
    
    /** Features within the GGEP Features block. */
    static final byte TLS_CAPABLE = 0x1;
    
    /**
     * instead of using the HTTP codes, use bit values.  The first three 
     * possible values are mutually exclusive though.  DOWNLOADING is
     * possible only if PARTIAL_FILE is set as well.
     */
    static final byte FILE_NOT_FOUND= (byte)0x0;
    static final byte COMPLETE_FILE= (byte)0x1;
    static final byte PARTIAL_FILE = (byte)0x2;
    static final byte FIREWALLED = (byte)0x4;
    static final byte DOWNLOADING = (byte)0x8;
    
    static final byte CODES_MASK=(byte)0xF;
    /** all our slots are full. */
    static final byte BUSY=(byte)0x7F;

    /**
     * 
     * @return whether the alternate location still has the file
     */
    public boolean hasFile();

    /**
     * 
     * @return whether the alternate location has the complete file
     */
    public boolean hasCompleteFile();

    /**
     * 
     * @return the available ranges the alternate location has
     */
    public IntervalSet getRanges();

    /**
     * @return set of <tt>Endpoint</tt> 
     * containing any alternate locations this alternate location returned or
     * and empty set if not available
     */
    public Set<IpPort> getAltLocs();

    /**
     * @return set of <tt>PushEndpoint</tt>
     * containing any firewalled locations this alternate location returned or
     * an empty set if not available.
     */
    public Set<PushEndpoint> getPushLocs();

    /** Whether or not this pong supports TLS. */
    public boolean isTLSCapable();

    /**
     * @return all altlocs carried in the pong as 
     * set of <tt>RemoteFileDesc</tt>
     */
    public Set<RemoteFileDesc> getAllLocsRFD(RemoteFileDesc original, RemoteFileDescFactory remoteFileDescFactory);

    /**
     * updates the rfd with information in this pong
     */
    public void updateRFD(RemoteFileDesc rfd);

    /**
     * 
     * @return the remote vendor as string
     */
    public String getVendor();

    /**
     * 
     * @return whether the remote is firewalled and will need a push
     */
    public boolean isFirewalled();

    public int getQueueStatus();

    public boolean isBusy();

    public boolean isDownloading();

    /**
     * @return true if this pong came from a host that doesn't support routing
     */
    public boolean isRoutingBroken();

    // TODO: hack to let PingRankerTest compile
    public byte[] getPayload();
}

