/* 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.crypt;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import freenet.support.HexUtil;
import freenet.support.Logger;

public abstract class CryptoKey implements CryptoElement, Serializable {

    private static final long serialVersionUID = 1L;
    protected static final MessageDigest shactx;
	static {
		try {
			shactx = MessageDigest.getInstance("SHA1", Util.mdProviders.get("SHA1"));
		} catch(NoSuchAlgorithmException e) {
			// impossible
			throw new Error(e);
		}
	}

	CryptoKey() {
	}

	public static CryptoKey read(InputStream i) throws IOException, CryptFormatException {
		DataInputStream dis = new DataInputStream(i);
		String type = dis.readUTF();
		try {
			Class<?> keyClass = Class.forName(type);
			Method m =
				keyClass.getMethod("read", new Class[] { InputStream.class });
			return (CryptoKey) m.invoke(null, dis);
		} catch (Exception e) {
			e.printStackTrace();
			if (e instanceof CryptFormatException)
				throw (CryptFormatException) e;
			if (e instanceof IOException)
				throw (IOException) e;
			Logger.error(CryptoKey.class, "Unknown exception while reading CryptoKey", e);
			return null;
		}
	}

//	public abstract void write(OutputStream o) throws IOException;

	public abstract String keyType();
	public abstract byte[] fingerprint();
	public abstract byte[] asBytes();

	protected byte[] fingerprint(BigInteger[] quantities) {
		synchronized (shactx) {
			for (BigInteger quantity: quantities) {
				byte[] mpi = Util.MPIbytes(quantity);
				shactx.update(mpi, 0, mpi.length);
			}
			return shactx.digest();
		}
	}

	public String verboseToString() {
		StringBuilder b = new StringBuilder();
		b.append(toString()).append('\t').append(fingerprintToString());
		return b.toString();
	}

	@Override
	public String toString() {
		StringBuilder b = new StringBuilder(keyType().length() + 1 + 4);
		b.append(keyType()).append('/');
		HexUtil.bytesToHexAppend(fingerprint(), 16, 4, b);
		return b.toString();
	}

//	protected void write(OutputStream o, String clazz) throws IOException {
//		UTF8.writeWithLength(o, clazz);
//	}
//
	public String fingerprintToString() {
		String fphex = HexUtil.bytesToHex(fingerprint());
		StringBuilder b = new StringBuilder(40 + 10);
		b
			.append(fphex.substring(0, 4))
			.append(' ')
			.append(fphex.substring(4, 8))
			.append(' ')
			.append(fphex.substring(8, 12))
			.append(' ')
			.append(fphex.substring(12, 16))
			.append(' ')
			.append(fphex.substring(16, 20))
			.append("  ")
			.append(fphex.substring(20, 24))
			.append(' ')
			.append(fphex.substring(24, 28))
			.append(' ')
			.append(fphex.substring(28, 32))
			.append(' ')
			.append(fphex.substring(32, 36))
			.append(' ')
			.append(fphex.substring(36, 40));
		return b.toString();
	}

	public static void main(String[] args) throws Exception {
		for (;;) {
			CryptoKey kp = CryptoKey.read(System.in);
			System.err.println("-+ " + kp.verboseToString());
		}
	}

}
