package com.fitbank.common.crypto;

import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.Cipher;
import javax.crypto.CipherOutputStream;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class Crypto implements Serializable {
    private static final long serialVersionUID = 1L;

    public static void main(String[] args) {
        try {
            if (args.length != 3) {
                throw new RuntimeException("Deben enviarse 3 parámetros: PHRASE, ALGORITMO, y CADENA A ENCRIPTAR (ejm: FIT-2008 DES holaMundo)");
            }

            Crypto de = new Crypto(args[0], args[1]);
            String cadena = args[2];
            cadena = de.encrypt(cadena);
            System.out.println("Cadena encriptada: " + cadena);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private Cipher    cipher;

    private String    algorithm;

    private SecretKey key;

    /**
     * Constructor.
     */
    public Crypto(String pPhrase, String pAlgorithm) {
        try {
            String phrase = pPhrase;
            this.algorithm = pAlgorithm;
            this.key = new SecretKeySpec(phrase.getBytes(), this.algorithm);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * Byte a Hexadecimal
     * 
     * @param b
     * @return
     */
    private String byteArrayToHexString(byte[] b) {
        StringBuffer sb = new StringBuffer(b.length * 2);
        for (byte element : b) {
            int v = element & 0xff;
            if (v < 16) {
                sb.append('0');
            }
            sb.append(Integer.toHexString(v));
        }
        return sb.toString().toUpperCase();
    }

    /**
     * Desencripta un texto
     * 
     * @param pText
     * @return
     * @throws Exception
     */
    public String decrypt(String pText) throws Exception {
        if ((pText == null) || (pText.compareTo("") == 0)) {
            return pText;
        }
        this.init(Cipher.DECRYPT_MODE);
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        OutputStream out = new CipherOutputStream(bout, this.cipher);
        out.write(this.hexStringToByteArray(pText));
        out.flush();
        out.close();
        return new String(bout.toByteArray());
    }

    /**
     * Encripta un texto
     * 
     * @param pText
     * @return
     * @throws Exception
     */
    public String encrypt(String pText) throws Exception {
        if ((pText == null) || (pText.compareTo("") == 0)) {
            return pText;
        }
        this.init(Cipher.ENCRYPT_MODE);
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        OutputStream out = new CipherOutputStream(bout, this.cipher);
        out.write(pText.getBytes());
        out.flush();
        out.close();
        return this.byteArrayToHexString(bout.toByteArray());
    }

    /**
     * Hexadecimal a String
     * 
     * @param s
     * @return
     */
    private byte[] hexStringToByteArray(String s) {
        byte[] b = new byte[s.length() / 2];
        for (int i = 0; i < b.length; i++) {
            int index = i * 2;
            int v = Integer.parseInt(s.substring(index, index + 2), 16);
            b[i] = (byte) v;
        }
        return b;
    }

    /**
     * Inicializa la clase
     * 
     * @param pType
     * @throws Exception
     */
    private void init(int pType) throws Exception {
        byte[] iv = new byte[] { (byte) 0x8E, 0x12, 0x39, (byte) 0x9C, 0x07, 0x72, 0x6F, 0x5A };
        AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);
        this.cipher = Cipher.getInstance(this.algorithm + "/CBC/PKCS5Padding");
        this.cipher.init(pType, this.key, paramSpec);
    }
}
