package project.common;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;

import org.apache.commons.codec.EncoderException;
import org.apache.commons.codec.net.BCodec;

import core.util.MojiUtil;

/**
 * エンコードユーティリティ
 *
 * @author Tadashi Nakayama
 * @version 1.0.0
 */
public final class CodecUtil {

	/**
	 * コンストラクタ
	 */
	private CodecUtil() {
		throw new AssertionError();
	}

	/**
	 * BCodecエンコード（日本語対応）
	 *
	 * @param str 対象文字列
	 * @param charset エンコード
	 * @return エンコード済文字列
	 */
	public static String encodeBCodec(final String str, final Charset charset) {
		final var enc = MojiUtil.CHARSET_JIS.equals(charset) ? MojiUtil.CHARSET_X_JIS : charset;
		try {
			final var ret = new BCodec(enc.name()).encode(MojiUtil.correctGarbled(str, charset));
			if (MojiUtil.CHARSET_JIS.equals(charset)) {
				return ret.replace(MojiUtil.CHARSET_X_JIS.name(), MojiUtil.CHARSET_JIS.name());
			}
			return ret;
		} catch (final EncoderException ex) {
			throw new IllegalStateException(ex);
		}
	}

	/**
	 * Base64エンコーディングする。
	 *
	 * @param str 文字データ
	 * @return 符号化文字列
	 */
	public static String encodeBase64(final String str) {
		return encodeBase64(str.getBytes(StandardCharsets.ISO_8859_1));
	}

	/**
	 * Base64エンコーディングする。
	 *
	 * @param data バイト型文字データ
	 * @return 符号化文字列
	 */
	public static String encodeBase64(final byte[] data) {
		return new String(Base64.getEncoder().encode(data), StandardCharsets.ISO_8859_1);
	}

	/**
	 * Base64デコーディングする。
	 *
	 * @param data 文字データ
	 * @return 復号化文字列
	 */
	public static byte[] decodeBase64(final String data) {
		return Base64.getDecoder().decode(data.getBytes(StandardCharsets.ISO_8859_1));
	}

	/**
	 * 引数文字列をLDAPのパスワード形式に変換する。
	 * @param str 文字データ
	 * @return 暗号化文字列
	 */
	public static String encryptPassword(final String str) {
		final var sha = "SHA-256";
		try {
			// SHAハッシュ化
			final var md = MessageDigest.getInstance(sha);
			md.update(str.getBytes(StandardCharsets.ISO_8859_1));
			return "{" + sha + "}" + encodeBase64(md.digest());
		} catch (final NoSuchAlgorithmException ex) {
			throw new IllegalStateException(ex);
		}
	}

	/**
	 * 文字列のダイジェストを16進文字列で返却する。
	 *
	 * @param str 元文字列
	 * @return ダイジェスト
	 */
	public static String toDigestString(final String str) {
		try {
			final var md = MessageDigest.getInstance("SHA-256");
			// バイト配列からダイジェストを計算する
			md.update(str.getBytes(StandardCharsets.ISO_8859_1));
			return toStringDigest(md.digest());
		} catch (final NoSuchAlgorithmException ex) {
			throw new IllegalStateException(ex);
		}
	}

	/**
	 * バイト配列を文字列に変換する。
	 *
	 * @param digest バイト配列
	 * @return 変換後文字列
	 */
	private static String toStringDigest(final byte[] digest) {
		// 返却値
		final var sb = new StringBuilder();
		for (final var d : digest) {
			sb.append(String.format("%02x", d));
		}
		return sb.toString();
	}
}
