package charactermanaj.ui.model;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import charactermanaj.model.CustomLayerOrder;
import charactermanaj.model.CustomLayerOrderKey;
import charactermanaj.model.CustomLayerOrderMapper;
import charactermanaj.model.Layer;
import charactermanaj.model.LayerOrderMapper;

/**
 * アクティブなカスタムレイヤーパターンのリストを管理する。
 */
public class ActiveCustomLayerPatternMgr {

	/**
	 * 認識している、すべてのカスタムレイヤーパターンを保持する
	 */
	private final Map<CustomLayerOrderKey, List<CustomLayerOrder>> customLayerPatternMap =
			new HashMap<CustomLayerOrderKey, List<CustomLayerOrder>>();

	/**
	 * 現在有効にしているカスタムレイヤーパターン名を保持する
	 */
	private final Set<CustomLayerOrderKey> activeCustomLayers = new HashSet<CustomLayerOrderKey>();

	/**
	 * カスタムレイヤーパターンと有効にしているパターン名から現在有効なレイヤーマッピングを導出する。
	 * まだ導出されていないかリセットされた場合はnull
	 */
	private transient CustomLayerOrderMapper customLayerOrderMapper;

	/**
	 * レイヤーパターンの一覧を取得する。
	 *
	 * @return map パターン名をキーとし、カスタムレイヤー順のリストを値とするマップ
	 */
	public Map<CustomLayerOrderKey, List<CustomLayerOrder>> getMap() {
		return customLayerPatternMap;
	}

	/**
	 * レイヤーパターンの一覧を設定する。
	 * (現在のパターン一覧はクリアされる。)
	 * @param map パターン名をキーとし、カスタムレイヤー順のリストを値とするマップ
	 */
	public void setMap(Map<CustomLayerOrderKey, List<CustomLayerOrder>> map) {
		customLayerPatternMap.clear();
		customLayerPatternMap.putAll(map);
		activeCustomLayers.retainAll(customLayerPatternMap.keySet()); // 現在存在するパターン名に絞り込む
		customLayerOrderMapper = null; // レイヤー順マッピングは再構築する必要がある。
	}

	/**
	 * すべてのレイヤーパターンのキーとレイヤー順リストについて、パターンの表示名順にソートしたエントリリストとして返す。
	 * @return ソートされたエントリーリスト
	 */
	public List<Map.Entry<CustomLayerOrderKey, List<CustomLayerOrder>>> getOrderedEntries() {
		List<Map.Entry<CustomLayerOrderKey, List<CustomLayerOrder>>> entries =
				new ArrayList<Map.Entry<CustomLayerOrderKey, List<CustomLayerOrder>>>(customLayerPatternMap.entrySet());
		Collections.sort(entries, new Comparator<Map.Entry<CustomLayerOrderKey, List<CustomLayerOrder>>>() {
			@Override
			public int compare(Entry<CustomLayerOrderKey, List<CustomLayerOrder>> o1,
					Entry<CustomLayerOrderKey, List<CustomLayerOrder>> o2) {
				CustomLayerOrderKey k1 = o1.getKey();
				CustomLayerOrderKey k2 = o2.getKey();
				return CustomLayerOrderKey.COMPARATOR.compare(k1, k2);
			}
		});
		return entries;
	}


	/**
	 * アクティブなカスタムレイヤー順定義でカスタムレイヤー順序索引を行うようにする。
	 */
	public void initActiveCustomLayerOrderMap() {
		initCustomLayerOrderMap(getActiveCustomLayerOrderList());
	}

	/**
	 * リストで指定したカスタムレイヤー順定義でカスタムレイヤー順序索引を行うようにする。
	 * @param orderList カスタムレイヤー順定義、nullの場合はすべてレイヤーの規定値となる。
	 */
	public void initCustomLayerOrderMap(List<CustomLayerOrder> orderList) {
		customLayerOrderMapper = new CustomLayerOrderMapper(orderList);
	}

	/**
	 * 現在設定されているカスタムレイヤー順序索引を取得する。
	 * @see {@link #initCustomLayerOrderMap(List)}
	 * @return
	 */
	public LayerOrderMapper getLayerOrderMapper() {
		if (customLayerOrderMapper == null) {
			initActiveCustomLayerOrderMap();
		}
		return customLayerOrderMapper;
	}

	/**
	 * 現在有効なカスタムレイヤーパターン名の一覧のコピーを取得する。
	 * (返される順序は不定です。)
	 * @return
	 */
	public Set<CustomLayerOrderKey> getActivePatternNames() {
		return new HashSet<CustomLayerOrderKey>(activeCustomLayers);
	}

	/**
	 * 現在有効なカスタムレイヤー定義リストを取得する
	 * @return
	 */
	public List<CustomLayerOrder> getActiveCustomLayerOrderList() {
		List<CustomLayerOrder> mergedList = new ArrayList<CustomLayerOrder>();
		for (CustomLayerOrderKey patternName : activeCustomLayers) {
			List<CustomLayerOrder> list = customLayerPatternMap.get(patternName);
			if (list != null) {
				for (CustomLayerOrder layerOrder : list) {
					if (layerOrder != null && !mergedList.contains(layerOrder)) {
						mergedList.add(layerOrder);
					}
				}
			}
		}
		return mergedList;
	}


	/**
	 * レイヤーパターンの選択状態を取得する
	 * @param name
	 * @return
	 */
	public boolean isSelected(CustomLayerOrderKey name) {
		return activeCustomLayers.contains(name);
	}

	/**
	 * レイヤーパターンの選択状態を設定する
	 * @param name
	 * @param selected
	 */
	public void setSelected(CustomLayerOrderKey name, boolean selected) {
		if (customLayerPatternMap.containsKey(name)) {
			if (selected) {
				unselectConflict(name);
				activeCustomLayers.add(name);
			} else {
				activeCustomLayers.remove(name);
			}
			customLayerOrderMapper = null; // レイヤー順マッピングは再構築する必要がある。
		}
	}

	/**
	 * 現在のアクティブなレイヤーパターンの一覧を一括更新する。
	 * @param patternNames
	 */
	public void setActivePatternNames(Set<CustomLayerOrderKey> patternNames) {
		activeCustomLayers.clear();
		for (CustomLayerOrderKey name : patternNames) {
			if (customLayerPatternMap.containsKey(name)) {
				unselectConflict(name);
				activeCustomLayers.add(name);
			}
		}
		customLayerOrderMapper = null; // レイヤー順マッピングは再構築する必要がある。
	}

	/**
	 * 選択するレイヤーパターンと重複するレイヤーをもつパターンがすでに選択されている場合、
	 * その選択を解除する。
	 * @param name 選択するレイヤーパターン
	 */
	private void unselectConflict(CustomLayerOrderKey orderKey) {
		// 指定されたパターン名が使用しているレイヤー一覧を作成する。
		Set<Layer> targetLayers = new HashSet<Layer>();
		List<CustomLayerOrder> targetLayerOrders = customLayerPatternMap.get(orderKey);
		if (targetLayerOrders != null) {
			for (CustomLayerOrder layerOrder : targetLayerOrders) {
				targetLayers.add(layerOrder.getLayer());
			}
		}

		// 現在選択しているレイヤーパターンから、
		// これから選択しようとしているレイヤーを含んでいる(衝突)パターンを検出する
		ArrayList<CustomLayerOrderKey> conflictPatterns = new ArrayList<CustomLayerOrderKey>();
		for (CustomLayerOrderKey patternName : activeCustomLayers) {
			if (!patternName.equals(orderKey)) { // 自分と同一名であれば検査の必要なし
				List<CustomLayerOrder> layerOrders = customLayerPatternMap.get(patternName);
				if (layerOrders != null) {
					for (CustomLayerOrder layerOrder : layerOrders) {
						Layer layer = layerOrder.getLayer();
						if (targetLayers.contains(layer)) {
							// ターゲットにレイヤーが含まれている場合は、このパターンの選択は解除する
							conflictPatterns.add(patternName);
							break;
						}
					}
				}
			}
		}
		// 衝突のあるパターン名を現在の選択から除外する
		activeCustomLayers.removeAll(conflictPatterns);
	}
}