package com.limegroup.gnutella.dht.db;

import org.limewire.io.GGEP;
import org.limewire.mojito.db.DHTValueType;
import org.limewire.mojito.routing.Version;
import org.limewire.mojito.util.ArrayUtils;
import org.limewire.util.ByteUtils;

import com.limegroup.gnutella.GUID;

/**
 * An implementation of DHTValue for for Gnutella Alternate Locations
 */
public abstract class AbstractAltLocValue implements AltLocValue {
    
    /**
     * DHTValueType for AltLocs
     */
    public static final DHTValueType ALT_LOC = DHTValueType.valueOf("Gnutella Alternate Location", "ALOC");
    
    /*
     * AltLocValue version history
     * 
     * Version 0:
     * GUID
     * Port
     * Firewalled
     * 
     * Version 1:
     * File Length
     * TigerTree root hash (optional)
     * Incoming TLS support (optional)
     */
    
    /**
     * 
     */
    public static final Version VERSION_ONE = Version.valueOf(1);
    
    /**
     * Version of AltLocDHTValue
     */
    public static final Version VERSION = VERSION_ONE;
    
    static final String CLIENT_ID = "client-id";
    
    static final String PORT = "port";
    
    static final String FIREWALLED = "firewalled";
    
    static final String LENGTH = "length";
    
    static final String TTROOT = "ttroot";
    
    static final String TLS = "tls";
    
    protected final Version version;
    
    public AbstractAltLocValue(Version version) {
        this.version = version;
    }
    
    /*
     * (non-Javadoc)
     * @see org.limewire.mojito.db.DHTValue#getVersion()
     */
    /* (non-Javadoc)
     * @see com.limegroup.gnutella.dht.db.AltLocValue#getVersion()
     */
    public Version getVersion() {
        return version;
    }

    /*
     * (non-Javadoc)
     * @see org.limewire.mojito.db.DHTValue#getValueType()
     */
    /* (non-Javadoc)
     * @see com.limegroup.gnutella.dht.db.AltLocValue#getValueType()
     */
    public DHTValueType getValueType() {
        return ALT_LOC;
    }

    /*
     * (non-Javadoc)
     * @see org.limewire.mojito.db.DHTValue#size()
     */
    /* (non-Javadoc)
     * @see com.limegroup.gnutella.dht.db.AltLocValue#size()
     */
    public int size() {
        return getValue().length;
    }
    
    /* (non-Javadoc)
     * @see com.limegroup.gnutella.dht.db.AltLocValue#getGUID()
     */
    public abstract byte[] getGUID();
    
    /* (non-Javadoc)
     * @see com.limegroup.gnutella.dht.db.AltLocValue#getPort()
     */
    public abstract int getPort();
    
    /* (non-Javadoc)
     * @see com.limegroup.gnutella.dht.db.AltLocValue#getFileSize()
     */
    public abstract long getFileSize();
    
    /* (non-Javadoc)
     * @see com.limegroup.gnutella.dht.db.AltLocValue#getRootHash()
     */
    public abstract byte[] getRootHash();
    
    /* (non-Javadoc)
     * @see com.limegroup.gnutella.dht.db.AltLocValue#isFirewalled()
     */
    public abstract boolean isFirewalled();
    
    /* (non-Javadoc)
     * @see com.limegroup.gnutella.dht.db.AltLocValue#supportsTLS()
     */
    public abstract boolean supportsTLS();
    
    /*
     * (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        StringBuilder buffer = new StringBuilder();
        buffer.append("AltLoc: guid=").append(new GUID(getGUID()))
            .append(", port=").append(getPort())
            .append(", firewalled=").append(isFirewalled())
            .append(", tls=").append(supportsTLS())
            .append(", fileSize=").append(getFileSize())
            .append(", ttroot=").append(getRootHash() != null 
                    ? ArrayUtils.toHexString(getRootHash()) : "null");
        
        if (this instanceof AltLocValueForSelf) {
            buffer.append(", local=true");
        }
        
        return buffer.toString();
    }
    
    /**
     * A helper method to serialize AltLocValues
     */
    protected static byte[] serialize(AbstractAltLocValue value) {
        Version version = value.getVersion();
        
        GGEP ggep = new GGEP();
        
        ggep.put(CLIENT_ID, value.getGUID());
        
        byte[] port = new byte[2];
        ByteUtils.short2beb((short)value.getPort(), port, 0);
        ggep.put(PORT, port);
        
        byte[] firewalled = { (byte)(value.isFirewalled() ? 1 : 0) };
        ggep.put(FIREWALLED, firewalled);
        
        if (version.compareTo(VERSION_ONE) >= 0) {
            ggep.put(LENGTH, /* long */ value.getFileSize());
            
            byte[] ttroot = value.getRootHash();
            if (ttroot != null) {
                ggep.put(TTROOT, ttroot);
            }
            
            if (value.supportsTLS())
                ggep.put(TLS);
        }
        
        return ggep.toByteArray();
    }
}
