package project.web.generic.master;

import java.sql.Types;
import java.util.Map;
import java.util.Map.Entry;

import online.annotation.SessionEntry;
import online.annotation.SessionExit;
import online.annotation.SessionReserved;
import online.context.check.InputCheck;
import online.struts.action.BrowseAction;
import online.struts.action.UniForm;
import project.check.TopMessage;
import project.check.attribute.DateCheck;
import project.check.attribute.DecimalCheck;
import project.check.attribute.HanEisuCheck;
import project.check.attribute.HankakuCheck;
import project.check.attribute.LongNumeralCheck;
import project.check.attribute.TimestampCheck;
import project.check.existence.MustCheck;
import project.check.range.LengthRangeCheck;
import project.common.db.DBColumnInfo;
import project.common.db.DBMetaData;
import project.master.MsgUtil;
import project.svc.generic.db.RecordOperate;
import project.web.InstanceFactory;
import core.config.Factory;
import core.util.bean.CamelCase;

/**
 * 汎用更新画面
 *
 * @author Tadashi Nakayama
 * @version 1.0.0
 */
@SessionEntry
public final class UpdateAction implements BrowseAction {

	/** キー項目 */
	@SessionReserved
	private static final String ID = "Id";
	/** バージョン項目 */
	private static final String VERSION = "Version";

	/** テーブル名 */
	private String table;

	/**
	 * テーブル名設定
	 * @param val テーブル名
	 */
	public void setTable(final String val) {
		this.table = val;
	}

	/**
	 * 初期表示処理
	 *
	 * @param model 汎用モデル
	 * @return 処理結果
	 */
	public String get(final UniForm model) {

		final InputCheck ic = InstanceFactory.create(InputCheck.class, model);
		ic.add(ID, new LongNumeralCheck());
		ic.populate();

		// TODO 権限確認
		final RecordOperate ro = InstanceFactory.create(RecordOperate.class, model);
		if (!ro.find(model.getNumber(ID), model)) {
			if (model.hasQueryString()) {
				return ID_NG_REDIRECT;
			}

			model.noValue(VERSION);
			// メッセージ
			MsgUtil.putTopMessage(model, "ZZ000000014");
		}

		return ID_VIEW;
	}

	/**
	 * 更新処理
	 *
	 * @param model 汎用モデル
	 * @return 処理結果
	 */
	public String update(final UniForm model) {

		// テーブル情報取得
		final DBMetaData dmd = Factory.create(DBMetaData.class);
		final Map<String, DBColumnInfo> info = dmd.getColumnInfo(this.table);

		// 入力チェック
		final RecordOperate ro = InstanceFactory.create(RecordOperate.class, model);
		final InputCheck ic = InstanceFactory.create(InputCheck.class, model);
		ic.onError(new TopMessage("ZZ000000005"));
		for (final Entry<String, DBColumnInfo> ent : info.entrySet()) {
			final String name = CamelCase.convert(ent.getKey());
			if (!ID.equals(name) && !ro.isMaintenance(name)) {
				addChecks(name, ent.getValue(), ic);
			}
		}

		ic.add("FooCheck", new HankakuCheck());
		ic.add("FooCheck2", new HankakuCheck());
		ic.populate();

		return ID_OK;
	}

	/**
	 * 即時更新処理
	 *
	 * @param model 汎用モデル
	 * @return 処理結果
	 */
	@SessionExit
	public String complete(final UniForm model) {

		final String ret = update(model);
		if (!ID_OK.equals(ret)) {
			return ret;
		}

		final RecordOperate ro = InstanceFactory.create(RecordOperate.class, model);
		if (!model.hasValue(ID)) {
			if (!ro.insert(model)) {
				// 排他エラー
				MsgUtil.putTopMessage(model, "ZZ000000015");
				return ID_NG;
			}
		} else {
			if (!ro.update(model.getNumber(ID), model.getNumber(VERSION), model)) {
				// レコード不在、バージョン不一致
				MsgUtil.putTopMessage(model, "ZZ000000016");
				return ID_NG;
			}
		}

		return ID_OK;
	}

	/**
	 * 入力チェック追加
	 *
	 * @param name 項目名
	 * @param info テーブル情報
	 * @param ic 入力チェッカー
	 */
	private void addChecks(final String name, final DBColumnInfo info, final InputCheck ic) {
		// 必須入力
		if (info.isNotNull()) {
			ic.add(name, new MustCheck("ZZ000000006", info.getComment()));
		}

		// タイプと長さ
		if (info.getType() == Types.CHAR) {
			ic.add(name, new HanEisuCheck("ZZ000000007", info.getComment()));
			ic.add(name, new LengthRangeCheck(0, info.getSize(), "ZZ000000008", info.getComment()));
		} else if (info.getType() == Types.VARCHAR) {
			ic.add(name, new LengthRangeCheck(0, info.getSize(), "ZZ000000009", info.getComment()));
		} else if (info.getType() == Types.DATE) {
			ic.add(name, new DateCheck("ZZ000000010", info.getComment()));
		} else if (info.getType() == Types.TIMESTAMP) {
			ic.add(name, new TimestampCheck("ZZ000000011", info.getComment()));
		} else {
			ic.add(name, new DecimalCheck(
					info.getSize(), info.getScale(), "ZZ000000012", info.getComment()));
		}
	}
}
