package org.scriptonbasestar.tool.data.jpa.usertype;

import com.google.common.base.Objects;
import org.apache.commons.codec.binary.StringUtils;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.type.StringType;
import org.hibernate.usertype.ParameterizedType;
import org.hibernate.usertype.UserType;
import org.scriptonbasestar.tool.crypto.symmetry.RC2;
import org.scriptonbasestar.tool.crypto.symmetry.SBSymmetryService;

import java.io.Serializable;
import java.nio.charset.Charset;
import java.nio.charset.IllegalCharsetNameException;
import java.nio.charset.UnsupportedCharsetException;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Properties;

/**
 * @author archmagece
 * @since 2017-09-07
 */
public class RC2UserType implements UserType, ParameterizedType {

	public static final String TYPE = "org.scriptonbasestar.tool.data.jpa.usertype.RC2UserType";

	public static final String PARAM_CHARSET = "charset";

	public static final Charset DEFAULT_CHARSET = Charset.forName("UTF8");

	public static final int SQL_TYPE = Types.VARCHAR;

	private final SBSymmetryService byteEncryptor = new RC2("r390prifk0p2i03082");

	protected Charset charset;

	@Override
	public void setParameterValues(Properties parameters) {
		if (parameters != null) {
			String tmp = parameters.getProperty(PARAM_CHARSET);
			if (tmp != null) {
				try {
					charset = Charset.forName(tmp);
				} catch (IllegalCharsetNameException x) {
					throw new HibernateException("Unsupported character set " + tmp + ": " + x.getMessage(), x);
				} catch (UnsupportedCharsetException x) {
					throw new HibernateException("Unsupported character set " + tmp + ": " + x.getMessage(), x);
				}
			}
		}
		if (charset == null) {
			charset = DEFAULT_CHARSET;
		}
	}

	@Override
	public int[] sqlTypes() {
		return new int[] { StringType.INSTANCE.sqlType() };
	}

	@Override
	public Class returnedClass() {
		return String.class;
	}

	@Override
	public boolean equals(Object x, Object y) throws HibernateException {
		return Objects.equal(x, y);
	}

	@Override
	public int hashCode(Object x) throws HibernateException {
		return Objects.hashCode(x);
	}

	@Override
	public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner) throws HibernateException, SQLException {
		try {
			String hexStr = StringType.INSTANCE.nullSafeGet(rs, names[0], session);
			return StringUtils.newStringUtf8(byteEncryptor.decrypt(hexStr.getBytes()));
		} catch (Exception ex) {
			throw new HibernateException("암호화를 복원하는데 실패했습니다.", ex);
		}
	}

	@Override
	public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session) throws HibernateException, SQLException {
		try {
			StringType.INSTANCE.nullSafeSet(st, byteEncryptor.encrypt(((String) value).getBytes()), index, session);
		} catch (Exception ex) {
			throw new HibernateException("암호화를 수행하는데 실패했습니다.", ex);
		}
	}

	@Override
	public Object deepCopy(Object value) throws HibernateException {
		return value;
	}

	@Override
	public boolean isMutable() {
		return StringType.INSTANCE.isMutable();
	}

	@Override
	public Serializable disassemble(Object value) throws HibernateException {
		return (Serializable) deepCopy(value);
	}

	@Override
	public Object assemble(Serializable cached, Object owner) throws HibernateException {
		return deepCopy(cached);
	}

	@Override
	public Object replace(Object original, Object target, Object owner) throws HibernateException {
		return deepCopy(original);
	}
}

@@ -86,16 +86,60 @@
 bjects;%0A
+import org.apache.commons.codec.binary.Hex;%0A
 import o
@@ -2577,16 +2577,59 @@
 ssion);%0A
+%09%09%09if(hexStr==null)%7B%0A%09%09%09%09return null;%0A%09%09%09%7D%0A
 %09%09%09retur
@@ -2960,32 +2960,53 @@
 ption %7B%0A%09%09try %7B%0A
+%09%09%09if(value==null)%7B%0A%09
 %09%09%09StringType.IN
@@ -3028,16 +3028,109 @@
 Set(st, 
+null, index, session);%0A%09%09%09%7Delse%7B%0A%09%09%09%09StringType.INSTANCE.nullSafeSet(st, Hex.encodeHexString(
 byteEncr
@@ -3169,16 +3169,24 @@
 etBytes(
+%22UTF-8%22)
 )), inde
@@ -3198,16 +3198,21 @@
 ssion);%0A
+%09%09%09%7D%0A
 %09%09%7D catc

improve null safe