package online.struts.action;

import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.upload.FormFile;

import online.context.ActionParameter;
import online.context.session.SessionAttributeUtil;
import online.context.session.SessionScope;
import online.context.session.SessionUser;
import online.filter.FilterUtil;
import online.model.UniModel;

/**
 * 汎用フォーム
 *
 * @author Tadashi Nakayama
 * @version 1.0.0
 */
public final class UniForm extends ActionForm implements UniModel {
	/** serialVersionUID */
	private static final long serialVersionUID = 1L;

	/** アクションパラメタ */
	private ActionParameter ap = null;
	/** セション属性 */
	private SessionScope attr = null;
	/** 共通情報 */
	private SessionUser common = null;
	/** 汎用データモデル */
	private UniModel um = null;
	/** マップ優先変更フラグ */
	private boolean reverse = false;
	/** クエリ文字列 */
	private String query = null;
	/** ロールバック対象モデル名 */
	private String rollback = null;

	/**
	 * このフォームの内容を初期化する。
	 *
	 * @param mapping アクションマッピング
	 * @param request サーブレットリクエスト
	 */
	@Override
	public void reset(final ActionMapping mapping, final HttpServletRequest request) {
		this.query = SessionAttributeUtil.stripSid(FilterUtil.getQueryString(request));
		this.reverse = false;
		this.rollback = null;
	}

	/**
	 * FormFile取得
	 * @param name 項目名
	 * @return FormFile
	 */
	public List<FormFile> getFormFile(final String name) {
		final var mrh = getMultipartRequestHandler();
		return Optional.ofNullable(mrh).map(m -> m.getFileElements().get(name)).orElse(null);
	}

	/**
	 * パラメタ文字列存在判断
	 *
	 * @return パラメタ文字列が存在した場合 true を返す。
	 */
	public boolean hasQueryString() {
		return !Objects.toString(this.query, "").isEmpty();
	}

	/**
	 * パラメタ文字列取得
	 *
	 * @return パラメタ文字列
	 */
	public String getQueryString() {
		return this.query;
	}

	/**
	 * 値設定
	 * @param model モデル
	 */
	public void populate(final UniModel model) {
		this.um = model;
	}

	/**
	 * マップ優先変更設定
	 */
	public void setReverse() {
		this.reverse = true;
	}

	/**
	 * マップ優先変更取得
	 * @return 優先変更の場合 true を返す。
	 */
	public boolean isReverse() {
		return this.reverse;
	}

	/**
	 * ロールバックモデル名設定
	 * @param key ロールバックモデル名
	 */
	public void setRollbackTo(final String key) {
		this.rollback = key;
	}

	/**
	 * ロールバックモデル名取得
	 * @return ロールバックモデル名
	 */
	public String getRollbackTo() {
		return this.rollback;
	}

	/**
	 * セション属性取得
	 *
	 * @return セション属性
	 */
	public SessionScope getSessionAttribute() {
		return this.attr;
	}

	/**
	 * セション属性設定
	 *
	 * @param val セション属性
	 */
	public void setSessionAttribute(final SessionScope val) {
		this.attr = val;
	}

	/**
	 * 共通情報モデル取得
	 *
	 * @return 共通情報モデル
	 */
	public SessionUser getSessionUser() {
		return this.common;
	}

	/**
	 * 共通情報モデル設定
	 *
	 * @param val 共通情報モデル
	 */
	public void setSessionUser(final SessionUser val) {
		this.common = val;
	}

	/**
	 * アクションパラメタ取得
	 *
	 * @return アクションパラメタ
	 */
	public ActionParameter getActionParameter() {
		return this.ap;
	}

	/**
	 * アクションパラメタ設定
	 *
	 * @param val アクションパラメタ
	 */
	public void setActionParameter(final ActionParameter val) {
		this.ap = val;
	}

	/**
	 * 保存可状態取得
	 *
	 * @return 保存可能の場合 true を返す。
	 */
	@Override
	public boolean isReservable() {
		return this.um.isReservable();
	}

	/**
	 * キー値保存
	 *
	 * @param key キー
	 */
	@Override
	public void reserve(final String... key) {
		this.um.reserve(key);
	}

	/**
	 * 保存モデル取得
	 *
	 * @return 保存モデル
	 */
	@Override
	public Set<String> getReservedKeySet() {
		return this.um.getReservedKeySet();
	}

	/**
	 * 全ての値とキーを組にした文字列を返す。
	 *
	 * @return 文字列
	 */
	@Override
	public String toString() {
		return Objects.toString(this.um, "");
	}

	/**
	 * キーに対応した値を保持する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void setValue(final String key, final Boolean value) {
		this.um.setValue(key, value);
	}

	/**
	 * キーに対応した値を保持する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void setValue(final String key, final Boolean... value) {
		this.um.setValue(key, value);
	}

	/**
	 * キーに対応した値を保持する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void setValue(final String key, final String value) {
		this.um.setValue(key, value);
	}

	/**
	 * キーに対応した値を保持する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void setValue(final String key, final String... value) {
		this.um.setValue(key, value);
	}

	/**
	 * キーに対応した値を保持する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void setValue(final String key, final Byte value) {
		this.um.setValue(key, value);
	}

	/**
	 * キーに対応した値を保持する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void setValue(final String key, final Byte... value) {
		this.um.setValue(key, value);
	}

	/**
	 * キーに対応した値を保持する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void setValue(final String key, final Short value) {
		this.um.setValue(key, value);
	}

	/**
	 * キーに対応した値を保持する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void setValue(final String key, final Short... value) {
		this.um.setValue(key, value);
	}

	/**
	 * キーに対応した値を保持する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void setValue(final String key, final Integer value) {
		this.um.setValue(key, value);
	}

	/**
	 * キーに対応した値を保持する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void setValue(final String key, final Integer... value) {
		this.um.setValue(key, value);
	}

	/**
	 * キーに対応した値を保持する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void setValue(final String key, final Long value) {
		this.um.setValue(key, value);
	}

	/**
	 * キーに対応した値を保持する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void setValue(final String key, final Long... value) {
		this.um.setValue(key, value);
	}

	/**
	 * キーに対応した値を保持する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void setValue(final String key, final Float value) {
		this.um.setValue(key, value);
	}

	/**
	 * キーに対応した値を保持する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void setValue(final String key, final Float... value) {
		this.um.setValue(key, value);
	}

	/**
	 * キーに対応した値を保持する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void setValue(final String key, final Double value) {
		this.um.setValue(key, value);
	}

	/**
	 * キーに対応した値を保持する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void setValue(final String key, final Double... value) {
		this.um.setValue(key, value);
	}

	/**
	 * キーに対応した値を保持する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void setValue(final String key, final BigInteger value) {
		this.um.setValue(key, value);
	}

	/**
	 * キーに対応した値を保持する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void setValue(final String key, final BigInteger... value) {
		this.um.setValue(key, value);
	}

	/**
	 * キーに対応した値を保持する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void setValue(final String key, final BigDecimal value) {
		this.um.setValue(key, value);
	}

	/**
	 * キーに対応した値を保持する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void setValue(final String key, final BigDecimal... value) {
		this.um.setValue(key, value);
	}

	/**
	 * キーに対応した値を保持する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void setValue(final String key, final Number value) {
		this.um.setValue(key, value);
	}

	/**
	 * キーに対応した値を保持する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void setValue(final String key, final Number... value) {
		this.um.setValue(key, value);
	}

	/**
	 * キーに対応した値を保持する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void setValue(final String key, final Date value) {
		this.um.setValue(key, value);
	}

	/**
	 * キーに対応した値を保持する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void setValue(final String key, final Date... value) {
		this.um.setValue(key, value);
	}

	/**
	 * キーに対応した値を保持する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void setValue(final String key, final UniModel value) {
		this.um.setValue(key, value);
	}

	/**
	 * キーに対応した値を取得する。
	 *
	 * @param key キー
	 * @return 値
	 */
	@Override
	public Boolean getBoolean(final String key) {
		return this.um.getBoolean(key);
	}

	/**
	 * キーに対応した値を取得する。
	 *
	 * @param key キー
	 * @return 値
	 */
	@Override
	public Boolean[] getBooleanArray(final String key) {
		return this.um.getBooleanArray(key);
	}

	/**
	 * キーに対応した値を取得する。
	 * 指定したキーに対応した値がnullの場合、空文字列を返す。
	 * 指定したキーの値が配列の場合、添字0の値を返す。
	 * 添字0が存在しない場合、空文字列を返す。
	 *
	 * @param key キー
	 * @return 値
	 */
	@Override
	public String getString(final String key) {
		return this.um.getString(key);
	}

	/**
	 * キーに対応した値を取得する。
	 * 指定したキーに対応した値がnullの場合、空配列を返す。
	 *
	 * @param key キー
	 * @return 値
	 */
	@Override
	public String[] getStringArray(final String key) {
		return this.um.getStringArray(key);
	}

	/**
	 * キーに対応した値を取得する。
	 * 指定したキーの値が配列の場合、添字0の値を返す。
	 * 添字0が存在しない場合、nullを返す。
	 * @param <T> Type
	 * @param key キー
	 * @return 値
	 */
	@Override
	public <T extends Number> T getNumber(final String key) {
		return this.um.getNumber(key);
	}

	/**
	 * キーに対応した値を取得する。
	 *
	 * @param key キー
	 * @return 値
	 */
	@Override
	public Number[] getNumberArray(final String key) {
		return this.um.getNumberArray(key);
	}

	/**
	 * キーに対応した値を取得する。
	 * 指定したキーの値が配列の場合、添字0の値を返す。
	 * 添字0が存在しない場合、nullを返す。
	 * @param <T> Type
	 * @param key キー
	 * @return 値
	 */
	@Override
	public <T extends Date> T getDate(final String key) {
		return this.um.getDate(key);
	}

	/**
	 * キーに対応した値を取得する。
	 *
	 * @param key キー
	 * @return 値
	 */
	@Override
	public Date[] getDateArray(final String key) {
		return this.um.getDateArray(key);
	}

	/**
	 * キーに対応した値を取得する。
	 *
	 * @param key キー
	 * @return 値
	 */
	@Override
	public UniModel getModel(final String key) {
		return this.um.getModel(key);
	}

	/**
	 * 内容を消去する。
	 *
	 */
	@Override
	public void clear() {
		this.um.clear();
	}

	/**
	 * キーが含まれているかを返す。
	 *
	 * @param key キー
	 * @return boolean
	 */
	@Override
	public boolean containsKey(final String key) {
		return this.um.containsKey(key);
	}

	/**
	 * キーの集合を返す。
	 *
	 * @return キー集合
	 */
	@Override
	public Set<String> keySet() {
		return this.um.keySet();
	}

	/**
	 * 他のモデルの内容を全て設定する。
	 *
	 * @param value モデル
	 */
	@Override
	public void putAll(final UniModel value) {
		this.um.putAll(value);
	}

	/**
	 * 他のモデルの内容を全て追加する。
	 *
	 * @param value モデル
	 */
	@Override
	public void addAll(final UniModel value) {
		this.um.addAll(value);
	}

	/**
	 * キーに対応した値を削除する。
	 *
	 * @param key キー
	 */
	@Override
	public void remove(final String... key) {
		this.um.remove(key);
	}

	/**
	 * 別名キーを作成する。
	 *
	 * @param key キー
	 * @param alias 別名キー
	 */
	@Override
	public void aliasKey(final String key, final String alias) {
		this.um.aliasKey(key, alias);
	}

	/**
	 * 空値設定
	 *
	 * @param key キー
	 */
	@Override
	public void noValue(final String... key) {
		this.um.noValue(key);
	}

	/**
	 * 空値確認
	 * @param key キー
	 * @return 値が存在して、長さ1以上の配列の場合 true を返す。
	 */
	@Override
	public boolean hasValue(final String key) {
		return this.um.hasValue(key);
	}

	/**
	 * 保持している値の数を返す。
	 *
	 * @return 保持数
	 */
	@Override
	public int size() {
		return this.um.size();
	}

	/**
	 * 空判断
	 * @return sizeが0の場合 true を返す。
	 */
	@Override
	public boolean isEmpty() {
		return this.um.isEmpty();
	}

	/**
	 * キーに対応した値の配列長を返す。
	 *
	 * @param key キー
	 * @return 配列長
	 */
	@Override
	public int getArraySize(final String key) {
		return this.um.getArraySize(key);
	}

	/**
	 * キーに対応した値のクラスを返す。
	 *
	 * @param key キー
	 * @return クラス
	 */
	@Override
	public <T> Class<T> getValueClass(final String key) {
		return this.um.getValueClass(key);
	}

	/**
	 * キーに対応した値を追加する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void addValue(final String key, final Boolean value) {
		this.um.addValue(key, value);
	}

	/**
	 * キーに対応した値を追加する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void addValue(final String key, final Boolean... value) {
		this.um.addValue(key, value);
	}

	/**
	 * キーに対応した値を追加する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void addValue(final String key, final String value) {
		this.um.addValue(key, value);
	}

	/**
	 * キーに対応した値を追加する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void addValue(final String key, final String... value) {
		this.um.addValue(key, value);
	}

	/**
	 * キーに対応した値を追加する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void addValue(final String key, final Byte value) {
		this.um.addValue(key, value);
	}

	/**
	 * キーに対応した値を追加する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void addValue(final String key, final Byte... value) {
		this.um.addValue(key, value);
	}

	/**
	 * キーに対応した値を追加する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void addValue(final String key, final Short value) {
		this.um.addValue(key, value);
	}

	/**
	 * キーに対応した値を追加する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void addValue(final String key, final Short... value) {
		this.um.addValue(key, value);
	}

	/**
	 * キーに対応した値を追加する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void addValue(final String key, final Integer value) {
		this.um.addValue(key, value);
	}

	/**
	 * キーに対応した値を追加する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void addValue(final String key, final Integer... value) {
		this.um.addValue(key, value);
	}

	/**
	 * キーに対応した値を追加する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void addValue(final String key, final Long value) {
		this.um.addValue(key, value);
	}

	/**
	 * キーに対応した値を追加する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void addValue(final String key, final Long... value) {
		this.um.addValue(key, value);
	}

	/**
	 * キーに対応した値を追加する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void addValue(final String key, final Float value) {
		this.um.addValue(key, value);
	}

	/**
	 * キーに対応した値を追加する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void addValue(final String key, final Float... value) {
		this.um.addValue(key, value);
	}

	/**
	 * キーに対応した値を追加する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void addValue(final String key, final Double value) {
		this.um.addValue(key, value);
	}

	/**
	 * キーに対応した値を追加する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void addValue(final String key, final Double... value) {
		this.um.addValue(key, value);
	}

	/**
	 * キーに対応した値を追加する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void addValue(final String key, final BigInteger value) {
		this.um.addValue(key, value);
	}

	/**
	 * キーに対応した値を追加する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void addValue(final String key, final BigInteger... value) {
		this.um.addValue(key, value);
	}

	/**
	 * キーに対応した値を追加する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void addValue(final String key, final BigDecimal value) {
		this.um.addValue(key, value);
	}

	/**
	 * キーに対応した値を追加する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void addValue(final String key, final BigDecimal... value) {
		this.um.addValue(key, value);
	}

	/**
	 * キーに対応した値を追加する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void addValue(final String key, final Number value) {
		this.um.addValue(key, value);
	}

	/**
	 * キーに対応した値を追加する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void addValue(final String key, final Number... value) {
		this.um.addValue(key, value);
	}

	/**
	 * キーに対応した値を追加する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void addValue(final String key, final Date value) {
		this.um.addValue(key, value);
	}

	/**
	 * キーに対応した値を追加する。
	 *
	 * @param key キー
	 * @param value 値
	 */
	@Override
	public void addValue(final String key, final Date... value) {
		this.um.addValue(key, value);
	}

	/**
	 * 日時取得
	 *
	 * @return 日時
	 */
	@Override
	public Timestamp getPresent() {
		return this.um.getPresent();
	}

	/**
	 * 日時設定
	 *
	 * @param val 日時
	 */
	@Override
	public void setPresent(final Timestamp val) {
		this.um.setPresent(val);
	}

	/**
	 * 値保持マップ取得
	 *
	 * @return Mapオブジェクト
	 */
	@Override
	public Map<String, Serializable> toMap() {
		return this.um.toMap();
	}

	/**
	 * キーに対応した値をmodelに複製設定する。
	 *
	 * @param model 汎用モデル
	 * @param key キー
	 */
	@Override
	public void copyValueTo(final UniModel model, final String... key) {
		this.um.copyValueTo(model, key);
	}

	/**
	 * キーに対応した値をmodelに設定する。
	 *
	 * @param model 汎用モデル
	 * @param key キー
	 */
	@Override
	public void putValueTo(final UniModel model, final String... key) {
		this.um.putValueTo(model, key);
	}
}
