package charactermanaj.ui.model;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;

import charactermanaj.graphics.filters.ColorConvertParameter;
import charactermanaj.model.ColorGroup;
import charactermanaj.model.ColorInfo;
import charactermanaj.model.Layer;
import charactermanaj.model.PartsCategory;
import charactermanaj.model.PartsColorInfo;
import charactermanaj.model.PartsColorManager;
import charactermanaj.model.PartsIdentifier;
import charactermanaj.ui.ColorDialog;

public class ColorGroupCoordinator {
	
	private PartsSelectionManager partsSelectionMrg;
	
	private LinkedList<ColorDialog> colorDialogs = new LinkedList<ColorDialog>();
	
	private LinkedList<ColorChangeListener> listeners = new LinkedList<ColorChangeListener>();
	
	private PartsColorManager partsColorMrg;

	protected ColorChangeListener listener;
	
	public ColorGroupCoordinator(PartsSelectionManager partsSelectionMrg, PartsColorManager partsColorMrg) {
		if (partsSelectionMrg == null || partsColorMrg == null) {
			throw new IllegalArgumentException();
		}
		this.partsSelectionMrg = partsSelectionMrg;
		this.partsColorMrg = partsColorMrg;
		
		listener = new ColorChangeListener() {
			public void onColorChange(ColorChangeEvent event) {
				Layer layer = event.getLayer();
				ColorDialog colorDialog = (ColorDialog) event.getSource();
				PartsCategory partsCategory = colorDialog.getPartsCategory();
				ColorGroupCoordinator.this.syncColorGroup(partsCategory, layer, colorDialog);
				ColorGroupCoordinator.this.fireColorChangeEvent(event);
			}
			public void onColorGroupChange(ColorChangeEvent event) {
				Layer layer = event.getLayer();
				ColorDialog colorDialog = (ColorDialog) event.getSource();
				ColorGroup colorGroup = colorDialog.getColorGroup(layer);
				ColorGroupCoordinator.this.onChangeColorGroup(colorDialog, layer, colorGroup);
				ColorGroupCoordinator.this.fireColorGroupChangeEvent(event);
			}
		};
	}
	
	public void registerColorDialog(ColorDialog colorDialog) {
		if (colorDialog == null) {
			throw new IllegalArgumentException();
		}
		this.colorDialogs.add(colorDialog);
		colorDialog.addColorChangeListener(listener);
	}
	
	public void unregisterColorDialog(ColorDialog colorDialog) {
		Iterator<ColorDialog> ite = colorDialogs.iterator();
		while (ite.hasNext()) {
			ColorDialog dlg = ite.next();
			if (dlg == colorDialog) {
				dlg.removeColorChangeListener(listener);
				ite.remove();
			}
		}
	}
	
	public void addColorChangeListener(ColorChangeListener listener) {
		if (listener == null) {
			throw new IllegalArgumentException();
		}
		listeners.add(listener);
	}
	
	public void removeColorChangeListener(ColorChangeListener listener) {
		listeners.remove(listener);
	}
	
	protected void fireColorChangeEvent(ColorChangeEvent e) {
		if (e == null) {
			throw new IllegalArgumentException();
		}
		for (ColorChangeListener listener : listeners) {
			listener.onColorChange(e);
		}
	}
	
	protected void fireColorGroupChangeEvent(ColorChangeEvent e) {
		if (e == null) {
			throw new IllegalArgumentException();
		}
		for (ColorChangeListener listener : listeners) {
			listener.onColorGroupChange(e);
		}
	}
	
	protected void onChangeColorGroup(ColorDialog destColorDialog, Layer layer, ColorGroup colorGroup) {
		if (destColorDialog == null || layer == null || colorGroup == null) {
			throw new IllegalArgumentException();
		}
		for (ColorDialog colorDlg : colorDialogs) {
			for (Layer srcLayer : colorDlg.getPartsCategory().getLayers()) {
				if (!srcLayer.equals(layer)) {
					if (ColorGroup.equals(colorGroup, colorDlg.getColorGroup(srcLayer))
							&& colorDlg.isSyncColorGroup(srcLayer)) {
						ColorConvertParameter param = colorDlg.getColorConvertParameter(srcLayer);
						destColorDialog.setColorConvertParameter(layer, param);
						break;
					}
				}
			}
		}
	}
	
	public void syncColorGroup(PartsCategory partsCategory, Layer eventSourceLayer, ColorDialog sourceColorDialog) {
		if (partsCategory == null || eventSourceLayer == null || sourceColorDialog == null) {
			throw new IllegalArgumentException();
		}

		// 変更もと
		ArrayList<Layer> syncSources = new ArrayList<Layer>();
		if (eventSourceLayer != null) {
			if (sourceColorDialog.isSyncColorGroup(eventSourceLayer)) {
				syncSources.add(eventSourceLayer);
			}
		} else {
			for (Layer layer2 : partsCategory.getLayers()) {
				if (sourceColorDialog.isSyncColorGroup(layer2)) {
					syncSources.add(layer2);
				}
			}
		}
		
		// 変更もとのレイヤーのカラーグループを他のレイヤーにも適用する.
		for (Layer sourceLayer : syncSources) {
			ColorGroup sourceColorGroup = sourceColorDialog.getColorGroup(sourceLayer);
			if (sourceColorGroup != null && sourceColorGroup.isEnabled()) {
				ColorConvertParameter param = sourceColorDialog.getColorConvertParameter(sourceLayer);
				
				// 他のパネルに適用する
				for (ColorDialog targetColorDialog : colorDialogs) {
					for (Layer targetLayer : targetColorDialog.getPartsCategory().getLayers()) {
						if (!targetLayer.equals(sourceLayer)) {
							if (ColorGroup.equals(targetColorDialog.getColorGroup(targetLayer), sourceColorGroup)) {
								if (targetColorDialog.isSyncColorGroup(targetLayer)) {
									targetColorDialog.setColorConvertParameter(targetLayer, param);
								}
							}
						}
					}
				}
				// 色ダイアログで選択中でない有効なパーツも含めてパーツカラーを更新する.
				for (PartsCategory targetPartsCategory : partsSelectionMrg.getAllCategories()) {
					Collection<PartsIdentifier> selectedPartss = partsSelectionMrg.getSelectedPartsIdentifiers(targetPartsCategory);
					for (PartsIdentifier partsIdentifier : selectedPartss) {
						// カラーダイアログで選択されていない他のパーツも含めてパーツカラーを更新する.
						PartsColorInfo partsColorInfo = partsColorMrg.getPartsColorInfo(partsIdentifier, true);
						for (Map.Entry<Layer, ColorInfo> entry : partsColorInfo.entrySet()) {
							ColorInfo colorInfo = entry.getValue();
							if (ColorGroup.equals(sourceColorGroup, colorInfo.getColorGroup())) {
								if (colorInfo.isSyncColorGroup()) {
									colorInfo.setColorParameter(param);
								}
							}
						}
					}
				}
			}
		}
	}
	
}
