/*
 * @(#)ShardedConfiguration.java 2012-8-1 下午10:00:00
 *
 * Copyright (c) 2011-2012 Makersoft.org all rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 *
 */
package org.makersoft.shards;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.session.SqlSessionFactory;
import org.makersoft.shards.cfg.ShardConfiguration;
import org.makersoft.shards.id.IdGenerator;
import org.makersoft.shards.session.ShardedSqlSessionFactory;
import org.makersoft.shards.session.impl.ShardedSqlSessionFactoryImpl;
import org.makersoft.shards.strategy.ShardStrategyFactory;
import org.makersoft.shards.utils.Assert;
import org.makersoft.shards.utils.Maps;

/**
 * @author Feng Kuok
 */
public class ShardedConfiguration {

	private final Log log = LogFactory.getLog(getClass());

	// private final

	// 分区配置
	private final List<ShardConfiguration> shardConfigs;

	// 用户自定义分区策略
	private final ShardStrategyFactory shardStrategyFactory;

	private final IdGenerator idGenerator;

	// 虚拟分区ids --> 物理分区 ids 映射
	private final Map<Integer, Integer> virtualShardToShardMap;

	// 物理分区ids --> 虚拟分区ids集合 映射
	private final Map<Integer, Set<ShardId>> shardToVirtualShardIdMap;

	public ShardedConfiguration(List<ShardConfiguration> shardConfigs,
			ShardStrategyFactory shardStrategyFactory, IdGenerator idGenerator) {

		this(shardConfigs, shardStrategyFactory, new HashMap<Integer, Integer>(), idGenerator);
	}

	public ShardedConfiguration(List<ShardConfiguration> shardConfigs,
			ShardStrategyFactory shardStrategyFactory,
			Map<Integer, Integer> virtualShardToShardMap, IdGenerator idGenerator) {

		Assert.notNull(shardConfigs);
		Assert.notNull(shardStrategyFactory);
		Assert.notNull(virtualShardToShardMap);

		this.shardConfigs = shardConfigs;
		this.shardStrategyFactory = shardStrategyFactory;
		this.virtualShardToShardMap = virtualShardToShardMap;
		this.idGenerator = idGenerator;

		if (!virtualShardToShardMap.isEmpty()) {
			// build the map from shard to set of virtual shards
			shardToVirtualShardIdMap = Maps.newHashMap();
			for (Map.Entry<Integer, Integer> entry : virtualShardToShardMap.entrySet()) {
				Set<ShardId> set = shardToVirtualShardIdMap.get(entry.getValue());
				// see if we already have a set of virtual shards
				if (set == null) {
					// we don't, so create it and add it to the map
					set = new HashSet<ShardId>();
					shardToVirtualShardIdMap.put(entry.getValue(), set);
				}
				set.add(new ShardId(entry.getKey()));
			}
		} else {
			shardToVirtualShardIdMap = new HashMap<Integer, Set<ShardId>>();
		}
	}

	public ShardedSqlSessionFactory buildShardedSessionFactory() {
		Map<SqlSessionFactory, Set<ShardId>> sqlSessionFactories = new HashMap<SqlSessionFactory, Set<ShardId>>();

		for (ShardConfiguration config : shardConfigs) {
			// populatePrototypeWithVariableProperties(config);
			// get the shardId from the shard-specific config
			Integer shardId = config.getShardId();
			if (shardId == null) {
				final String msg = "Attempt to build a ShardedSessionFactory using a "
						+ "ShardConfiguration that has a null shard id.";
				log.fatal(msg);
				throw new NullPointerException(msg);
			}
			Set<ShardId> virtualShardIds;
			if (virtualShardToShardMap.isEmpty()) {
				// simple case, virtual and physical are the same
				virtualShardIds = Collections.singleton(new ShardId(shardId));
			} else {
				// get the set of shard ids that are mapped to the physical
				// shard
				// described by this config
				virtualShardIds = shardToVirtualShardIdMap.get(shardId);
			}
			// sqlSessionFactories.put(buildSessionFactory(), virtualShardIds);
			sqlSessionFactories.put(config.getSqlSessionFactory(), virtualShardIds);
		}
		// final boolean doFullCrossShardRelationshipChecking =
		// PropertiesHelper.getBoolean(
		// ShardedEnvironment.CHECK_ALL_ASSOCIATED_OBJECTS_FOR_DIFFERENT_SHARDS,
		// prototypeConfiguration.getProperties(),
		// true);
		return new ShardedSqlSessionFactoryImpl(sqlSessionFactories, shardStrategyFactory,
				idGenerator);
	}

}
