/*
 * Copyright 2006 Takahiro Nakamura.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package woolpack.samples.gcrud;

import java.util.Collection;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import woolpack.factory.FactoryUtils;
import woolpack.fn.Fn;
import woolpack.fn.FnUtils;
import woolpack.sql.fn.PreparedStatementInfo;
import woolpack.sql.fn.SqlFnUtils;
import woolpack.sql.meta.SqlMetaUtils;
import woolpack.sql.meta.TableInfo;
import woolpack.sql.meta.TableInfoUtils;

/**
 * DB のメタ情報を利用して RDB にアクセスする CRUD 関数を生成するビルダです。
 * 生成する{@link Fn}の引数の内"tableName"をテーブル特定のために使用します。
 * @author nakamura
 *
 */
public class GcrudManager {
	private DataSource dataSource;
	private List<TableInfo> tableInfoList;
	private Map<String, TableInfo> tableInfoMap;
	
	public GcrudManager() {
	}
	public DataSource getDataSource() {
		return dataSource;
	}
	public void setDataSource(final DataSource dataSource) {
		this.dataSource = dataSource;
	}
	public List<TableInfo> getTableInfoList() {
		return tableInfoList;
	}
	public void setTableInfoList(final List<TableInfo> tableInfoList) {
		this.tableInfoList = tableInfoList;
	}
	public Map<String, TableInfo> getTableInfoMap() {
		return tableInfoMap;
	}
	
	private TableInfo getTableInfo(final Map<String, ?> c) {
		final TableInfo info = tableInfoMap.get(c.get("tableName"));
		info.getClass();
		return info;
	}
	
	private Fn<Map<String, ?>, PreparedStatementInfo, RuntimeException> getFactory(
			final Fn<TableInfo, Fn<Collection<String>, PreparedStatementInfo, RuntimeException>, RuntimeException> factoryFactory) {
		return new Fn<Map<String, ?>, PreparedStatementInfo, RuntimeException>() {
			public PreparedStatementInfo exec(final Map<String, ?> c) {
				return factoryFactory.exec(getTableInfo(c)).exec(c.keySet());
			}
		};
	}
	
	/**
	 * {@link TableInfoUtils#convertToMap(List)}を呼び出しその結果を保持します。
	 *
	 */
	public void init() {
		tableInfoMap = TableInfoUtils.convertToMap(tableInfoList);
	}
	
	/**
	 * ひとつのテーブルを検索してレコードの一覧を返す{@link Fn}を生成します。
	 * @return ひとつのテーブルを検索してレコードの一覧を返す{@link Fn}。
	 */
	public Fn<Map<String, ?>, List<Map<String, Object>>, Exception> createSelectListFn() {
		return SqlFnUtils.inputBean(
				dataSource,
				getFactory(SqlMetaUtils.SELECT_FACTORY_FACTORY),
				SqlFnUtils.getList(
						SqlFnUtils.getBeanResult(FactoryUtils.LINKED_MAP_FACTORY),
						FnUtils.fix(null)),
				FnUtils.fix(null));
	}
	
	/**
	 * ひとつのテーブルを検索してレコードの情報を返す{@link Fn}を生成します。
	 * @return ひとつのテーブルを検索してレコードの情報を返す{@link Fn}。
	 */
	public Fn<Map<String, ?>, Map<String, Object>, Exception> createSelectOneFn() {
		return SqlFnUtils.inputBean(
				dataSource,
				getFactory(SqlMetaUtils.SELECT_FACTORY_FACTORY),
				SqlFnUtils.getOne(
						SqlFnUtils.getBeanResult(FactoryUtils.LINKED_MAP_FACTORY),
						FnUtils.fix(null)),
				FnUtils.fix(null));
		
	}
	
	/**
	 * レコードを登録する{@link Fn}を生成します。
	 * @return レコードを登録する{@link Fn}。
	 */
	public Fn<Map<String, ?>, Integer, Exception> createInsertFn() {
		return SqlFnUtils.inputBean(
			dataSource,
			getFactory(SqlMetaUtils.INSERT_FACTORY_FACTORY),
			SqlFnUtils.GET_COUNT,
			FnUtils.fix(null));
	}
	
	/**
	 * レコードを更新する{@link Fn}を生成します。
	 * @return レコードを更新する{@link Fn}。
	 */
	public Fn<Map<String, ?>, Integer, Exception> createUpdateFn() {
		return SqlFnUtils.inputBean(
			dataSource,
			getFactory(SqlMetaUtils.UPDATE_FACTORY_FACTORY),
			SqlFnUtils.GET_COUNT,
			FnUtils.fix(null));
	}
	
	/**
	 * レコードを削除する{@link Fn}を生成します。
	 * @return レコードを削除する{@link Fn}。
	 */
	public Fn<Map<String, ?>, Integer, Exception> createDeleteFn() {
		return SqlFnUtils.inputBean(
			dataSource,
			new Fn<Map<String, ?>, PreparedStatementInfo, Exception>() {
				public PreparedStatementInfo exec(final Map<String, ?> c) {
					return SqlMetaUtils.DELETE_FACTORY.exec(getTableInfo(c));
				}
			},
			SqlFnUtils.GET_COUNT,
			FnUtils.fix(null));
	}
}
