package com.yuji.ec.utility;

import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;

import com.yuji.ec.base64.Base64;

public class Encryption {
	private static Encryption instance = null;
	private static final byte[] SALT = new byte[] { -47, 66, 32, -127, 98, -12,
			4, 113, -24, -111, 23, 121, -97 };

	private static final byte[] IV = { 16, 74, 71, -80, 32, 101, -47, 72, 117,
			-14, 0, -29, 70, 65, -12, 74 };

	private SecretKey secretKey = null;

	private Encryption(){
		
	}
	
	public static Encryption getInstance(){
		if (instance == null){
			instance = new Encryption();
		}
		return instance;
	}
	
	public String getEncrypted(Context context, String contents) {
		byte[] encoded = null;
		try {
			if (secretKey == null){
				secretKey = generateKey(context);
			}

			byte[] encrypted = encrypt(contents.getBytes(), secretKey);

			encoded = Base64.encode(encrypted);
		} catch (NoSuchAlgorithmException e) {
			;
		} catch (InvalidKeySpecException e) {
			;
		} catch (InvalidKeyException e) {
			;
		} catch (NoSuchPaddingException e) {
			;
		} catch (InvalidAlgorithmParameterException e) {
			;
		} catch (IllegalBlockSizeException e) {
			;
		} catch (BadPaddingException e) {
			;
		}
		
		return new String(encoded);
	}

	public String getDecrypted(Context context, String encoded){
		byte[] decrypted = null;
		try {
			if (secretKey == null){
				secretKey = generateKey(context);
			}
			byte[] crypted = Base64.decode(encoded.getBytes());
			
			decrypted = decrypt(crypted, secretKey);
		} catch (NoSuchAlgorithmException e) {
			;
		} catch (InvalidKeySpecException e) {
			;
		} catch (InvalidKeyException e) {
			;
		} catch (NoSuchPaddingException e) {
			;
		} catch (InvalidAlgorithmParameterException e) {
			;
		} catch (IllegalBlockSizeException e) {
			;
		} catch (BadPaddingException e) {
			;
		}
		return (decrypted != null)? new String(decrypted) : null;
	}
			
	private byte[] encrypt(byte[] src, Key key) throws NoSuchAlgorithmException,
			NoSuchPaddingException, InvalidKeyException,
			InvalidAlgorithmParameterException, IllegalBlockSizeException,
			BadPaddingException {
		Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

		cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(IV));

		byte[] encrypted = cipher.doFinal(src);

		return encrypted;
	}

	private byte[] decrypt(byte[] src, Key key) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException{
		Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

		cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(IV));

		byte[] decrypted = cipher.doFinal(src);

		return decrypted;
	}
	
	private SecretKey generateKey(Context context)
			throws NoSuchAlgorithmException, InvalidKeySpecException {
		char[] password = generatePassword(context);

		KeySpec keySpec = new PBEKeySpec(password, SALT, 1024, 256);
		SecretKeyFactory factory = SecretKeyFactory
				.getInstance("PBEWITHSHAAND256BITAES-CBC-BC");
		SecretKey secretKey = factory.generateSecret(keySpec);

		return secretKey;
	}

	private char[] generatePassword(Context context) {
		PackageInfo packageInfo;
		StringBuffer sb = new StringBuffer();
		try {
			packageInfo = context.getPackageManager().getPackageInfo(
					context.getPackageName(), PackageManager.GET_SIGNATURES);
			Signature[] signatures = packageInfo.signatures;

			for (int i = 0; i < signatures.length; i++) {
				Signature signature = packageInfo.signatures[i];
				char[] c = signature.toChars();
				sb.append(c);
			}
		} catch (NameNotFoundException e) {
			return null;
		}
		
		String s = sb.toString();
		if (s.length() > 10){
			s = s.substring(0, 10);
		}
		return s.toCharArray();
	}
}
