package project.view.tag;

import java.nio.charset.Charset;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.servlet.jsp.JspException;

import online.view.ViewUtil;
import online.view.tag.BaseTag;
import project.common.db.DBColumnInfo;
import project.common.db.DBMetaData;
import project.view.LabelValue;

import common.db.JdbcSource;
import common.db.jdbc.Jdbc;

import core.config.Factory;
import core.exception.ThrowableUtil;

/**
 * 項目選択タグライブラリ
 *
 * @author Tadashi Nakayama
 * @version 1.0.0
 */
public final class MasterSelectTag extends BaseTag {
	/** serialVersionUID */
	private static final long serialVersionUID = -5637333263760181512L;

	/** 変数用変数 */
	private String var = null;
	/** テーブル */
	private String table = null;
	/** 条件値 */
	private String[] vals = null;
	/** ラベルカラム名 */
	private String label = null;
	/** 値カラム名 */
	private String value = null;
	/** 順序 */
	private String orderBy = null;
	/** グループ */
	private boolean groupBy = false;
	/** 条件カラム */
	private String[] cols = null;

	/**
	 * リリース処理
	 */
	@Override
	public void release() {
		this.var = null;
		this.table = null;
		this.vals = null;
		this.label = null;
		this.value = null;
		this.orderBy = null;
		this.groupBy = false;
		this.cols = null;
	}

	/**
	 * 変数設定
	 *
	 * @param val 保存変数名文字列
	 */
	public void setVar(final String val) {
		this.var = val;
	}

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

	/**
	 * ラベルのカラム名設定
	 *
	 * @param val ラベルのカラム名
	 */
	public void setLabelColumn(final String val) {
		this.label = val;
	}

	/**
	 * 値のカラム名設定
	 *
	 * @param val 値のカラム名
	 */
	public void setValueColumn(final String val) {
		this.value = val;
	}

	/**
	 * 条件値設定
	 *
	 * @param val 条件値
	 */
	public void setWhereValue(final String val) {
		this.vals = super.splitValue(val);
	}

	/**
	 * 条件カラム名設定
	 *
	 * @param val 条件カラム名
	 */
	public void setWhereColumn(final String val) {
		this.cols = super.splitValue(val);
	}

	/**
	 * 順序カラム名設定
	 *
	 * @param val 順序カラム名
	 */
	public void setOrderBy(final String val) {
		this.orderBy = val;
	}

	/**
	 * グループ設定
	 * @param val グループ
	 */
	public void setGroupBy(final String val) {
		this.groupBy = Boolean.parseBoolean(val);
	}

	/**
	 * @see javax.servlet.jsp.tagext.TagSupport#doStartTag()
	 */
	@Override
	public int doStartTag() throws JspException {
		try {
			doTag();
			return SKIP_BODY;
		} finally {
			release();
		}
	}

	/**
	 * タグ処理
	 * @throws JspException jsp例外
	 */
	public void doTag() throws JspException {
		if (this.var != null) {
			this.pageContext.removeAttribute(this.var);
		}

		setLists(getQuery());
	}

	/**
	 * リストをコンテキストに設定
	 *
	 * @param query クエリ
	 * @throws JspException JSP例外
	 */
	private void setLists(final String query) throws JspException {
		try (Jdbc conn = JdbcSource.getConnection()) {
			List<LabelValue> lv = new ArrayList<>();
			try (PreparedStatement psmt = conn.readonlyStatement(query)) {
				for (int i = 0, loc = 1; this.vals != null && i < this.vals.length; i++) {
					psmt.setObject(loc++, this.vals[i]);
				}

				Charset cs = ViewUtil.getCharset(super.getResponse());
				try (ResultSet rs = psmt.executeQuery()) {
					while (rs.next()) {
						String lbl = ViewUtil.sanitize(rs.getString(this.label), cs, true);
						String val = ViewUtil.sanitize(rs.getString(this.value), cs, true);
						lv.add(new LabelValue(lbl, val));
					}
				}
			}
			// 項目選択設定
			this.pageContext.setAttribute(this.var, lv);
		} catch (SQLException ex) {
			ThrowableUtil.error(ex);
			throw new JspException(ex);
		}
	}

	/**
	 * クエリ取得
	 *
	 * @return クエリ
	 */
	private String getQuery() {
		StringBuilder sb = new StringBuilder();
		sb.append("SELECT ");
		sb.append(this.value).append(", ").append(this.label);
		sb.append(" FROM ").append(this.table);
		sb.append(" WHERE ");
		sb.append(this.value).append(" IS NOT NULL ");
		sb.append(" AND ");
		sb.append(this.label).append(" IS NOT NULL ");
		if (this.cols != null) {
			for (int i = 0; i < this.cols.length; i++) {
				sb.append(" AND ");
				sb.append(this.cols[i]);
				if (this.vals[i].contains("%")) {
					sb.append(" LIKE ? ");
				} else {
					sb.append(" = ? ");
				}
			}
		}

		String str = getVersionQuery();
		if (str != null) {
			sb.append(" AND ");
			sb.append(str);
		}

		if (this.groupBy) {
			sb.append(" GROUP BY ").append(this.value).append(", ").append(this.label);
		}

		if (this.orderBy != null) {
			sb.append(" ORDER BY ").append(this.orderBy);
		}

		return sb.toString();
	}

	/**
	 * バージョンクエリ取得
	 *
	 * @return バージョンクエリ
	 */
	private String getVersionQuery() {
		DBMetaData dmd = Factory.create(DBMetaData.class);
		Map<String, DBColumnInfo> map = dmd.getColumnInfo(this.table);
		if (map.containsKey("VERSION") || map.containsKey("version")) {
			return " VERSION > 0 ";
		}
		return null;
	}
}
