package project.web;

import java.sql.Timestamp;
import java.util.Date;
import java.util.Map;
import java.util.Optional;

import core.config.Factory;
import online.context.ActionParameter;
import online.context.session.SessionUser;
import online.model.ModelUtil;
import online.model.UniModel;
import online.struts.BeanUtil;
import online.struts.action.UniForm;


/**
 * インスタンスFactory
 *
 * @author Tadashi Nakayama
 * @version 1.0.0
 */
public final class InstanceFactory {

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

	/**
	 * インスタンス作成
	 * @param <T> ジェネリックス
	 * @param cls サービスクラス
	 * @param model 汎用モデル
	 * @return インスタンス
	 */
	public static <T> T create(final Class<T> cls, final UniForm model) {
		final var ap = (model != null) ? model.getActionParameter() : null;
		final var su = Optional.ofNullable(model).map(UniForm::getSessionUser).orElse(null);
		return create(cls, model, ap, su);
	}

	/**
	 * インスタンス作成
	 * @param <T> ジェネリックス
	 * @param cls サービスクラス
	 * @param model 汎用モデル
	 * @param ap アクションパラメタ
	 * @param su セションユーザ情報
	 * @return インスタンス
	 */
	public static <T> T create(final Class<T> cls, final UniModel model,
			final ActionParameter ap, final SessionUser su) {
		final T instance = Factory.create(cls);

		if (model != null) {
			ModelUtil.setModelValue(instance, model);
			setObjectTo(instance, model, "setUniModel", UniModel.class);
		}
		if (ap != null) {
			BeanUtil.setPropertiesTo(instance, ap.getProperties());
			setObjectTo(instance, ap.getProperties(), "setProperty", Map.class);
			setObjectTo(instance, ap.getDateTime(), "setDateTime", Date.class);
			setObjectTo(instance, ap.getDateTime(), "setDateTime", Timestamp.class);
			setObjectTo(instance, ap.getIp(), "setIp", String.class);
			setObjectTo(instance, ap, "setActionParameter", ActionParameter.class);
		}
		if (su != null) {
			BeanUtil.setPropertiesTo(instance, su.getAttributeMap());
			setObjectTo(instance, su.getAttributeMap(), "setAttributeMap", Map.class);
			setObjectTo(instance, su.getUid(), "setUid", String.class);
			setObjectTo(instance, su, "setSessionUser", SessionUser.class);
		}

		return instance;
	}

	/**
	 * Beanにオブジェクトを設定します。
	 * @param bean 設定対象Bean
	 * @param obj 設定モデル
	 * @param method 設定メソッド名
	 * @param param パラメタクラス
	 */
	private static void setObjectTo(final Object bean, final Object obj,
			final String method, final Class<?>... param) {
		final var m = Factory.getMethod(bean.getClass(), method, param);
		Factory.invoke(bean, m, obj);
	}
}
