/* 
 * Copyright 2014 by AVM GmbH <info@avm.de>
 *
 * This software contains free software; you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License ("License") as 
 * published by the Free Software Foundation  (version 3 of the License). 
 * This software is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the copy of the 
 * License you received along with this software for more details.
 */

package de.avm.android.tr064.net;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;

import android.text.TextUtils;

public class Fingerprint
{
	public enum Type { SHA1, SHA256 };

	private static final String[] ALGORITHMS =
	{
		"SHA-1", "SHA-256"
	};

	private Type mType;
	private byte[] mFingerprint;
	
	public Type getType()
	{
		return mType;
	}
	
	public String getAlgorithm()
	{
		return ALGORITHMS[mType.ordinal()];
	}
	
	/**
	 * Create fingerprint of certificate bytes
	 * 
	 * @throws IllegalArgumentException
	 * @throws CertificateEncodingException
	 */
	public Fingerprint(X509Certificate certificate, Type type)
			throws IllegalArgumentException, CertificateEncodingException
	{
		if (certificate == null)
			throw new IllegalArgumentException("Argument certificate must not be null.");
		mType = type;
		makeFingerprint(certificate.getEncoded());
	}
	
	/**
	 * Create fingerprint of public key bytes
	 * 
	 * @throws IllegalArgumentException
	 * @throws CertificateEncodingException
	 */
	public Fingerprint(PublicKey publicKey, Type type)
			throws IllegalArgumentException, CertificateEncodingException
	{
		if (publicKey == null)
			throw new IllegalArgumentException("Argument publicKey must not be null.");
		mType = type;
		makeFingerprint(publicKey.getEncoded());
	}
	
	/**
	 * Instantiate from string
	 * 
	 * @param fingerprint compact String, formerly created by toString()
	 * @throws IllegalArgumentException
	 */
	public Fingerprint(String fingerprint)
			throws IllegalArgumentException
	{
		if (TextUtils.isEmpty(fingerprint))
			throw new IllegalArgumentException("Argument fingerprint must not be null or empty.");

		int pos = fingerprint.indexOf(TYPE_DELIMITER);
		if (pos < 1)
			throw new IllegalArgumentException("Illegal format of Argument fingerprint.");
		try
		{
			mType = Type.valueOf(fingerprint.substring(0, pos));
		}
		catch(IllegalArgumentException e)
		{
			throw new IllegalArgumentException(
					"Illegal format of Argument fingerprint.", e);
		}
		
		fingerprint = fingerprint.substring(pos + 1);
		if (TextUtils.isEmpty(fingerprint) || (fingerprint.length() % 2) != 0)
			throw new IllegalArgumentException("Illegal format of Argument fingerprint.");
		
		int len = fingerprint.length() / 2;
		mFingerprint = new byte[len];
		for (int ii = 0; ii < len; ii++)
		{
			try
			{
				int index = ii * 2;
				mFingerprint[ii] = (byte)Integer.parseInt(fingerprint
						.substring(index, index + 2), 16);
			}
			catch(NumberFormatException e)
			{
				throw new IllegalArgumentException(
						"Illegal format of Argument fingerprint.", e);
			}
		}
	}

	private void makeFingerprint(byte[] data)
			throws IllegalArgumentException
	{
		try
		{
			mFingerprint = MessageDigest.getInstance(getAlgorithm())
					.digest(data);
		}
		catch (NoSuchAlgorithmException e)
		{
			throw new IllegalArgumentException("No algorithm for type " +
					mType.toString() + ".", e);
		}
	}

	private static final char TYPE_DELIMITER = ':';
	private static final String USERFRIENDLY_DELIMITER = ":";
    private static final char[] HEXDIGITS =
    {
    	'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
    };

	/**
	 * Gets fingerprint as user friendly string
	 * to display
	 */
	public String toUserfriendlyString()
	{
        StringBuilder result = new StringBuilder(); 
        for (byte by : mFingerprint)
        {
        	if (result.length() > 0)
        		result.append(USERFRIENDLY_DELIMITER);
        	result.append(HEXDIGITS[(0xF0 & by) >>> 4]);
        	result.append(HEXDIGITS[0x0F & by]);
        }
        return result.toString();
	}
	
	/**
	 * Gets fingerprint as compact string
	 */
	public String toString()
	{
        StringBuilder result = new StringBuilder(); 
    	result.append(mType.toString());
		result.append(TYPE_DELIMITER);
        for (byte by : mFingerprint)
        {
        	result.append(HEXDIGITS[(0xF0 & by) >>> 4]);
        	result.append(HEXDIGITS[0x0F & by]);
        }
        return result.toString();
	}
	
	@Override
	public boolean equals(Object object)
	{
		if (object == null) return false;
		if (this == object) return true;
		if (!getClass().isAssignableFrom(object.getClass())) return false;

		if (mType != ((Fingerprint)object).mType) return false;
		
		byte[] fingerprint = ((Fingerprint)object).mFingerprint;
		if (mFingerprint.length != fingerprint.length) return false;

		for (int ii = 0; ii < fingerprint.length; ii++)
			if (mFingerprint[ii] != fingerprint[ii])
				return false;
		
		return true;
	}
	
	@Override
	public int hashCode()
	{
		throw new UnsupportedOperationException();
	}
}
