package charactermanaj.model;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import charactermanaj.graphics.filters.ColorConvertParameter;

/**
 * パーツごとの色情報を設定・取得したり、最後に設定した色情報を記憶するなどの色情報アクセスのためのクラス.<br>
 * @author seraphy
 *
 */
public class PartsColorManager {
	
	private static final long serialVersionUID = 3246538139597289650L;

	private HashMap<PartsCategory, PartsColorInfo> categoryColorInfoMap = new HashMap<PartsCategory, PartsColorInfo>();
	
	private HashMap<PartsIdentifier, PartsColorInfo> partsColorInfoMap = new HashMap<PartsIdentifier, PartsColorInfo>();
	
	private HashMap<ColorGroup, ColorConvertParameter> recentColorGroupMap = new HashMap<ColorGroup, ColorConvertParameter>();
	
	private PartsSpecResolver partsSpecResolver;
	
	public PartsColorManager(PartsSpecResolver partsSpecResolver) {
		if (partsSpecResolver == null) {
			throw new IllegalArgumentException();
		}
		this.partsSpecResolver = partsSpecResolver;
	}
	
	/**
	 * パーツ識別子ごとの色情報を取得します.<br>
	 * まだ一度も登録されていない場合は、現在の状態から色情報を作成して返します.<br>
	 * その場合、registered引数がtrueである場合は生成と同時に初期値として登録済みとする.<br>
	 * そうでない場合は生成された色情報は一時的なものとなる.
	 * @param partsIdentifier パーツ識別子
	 * @param registered 色情報を新規に作成した場合に登録する場合はtrue
	 * @return 色情報
	 */
	public PartsColorInfo getPartsColorInfo(PartsIdentifier partsIdentifier, boolean registered) {
		if (partsIdentifier == null) {
			throw new IllegalArgumentException();
		}
		PartsColorInfo partsColorInfo = partsColorInfoMap.get(partsIdentifier);
		if (partsColorInfo == null) {
			partsColorInfo = createDefaultColorInfo(partsIdentifier);
			if (registered) {
				partsColorInfoMap.put(partsIdentifier, partsColorInfo);
			}
		}
		return partsColorInfo;
	}
	
	public void setPartsColorInfo(PartsIdentifier partsIdentifier, PartsColorInfo partsColorInfo, boolean applyAll) {
		if (partsIdentifier == null || partsColorInfo == null) {
			throw new IllegalArgumentException();
		}
		if (applyAll) {
			// カテゴリ指定
			PartsCategory partsCategory = partsIdentifier.getPartsCategory();
			resetPartsColorInfo(partsCategory);
			categoryColorInfoMap.put(partsCategory, partsColorInfo);
		} else {
			// パーツ個別指定
			partsColorInfoMap.put(partsIdentifier, partsColorInfo);
		}
		
		setRecentColorGroup(partsColorInfo);
		categoryColorInfoMap.put(partsIdentifier.getPartsCategory(), partsColorInfo);
	}
	
	protected void setRecentColorGroup(PartsColorInfo partsColorInfo) {
		for (Map.Entry<Layer, ColorInfo> entry :partsColorInfo.entrySet()) {
			ColorInfo colorInfo = entry.getValue();
			ColorGroup colorGroup = colorInfo.getColorGroup();
			if (colorInfo.isSyncColorGroup() && colorGroup != null && colorGroup.isEnabled()) {
				recentColorGroupMap.put(colorGroup, colorInfo.getColorParameter());
			}
		}
	}
	
	protected ColorConvertParameter getRecentColorGroup(ColorGroup colorGroup) {
		if (colorGroup == null || !colorGroup.isEnabled()) {
			return null;
		}
		return recentColorGroupMap.get(colorGroup);
	}
	
	public PartsColorInfo getPartsColorInfo(PartsCategory partsCategory) {
		return categoryColorInfoMap.get(partsCategory);
	}

	public void resetPartsColorInfo() {
		resetPartsColorInfo(null);
	}
	
	public void resetPartsColorInfo(PartsCategory partsCategory) {
		if (partsCategory == null) {
			partsColorInfoMap.clear();
			return;
		}

		Iterator<Map.Entry<PartsIdentifier, PartsColorInfo>> ite = partsColorInfoMap.entrySet().iterator();
		while (ite.hasNext()) {
			Map.Entry<PartsIdentifier, PartsColorInfo> entry = ite.next();
			PartsIdentifier partsIdentifier = entry.getKey();
			if (partsIdentifier.getPartsCategory().equals(partsCategory)) {
				ite.remove();
			}
		}
	}
	
	/**
	 * パーツ識別子ごとの色情報を現在の状態から新たに構築する.
	 * @param partsIdentifier パーツ識別子
	 * @return 色情報
	 */
	protected PartsColorInfo createDefaultColorInfo(PartsIdentifier partsIdentifier) {
		PartsColorInfo partsColorInfo = new PartsColorInfo(partsIdentifier.getPartsCategory());
		PartsSpec partsSpec = partsSpecResolver.getPartsSpec(partsIdentifier);
		if (partsSpec != null) {
			// パーツ固有の設定がある場合
			// パーツ固有のカラーグループが指定されていれば、全レイヤーを該当カラーグループに設定する.
			ColorGroup colorGroup = partsSpec.getColorGroup();
			if (colorGroup != null && colorGroup.isEnabled()) {
				for (Map.Entry<Layer, ColorInfo> entry : partsColorInfo.entrySet()) {
					ColorInfo colorInfo = entry.getValue();
					colorInfo.setColorGroup(colorGroup);
					colorInfo.setSyncColorGroup(true);
				}
			}
		} else {
			// パーツ固有の設定がない場合、カテゴリ別登録で設定する
			PartsColorInfo categoryPartsColorInfo = categoryColorInfoMap.get(partsIdentifier.getPartsCategory());
			if (categoryPartsColorInfo != null) {
				for (Map.Entry<Layer, ColorInfo> entry : categoryPartsColorInfo.entrySet()) {
					Layer layer = entry.getKey();
					ColorInfo colorInfo = entry.getValue();
					if (partsColorInfo.containsKey(layer)) {
						partsColorInfo.put(layer, colorInfo);
					}
				}
			}
		}
		// カラーグループが指定されている場合、もっとも最近に設定されたカラーグループの色情報に設定し直す
		for (Map.Entry<Layer, ColorInfo> entry : partsColorInfo.entrySet()) {
			ColorInfo colorInfo = entry.getValue();
			ColorGroup colorGroup = colorInfo.getColorGroup();
			if (colorGroup != null && colorGroup.isEnabled() && colorInfo.isSyncColorGroup()) {
				ColorConvertParameter param = getRecentColorGroup(colorGroup);
				if (param != null) {
					colorInfo.setColorParameter(param);
				}
			}
		}
		return partsColorInfo;
	}
	
	public Map<PartsIdentifier, PartsColorInfo> getPartsColorInfoMap() {
		return partsColorInfoMap;
	}
	
}
