package com.lz.common.utils;

import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

/**
 *
 * @类DigestUtil.java 的实现描述：加解密
 * @author 陈金虎 2017年1月16日 下午11:29:26
 * @注意：本内容仅限于杭州霖梓网络科技有限公司内部传阅，禁止外泄以及用于其他的商业目的
 */
public class DigestUtil {

	private static final Logger logger = LoggerFactory.getLogger(DigestUtil.class);

	public static final String SHA1 = "SHA-1";
	public static final int DEFAULT_BUFFER_LENGTH = 8 * 1024;
	public static final int DEFAULT_BYTES_SIZE = 8;
	public static final int DEFAULT_DIGEST_TIMES = 1024;

	/**
	 * 获取16位MD5码
	 *
	 * @param md5Str
	 * @return
	 */
	public static String MD5_16(String md5Str) {
		String md5 = MD5(md5Str);
		return StringUtils.substring(md5, 8, 24);
	}

	/**
	 * 32位Md5码
	 *
	 * @param md5Str
	 * @return
	 */
	public static String MD5(String md5Str) {
		if (StringUtils.isBlank(md5Str)) {
			return null;
		}
		return DigestUtils.md5Hex(md5Str);
	}

	public static String getSha1Code(MultipartFile fileObj) throws IOException {
		if (null == fileObj) {
			return StringUtils.EMPTY;
		}
		InputStream ins = null;
		try {
			ins = fileObj.getInputStream();
			return DigestUtils.sha1Hex(ins);
		} catch (Exception e) {
			return StringUtils.EMPTY;
		} finally {
			if (null != ins) {
				ins.close();
			}
		}
	}

	public static String getMd5(String pathname) throws IOException {
		if (StringUtils.isBlank(pathname)) {
			return StringUtils.EMPTY;
		}
		InputStream ins = null;
		try {
			ins = new FileInputStream(pathname);
			return DigestUtils.md5Hex(ins);
		} catch (Exception e) {
			return StringUtils.EMPTY;
		} finally {
			if (null != ins) {
				ins.close();
			}
		}
	}

	/**
	 * 计算sha256值
	 *
	 * @param signStrBefore
	 *            需要计算的字符串
	 * @return
	 */
	public static String getDigestStr(String signStrBefore) {
		try {
			MessageDigest md = MessageDigest.getInstance("SHA-256");
			md.update(signStrBefore.getBytes());
			String tempStr = null;
			StringBuilder stb = new StringBuilder();
			byte[] origBytes = md.digest();
			for (int i = 0; i < origBytes.length; i++) {
				// 这里按位与是为了把字节转整时候取其正确的整数，java中一个int是4个字节
				// 如果origBytes[i]最高位为1，则转为int时，int的前三个字节都被1填充了
				tempStr = Integer.toHexString(origBytes[i] & 0xff);
				if (tempStr.length() == 1) {
					stb.append("0");
				}
				stb.append(tempStr);

			}
			return stb.toString();
		} catch (NoSuchAlgorithmException e) {
			 e.printStackTrace();
		}
		return null;
	}

	public static String SHA1(String decript) {
		try {
			MessageDigest digest = MessageDigest.getInstance("SHA-1");
			digest.update(decript.getBytes());
			byte messageDigest[] = digest.digest();
			// Create Hex String
			StringBuffer hexString = new StringBuffer();
			// 字节数组转换为 十六进制 数
			for (int i = 0; i < messageDigest.length; i++) {
				String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
				if (shaHex.length() < 2) {
					hexString.append(0);
				}
				hexString.append(shaHex);
			}
			return hexString.toString();

		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		return "";
	}

	// public static String getSha1Code(String pathname) throws IOException {
	// if (StringUtils.isBlank(pathname)) {
	// return StringUtils.EMPTY;
	// }
	// InputStream ins = null;
	// try {
	// ins = new FileInputStream(pathname);
	// return getSha1Code(ins);
	// } catch (Exception e) {
	// return StringUtils.EMPTY;
	// } finally {
	// if (null != ins) {
	// ins.close();
	// }
	// }
	// }

	// public static String getMd5(File file) throws IOException {
	// if (null == file) {
	// return StringUtils.EMPTY;
	// }
	// InputStream ins = null;
	// try {
	// ins = new FileInputStream(file);
	// return getMd5(ins);
	// } catch (Exception e) {
	// return StringUtils.EMPTY;
	// } finally {
	// if (null != ins) {
	// ins.close();
	// }
	// }
	// }

	// public static String getSha1Code(File file) throws IOException {
	// if (null == file) {
	// return StringUtils.EMPTY;
	// }
	// InputStream ins = null;
	// try {
	// ins = new FileInputStream(file);
	// return getSha1Code(ins);
	// } catch (Exception e) {
	// return StringUtils.EMPTY;
	// } finally {
	// if (null != ins) {
	// ins.close();
	// }
	// }
	// }

	public static String getSha1Code(byte[] byteArray) throws IOException {
		if (null == byteArray || byteArray.length < 1) {
			return StringUtils.EMPTY;
		}
		return DigestUtils.sha1Hex(byteArray);
	}

	/**
	 * get Sha1 Code with inputStream
	 *
	 * @param iStream
	 * @return
	 * @throws IOException
	 */
	// public static String getSha1Code(InputStream iStream) throws IOException {
	// if (null == iStream) {
	// return StringUtils.EMPTY;
	// }
	// return DigestUtils.sha1Hex(iStream);
	// }

	/**
	 * Generate Sha1 Code with inputStream
	 *
	 * @param iStream
	 * @return
	 * @throws IOException
	 */
	// public static String getMd5(InputStream iStream) throws IOException {
	// if (null == iStream) {
	// return StringUtils.EMPTY;
	// }
	// return DigestUtils.md5Hex(iStream);
	// }

	/**
	 * 根据流获取文件的sha1值
	 *
	 * @param in
	 * @return
	 * @throws OutOfMemoryError
	 * @throws IOException
	 */
	// public static String getFileSha1(InputStream in) throws OutOfMemoryError, IOException {
	// try {
	// return byte2hex(digest(in, SHA1));
	// } catch (OutOfMemoryError e) {
	// e.printStackTrace();
	// throw e;
	// } finally {
	// if (null != in) {
	// in.close();
	// }
	// }
	// }

	/**
	 * 对字符串进行给定算法散列(无盐值)
	 *
	 * @param bytes
	 * @param algorithm
	 * @return
	 */
	public static byte[] digestString(byte[] bytes, String algorithm) {
		return digest(bytes, algorithm, null, 1);
	}

	/**
	 * 对字符串进行给定算法散列(包含盐值)
	 *
	 * @param bytes
	 * @param algorithm
	 * @return
	 */
	public static byte[] digestString(byte[] bytes, byte[] salt, String algorithm) {
		return digest(bytes, algorithm, salt, 1);
	}

	/**
	 * 对字符串进行给定次数和指定算法的散列(包含盐值)
	 *
	 * @param bytes
	 * @param algorithm
	 * @return
	 */
	public static byte[] digestString(byte[] bytes, byte[] salt, int counts, String algorithm) {
		return digest(bytes, algorithm, salt, counts);
	}

	/**
	 * 对字符串进行给定次数和指定算法的散列(包含盐值)
	 *
	 * @param bytes
	 * @param algorithm
	 * @return
	 */
	public static String digestString(String password, String salt) {
		byte[] saltBytes = salt.getBytes();
		byte[] pwdBytes = digest(getStringByte(password), SHA1, saltBytes, DEFAULT_DIGEST_TIMES);
		return encodeHex(pwdBytes);
	}

	public static byte[] getStringByte(String str) {
		if (str != null && !"".equals(str.trim())) {
			try {
				return str.getBytes("UTF-8");
			} catch (UnsupportedEncodingException e) {
				return null;
			}
		} else {
			return null;
		}
	}

	/**
	 * 生成随机salt的字节数组
	 *
	 * @param num
	 * @return
	 */
	public static byte[] generateSalt(int num) {
		Validate.isTrue(num > 0, "num argument must be a positive integer, larger than 0", num);

		byte[] bytes = new byte[num];
		SecureRandom random = new SecureRandom();
		random.nextBytes(bytes);
		return bytes;
	}

	/**
	 * 对文件进行指定算法的散列散列
	 *
	 * @param bytes
	 * @return
	 * @throws IOException
	 */
	public static byte[] digestFile(InputStream bytes, String algorithm) throws IOException {
		return digest(bytes, algorithm);
	}

	private static byte[] digest(byte[] bytes, String algorithm, byte[] salt, int counts) {
		try {
			MessageDigest digest = MessageDigest.getInstance(algorithm);
			if (salt != null) {
				digest.update(salt);
			}

			byte[] result = digest.digest(bytes);

			for (int i = 1; i < counts; i++) {
				digest.reset();
				result = digest.digest(result);
			}
			return result;
		} catch (GeneralSecurityException e) {
			logger.error("general security exception occurs, detail exception is ", e);
			return null;
		}
	}

	private static byte[] digest(InputStream input, String algorithm) throws IOException {
		try {
			MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
			byte[] buffer = new byte[DEFAULT_BUFFER_LENGTH];
			int read = input.read(buffer, 0, DEFAULT_BUFFER_LENGTH);

			while (read > -1) {
				messageDigest.update(buffer, 0, read);
				read = input.read(buffer, 0, DEFAULT_BUFFER_LENGTH);
			}

			return messageDigest.digest();
		} catch (GeneralSecurityException e) {
			logger.error("general security exception occurs when digest inputstream ", e);
			return null;
		}
	}

	/**
	 * byte[]字节组转为字符串
	 *
	 * @param b
	 * @return
	 */
	// private static String byte2hex(byte[] b) {
	// String hs = "";
	// String stmp = "";
	// for (int n = 0; n < b.length; n++) {
	// stmp = (java.lang.Integer.toHexString(b[n] & 0XFF));
	// if (stmp.length() == 1) {
	// hs = hs + "0" + stmp;
	// } else {
	// hs = hs + stmp;
	// }
	// if (n < b.length - 1) {
	// hs = hs + ":";
	// }
	// }
	// return hs.toUpperCase(Locale.ENGLISH);
	// }

	/**
	 * decode String to bytes[]
	 *
	 * @param src
	 * @return
	 */
	public static byte[] decodeHex(String src) {
		if (StringUtils.isBlank(src)) {
			return null;
		}
		try {
			return Hex.decodeHex(src.toCharArray());
		} catch (DecoderException e) {
			logger.error("decode hex src failed, src vlaue is " + src, e);
		}
		return null;
	}

	public static String encodeHex(byte[] data) {
		return Hex.encodeHexString(data);
	}

	public static void main(String[] args) {
		// byte[] saltBytes = DigestUtil.generateSalt(DEFAULT_BYTES_SIZE);
		// String salt = DigestUtil.encodeHex(saltBytes);
		// System.out.println(salt);
		System.out.println(DigestUtil.getDigestStr("hello1234"));

	}

	// public static void main(String[] args) {
	// byte[] salts = generateSalt(8);
	// String saltRaw = "d2eb9f693820ce5d";
	// byte[] saltsRawBytes = CodecUtil.decodeHex(saltRaw);
	// System.out.println(saltsRawBytes);
	// String passwordRaw = "hello1234";
	// byte[] hashPasswordRaw = digestString(passwordRaw.getBytes(), saltsRawBytes, 1024, ConstantPool.SHA1);
	// System.out.println(CodecUtil.encodeHex(hashPasswordRaw));
	//
	// System.out.println(salts);
	// String salt = CodecUtil.encodeHex(salts);
	// System.out.println(salt);
	// byte[] salts2 = CodecUtil.decodeHex(salt);
	// System.out.println(salts2);
	// System.out.println(CodecUtil.encodeHex(salts2));
	// String password = "123456";
	// byte[] hashPassword = digestString(password.getBytes(), salts, 1024, ConstantPool.SHA1);
	// System.out.println(CodecUtil.encodeHex(hashPassword));
	// byte[] hashPassword2 = digestString(password.getBytes(), salts2, 1024, ConstantPool.SHA1);
	// System.out.println(CodecUtil.encodeHex(hashPassword2));
	// }

}
