/* This code is part of Freenet. It is distributed under the GNU General
 * Public License, version 2 (or at your option any later version). See
 * http://www.gnu.org/ for further details of the GPL. */
package freenet.keys;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Arrays;

import freenet.support.Base64;
import freenet.support.Fields;
import freenet.support.Logger;

/**
 * @author amphibian
 * 
 * Node-level CHK. Does not have enough information to decode the payload.
 * But can verify that it is intact. Just has the routingKey.
 */
public class NodeCHK extends Key {

    /** 32 bytes for hash, 2 bytes for type */
    public static final short FULL_KEY_LENGTH = 34;
	
    public NodeCHK(byte[] routingKey2, byte cryptoAlgorithm) {
    	super(routingKey2);
        if(routingKey2.length != KEY_LENGTH)
            throw new IllegalArgumentException("Wrong length: "+routingKey2.length+" should be "+KEY_LENGTH);
        this.cryptoAlgorithm = cryptoAlgorithm;
    }
    
    private NodeCHK(NodeCHK key) {
    	super(key);
    	this.cryptoAlgorithm = key.cryptoAlgorithm;
    }
    
    @Override
	public Key cloneKey() {
    	return new NodeCHK(this);
    }

    public static final int KEY_LENGTH = 32;
    
	/** Crypto algorithm */
	final byte cryptoAlgorithm;

	public static final byte BASE_TYPE = 1;

    @Override
    public final void writeToDataOutputStream(DataOutputStream stream) throws IOException {
        write(stream);
    }

    @Override
	public String toString() {
        return super.toString() + '@' +Base64.encode(routingKey)+ ':' +Integer.toHexString(hash);
    }

    @Override
	public final void write(DataOutput _index) throws IOException {
        _index.writeShort(getType());
        _index.write(routingKey);
    }
    
    public static Key readCHK(DataInput raf, byte algo) throws IOException {
        byte[] buf = new byte[KEY_LENGTH];
        raf.readFully(buf);
        return new NodeCHK(buf, algo);
    }

    @Override
	public boolean equals(Object key) {
    	if(key == this) return true;
        if(key instanceof NodeCHK) {
            NodeCHK chk = (NodeCHK) key;
            return java.util.Arrays.equals(chk.routingKey, routingKey) && (cryptoAlgorithm == chk.cryptoAlgorithm);
        }
        return false;
    }
    
    @Override
	public int hashCode(){
    	return super.hashCode();
    }
    
	@Override
	public short getType() {
		return (short) ((BASE_TYPE << 8) + (cryptoAlgorithm & 0xFF));
	}
    
    @Override
	public byte[] getRoutingKey(){
    	return routingKey;
    }

	@Override
	public byte[] getFullKey() {
		byte[] buf = new byte[FULL_KEY_LENGTH];
		short type = getType();
		buf[0] = (byte) (type >> 8);
		buf[1] = (byte) (type & 0xFF);
		System.arraycopy(routingKey, 0, buf, 2, routingKey.length);
		return buf;
	}
	
	public static byte cryptoAlgorithmFromFullKey(byte[] fullKey) {
		return fullKey[1];
	}

	public static byte[] routingKeyFromFullKey(byte[] keyBuf) {
		if(keyBuf.length == KEY_LENGTH) return keyBuf;
		if(keyBuf.length != FULL_KEY_LENGTH) {
			Logger.error(NodeCHK.class, "routingKeyFromFullKey() on "+keyBuf.length+" bytes");
			return null;
		}
		if(keyBuf[0] != 1 || (keyBuf[1] != Key.ALGO_AES_PCFB_256_SHA256 && keyBuf[1] != Key.ALGO_AES_CTR_256_SHA256)) {
			if(keyBuf[keyBuf.length-1] == 0 && keyBuf[keyBuf.length-2] == 0) {
				// We are certain it's a routing-key
				Logger.minor(NodeCHK.class, "Recovering routing-key stored wrong as full-key (two nulls at end)");
			} else {
				// It might be a routing-key or it might be random data
				Logger.error(NodeCHK.class, "Maybe recovering routing-key stored wrong as full-key");
			}
			return Arrays.copyOf(keyBuf, KEY_LENGTH);
		}
		return Arrays.copyOfRange(keyBuf, 2, 2 + KEY_LENGTH);
	}

	@Override
	public int compareTo(Key arg0) {
		if(arg0 instanceof NodeSSK) return 1;
		NodeCHK key = (NodeCHK) arg0;
		return Fields.compareBytes(routingKey, key.routingKey);
	}

	@Override
	public Key archivalCopy() {
		return new NodeCHK(this);
	}
}
