package charactermanaj.ui;

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JSpinner;
import javax.swing.JTabbedPane;
import javax.swing.SpinnerNumberModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import charactermanaj.graphics.filters.ColorConv;
import charactermanaj.graphics.filters.ColorConvertParameter;
import charactermanaj.model.AppConfig;
import charactermanaj.model.ColorGroup;
import charactermanaj.model.Layer;
import charactermanaj.model.PartsCategory;
import charactermanaj.model.PartsIdentifier;
import charactermanaj.ui.model.ColorChangeEvent;
import charactermanaj.ui.model.ColorChangeListener;
import charactermanaj.util.LocalizedResourcePropertyLoader;


public class ColorDialog extends JDialog {

	private static final long serialVersionUID = 1L;
	
	private final PartsCategory partsCategory;
	
	private HashMap<Layer, ColorDialogTabPanel> tabs = new HashMap<Layer, ColorDialogTabPanel>();
	
	private LinkedList<ColorChangeListener> listeners = new LinkedList<ColorChangeListener>();
	
	private String captionBase;
	
	/**
	 * 現在表示しているカラー情報の対象としているパーツ識別子
	 */
	private PartsIdentifier partsIdentifier;
	
	private JCheckBox chkApplyAll;
	
	public ColorDialog(JFrame parent, PartsCategory partsCategory, Collection<ColorGroup> colorGroups) {
		super(parent);
		this.partsCategory = partsCategory;

		final Properties strings = LocalizedResourcePropertyLoader.getInstance().getLocalizedProperties("strings/colordialog");
		
		String caption = strings.getProperty("colordialog.caption");
		String name = partsCategory.getLocalizedCategoryName();
		captionBase = caption + name;
		setTitle(captionBase);
		
		addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosing(WindowEvent e) {
				setVisible(false);
			}
		});
		
		Container container = getContentPane();
		
		final JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.TOP, JTabbedPane.WRAP_TAB_LAYOUT);
		for (final Layer layer : partsCategory.getLayers()) {
			
			ColorDialogTabPanel tabContainer = new ColorDialogTabPanel(this, layer, colorGroups);
			final ColorChangeListener innerListener = new ColorChangeListener() {
				private Semaphore semaphore = new Semaphore(1); // イベントが循環発生することを防ぐ
				public void onColorChange(ColorChangeEvent event) {
					if (semaphore.tryAcquire()) {
						try {
							ColorDialog.this.fireColorChangeEvent(layer, false);
						} finally {
							semaphore.release();
						}
					}
				}
				public void onColorGroupChange(ColorChangeEvent event) {
					if (semaphore.tryAcquire()) {
						try {
							ColorDialog.this.fireColorGroupChangeEvent(layer);
							ColorDialog.this.fireColorChangeEvent(layer, false);
						} finally {
							semaphore.release();
						}
					}
				}
			};
			tabContainer.addColorChangeListener(innerListener);

			tabbedPane.addTab(layer.getLocalizedName(), tabContainer);
			tabs.put(layer, tabContainer);
		}

		JPanel btnPanel = new JPanel(new BorderLayout());
		btnPanel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
		JButton btnApply = new JButton(strings.getProperty("button.apply"));
		btnApply.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				for (Layer layer : getPartsCategory().getLayers()) {
					ColorDialog.this.fireColorChangeEvent(layer, true);
				}
			}
		});
		btnPanel.add(btnApply, BorderLayout.EAST);
		chkApplyAll = new JCheckBox(strings.getProperty("checkbox.applyAllItems"));
		chkApplyAll.setSelected(!partsCategory.isMultipleSelectable());
		btnPanel.add(chkApplyAll, BorderLayout.WEST);

		container.setLayout(new BorderLayout());
		container.add(tabbedPane, BorderLayout.CENTER);
		container.add(btnPanel, BorderLayout.SOUTH);
		
		getRootPane().setDefaultButton(btnApply);
		
		pack();
	}
	
	public void adjustLocation(int offset_y) {
		Point pt = getParent().getLocation();
		Insets insets = getParent().getInsets();
		pt.x += getParent().getWidth();
		pt.y += (offset_y * insets.top);
		setLocation(pt);
	}
	
	public PartsCategory getPartsCategory() {
		return partsCategory;
	}
	
	public boolean isSyncColorGroup(Layer layer) {
		ColorDialogTabPanel tab = tabs.get(layer);
		if (tab == null) {
			return false;
		}
		return tab.isSyncColorGroup();
	}
	
	public void setSyncColorGroup(Layer layer, boolean selected) {
		ColorDialogTabPanel tab = tabs.get(layer);
		if (tab != null) {
			tab.setSyncColorGroup(selected);
		}
	}

	public void setColorConvertParameters(Map<Layer, ColorConvertParameter> params) {
		if (params == null) {
			throw new IllegalArgumentException();
		}
		for (Layer layer : partsCategory.getLayers()) {
			ColorConvertParameter param = params.get(layer);
			if (param == null) {
				param = new ColorConvertParameter();
			}
			setColorConvertParameter(layer, param);
		}
	}
	
	public void setPartsIdentifier(PartsIdentifier partsIdentifier) {
		this.partsIdentifier = partsIdentifier;
		if (partsIdentifier == null) {
			setTitle(captionBase);
		} else {
			setTitle(captionBase + "(" + partsIdentifier.getLocalizedPartsName() + ")");
		}
	}
	
	public PartsIdentifier getPartsIdentifier() {
		return partsIdentifier;
	}
	
	public boolean isApplyAll() {
		return chkApplyAll.isSelected();
	}
	
	public Map<Layer, ColorConvertParameter> getColorConvertParameters() {
		HashMap<Layer, ColorConvertParameter> params = new HashMap<Layer, ColorConvertParameter>();
		for (Layer layer : partsCategory.getLayers()) {
			ColorDialogTabPanel tab = tabs.get(layer);
			ColorConvertParameter param = tab.getColorConvertParameter();
			params.put(layer, param);
		}
		return params;
	}
	
	public void setColorConvertParameter(Layer layer, ColorConvertParameter param) {
		if (layer == null || param == null) {
			throw new IllegalArgumentException();
		}
		ColorDialogTabPanel tab = tabs.get(layer);
		if (tab == null) {
			throw new IllegalArgumentException("layer not found. " + layer + "/tabs=" + tabs);
		}
		tab.setColorConvertParameter(param);
	}
	
	public ColorConvertParameter getColorConvertParameter(Layer layer) {
		if (layer == null) {
			throw new IllegalArgumentException();
		}
		ColorDialogTabPanel tab = tabs.get(layer);
		if (tab == null) {
			throw new IllegalArgumentException("layer not found. " + layer);
		}
		return tab.getColorConvertParameter();
	}
	
	public ColorGroup getColorGroup(Layer layer) {
		if (layer == null) {
			throw new IllegalArgumentException();
		}
		ColorDialogTabPanel tab = tabs.get(layer);
		if (tab == null) {
			throw new IllegalArgumentException("layer not found. " + layer);
		}
		return tab.getColorGroup();
	}
	
	public void setColorGroup(Layer layer, ColorGroup colorGroup) {
		if (layer == null || colorGroup == null) {
			throw new IllegalArgumentException();
		}
		ColorDialogTabPanel tab = tabs.get(layer);
		if (tab != null) {
			tab.setColorGroup(colorGroup);
		}
	}
	
	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(Layer layer, boolean force) {
		if (layer == null) {
			throw new IllegalArgumentException();
		}
		if (!force) {
			AppConfig appConfig = AppConfig.getInstance();
			if (!appConfig.isEnableAutoColorChange()) {
				return;
			}
		}
		ColorChangeEvent event = new ColorChangeEvent(this, layer);
		for (ColorChangeListener listener : listeners) {
			listener.onColorChange(event);
		}
	}
	
	protected void fireColorGroupChangeEvent(Layer layer) {
		if (layer == null) {
			throw new IllegalArgumentException();
		}
		ColorChangeEvent event = new ColorChangeEvent(this, layer);
		for (ColorChangeListener listener : listeners) {
			listener.onColorGroupChange(event);
		}
	}
	
	@Override
	public String toString() {
		return "ColorDialog(partsCategory:" + partsCategory + ")";
	}
}


class ColorDialogTabPanel extends JPanel {
	private static final long serialVersionUID = 1L;

	private JComboBox cmbColorReplace;
	
	private JSpinner txtGray;
	
	private JSpinner txtOffsetR;
	
	private JSpinner txtOffsetG;
	
	private JSpinner txtOffsetB;
	
	private JSpinner txtOffsetA;

	private JSpinner txtFactorR;
	
	private JSpinner txtFactorG;
	
	private JSpinner txtFactorB;
	
	private JSpinner txtFactorA;
	
	private JSpinner txtHue;
	
	private JSpinner txtSaturation;
	
	private JSpinner txtBrightness;

	private JSpinner txtGammaR;

	private JSpinner txtGammaG;
	
	private JSpinner txtGammaB;
	
	private JSpinner txtGammaA;
	
	private JComboBox cmbColorGroup;
	
	private JCheckBox chkColorGroupSync;

	private final ColorDialog parent;
	
	private AtomicInteger disableEvent = new AtomicInteger();
	
	private ColorConvertParameter chachedParam;
	
	private LinkedList<ColorChangeListener> listeners = new LinkedList<ColorChangeListener>();
	
	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(Layer layer) {
		if (layer == null) {
			throw new IllegalArgumentException();
		}
		chachedParam = null;
		if (disableEvent.get() <= 0) {
			ColorChangeEvent event = new ColorChangeEvent(parent, layer);
			for (ColorChangeListener listener : listeners) {
				listener.onColorChange(event);
			}
		}
	}
	
	protected void fireColorGroupChangeEvent(Layer layer) {
		if (layer == null) {
			throw new IllegalArgumentException();
		}
		chachedParam = null;
		if (disableEvent.get() <= 0) {
			ColorChangeEvent event = new ColorChangeEvent(parent, layer);
			for (ColorChangeListener listener : listeners) {
				listener.onColorGroupChange(event);
			}
		}
	}

	public ColorDialogTabPanel(final ColorDialog parent, final Layer layer, Collection<ColorGroup> colorGroups) {
		if (parent == null || layer == null || colorGroups == null) {
			throw new IllegalArgumentException();
		}
		this.parent = parent;

		final Properties strings = LocalizedResourcePropertyLoader.getInstance().getLocalizedProperties("strings/colordialog");

		setLayout(new BorderLayout());
		JPanel container = new JPanel();
		BoxLayout boxlayout = new BoxLayout(container, BoxLayout.PAGE_AXIS);
		container.setLayout(boxlayout);
		add(container, BorderLayout.NORTH);
		
		// 変更イベントハンドラ
		final ChangeListener changeEventHandler = new ChangeListener() {
			public void stateChanged(ChangeEvent e) {
				fireColorChangeEvent(layer);
			}
		};
		
		// 色置換パネル
		
		JPanel colorReplacePanel = new JPanel();
		colorReplacePanel.setBorder(BorderFactory.createCompoundBorder(
				BorderFactory.createEmptyBorder(3, 3, 3, 3),
				BorderFactory.createTitledBorder(strings.getProperty("group.replacergb.caption"))));
		GridBagLayout gbl = new GridBagLayout();
		colorReplacePanel.setLayout(gbl);
		GridBagConstraints gbc = new GridBagConstraints();
		
		JLabel lblColorReplace = new JLabel(strings.getProperty("replacergb"), JLabel.RIGHT);
		cmbColorReplace = new JComboBox(ColorConv.values());
		JLabel lblGray = new JLabel(strings.getProperty("bright"), JLabel.RIGHT);
		SpinnerNumberModel grayModel = new SpinnerNumberModel(1., 0., 1., 0.05);
		grayModel.addChangeListener(changeEventHandler);
		cmbColorReplace.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				fireColorChangeEvent(layer);
			}
		});
		txtGray = new JSpinner(grayModel);
		
		gbc.gridx = 0;
		gbc.gridy = 0;
		gbc.gridwidth = 1;
		gbc.gridheight = 1;
		gbc.weightx = 0.;
		gbc.weighty = 0.;
		gbc.anchor = GridBagConstraints.WEST;
		gbc.fill = GridBagConstraints.BOTH;
		gbc.ipadx = 0;
		gbc.ipady = 0;
		gbc.insets = new Insets(3, 3, 3, 3);
		colorReplacePanel.add(lblColorReplace, gbc);
		
		gbc.gridx = 1;
		gbc.gridy = 0;
		gbc.gridwidth = 1;
		gbc.gridheight = 1;
		gbc.weightx = 1.;
		gbc.weighty = 0.;
		colorReplacePanel.add(cmbColorReplace, gbc);
		
		gbc.gridx = 2;
		gbc.gridy = 0;
		gbc.gridwidth = 1;
		gbc.gridheight = 1;
		gbc.weightx = 0.;
		gbc.weighty = 0.;
		colorReplacePanel.add(lblGray, gbc);

		gbc.gridx = 3;
		gbc.gridy = 0;
		gbc.weightx = 1.;
		gbc.weighty = 0.;
		gbc.gridwidth = 1;
		gbc.gridheight = 1;
		colorReplacePanel.add(txtGray, gbc);

		// RGB変更パネル
		
		JPanel colorLevelPanel = new JPanel();
		colorLevelPanel.setBorder(BorderFactory.createCompoundBorder(
				BorderFactory.createEmptyBorder(3, 3, 3, 3),
				BorderFactory.createTitledBorder(strings.getProperty("group.rgb.caption"))));
		GridLayout gl = new GridLayout(4, 5);
		gl.setHgap(2);
		gl.setVgap(2);
		colorLevelPanel.setLayout(gl);
		colorLevelPanel.add(Box.createGlue());
		colorLevelPanel.add(new JLabel(strings.getProperty("red"), JLabel.CENTER));
		colorLevelPanel.add(new JLabel(strings.getProperty("green"), JLabel.CENTER));
		colorLevelPanel.add(new JLabel(strings.getProperty("blue"), JLabel.CENTER));
		colorLevelPanel.add(new JLabel(strings.getProperty("alpha"), JLabel.CENTER));
		colorLevelPanel.add(new JLabel(strings.getProperty("offset"), JLabel.RIGHT));
		SpinnerNumberModel offsetModelR = new SpinnerNumberModel(0, -255, 255, 1);
		SpinnerNumberModel offsetModelG = new SpinnerNumberModel(0, -255, 255, 1);
		SpinnerNumberModel offsetModelB = new SpinnerNumberModel(0, -255, 255, 1);
		SpinnerNumberModel offsetModelA = new SpinnerNumberModel(0, -255, 255, 1);
		offsetModelR.addChangeListener(changeEventHandler);
		offsetModelG.addChangeListener(changeEventHandler);
		offsetModelB.addChangeListener(changeEventHandler);
		offsetModelA.addChangeListener(changeEventHandler);
		txtOffsetR = new JSpinner(offsetModelR);
		txtOffsetG = new JSpinner(offsetModelG);
		txtOffsetB = new JSpinner(offsetModelB);
		txtOffsetA = new JSpinner(offsetModelA);
		colorLevelPanel.add(txtOffsetR);
		colorLevelPanel.add(txtOffsetG);
		colorLevelPanel.add(txtOffsetB);
		colorLevelPanel.add(txtOffsetA);
		colorLevelPanel.add(new JLabel(strings.getProperty("factor"), JLabel.RIGHT));
		SpinnerNumberModel factorModelR = new SpinnerNumberModel(1., 0.01, 99, 0.01);
		SpinnerNumberModel factorModelG = new SpinnerNumberModel(1., 0.01, 99, 0.01);
		SpinnerNumberModel factorModelB = new SpinnerNumberModel(1., 0.01, 99, 0.01);
		SpinnerNumberModel factorModelA = new SpinnerNumberModel(1., 0.01, 99, 0.01);
		factorModelR.addChangeListener(changeEventHandler);
		factorModelG.addChangeListener(changeEventHandler);
		factorModelB.addChangeListener(changeEventHandler);
		factorModelA.addChangeListener(changeEventHandler);
		txtFactorR = new JSpinner(factorModelR);
		txtFactorG = new JSpinner(factorModelG);
		txtFactorB = new JSpinner(factorModelB);
		txtFactorA = new JSpinner(factorModelA);
		colorLevelPanel.add(txtFactorR);
		colorLevelPanel.add(txtFactorG);
		colorLevelPanel.add(txtFactorB);
		colorLevelPanel.add(txtFactorA);
		colorLevelPanel.add(new JLabel(strings.getProperty("gamma"), JLabel.RIGHT));
		SpinnerNumberModel gammaModelR = new SpinnerNumberModel(1., 0.01, 99, 0.01);
		SpinnerNumberModel gammaModelG = new SpinnerNumberModel(1., 0.01, 99, 0.01);
		SpinnerNumberModel gammaModelB = new SpinnerNumberModel(1., 0.01, 99, 0.01);
		SpinnerNumberModel gammaModelA = new SpinnerNumberModel(1., 0.01, 99, 0.01);
		gammaModelR.addChangeListener(changeEventHandler);
		gammaModelG.addChangeListener(changeEventHandler);
		gammaModelB.addChangeListener(changeEventHandler);
		gammaModelA.addChangeListener(changeEventHandler);
		txtGammaR = new JSpinner(gammaModelR);
		txtGammaG = new JSpinner(gammaModelG);
		txtGammaB = new JSpinner(gammaModelB);
		txtGammaA = new JSpinner(gammaModelA);
		colorLevelPanel.add(txtGammaR);
		colorLevelPanel.add(txtGammaG);
		colorLevelPanel.add(txtGammaB);
		colorLevelPanel.add(txtGammaA);
		
		// 色調パネル
		
		JPanel colorTunePanel = new JPanel();
		colorTunePanel.setBorder(BorderFactory.createCompoundBorder(
				BorderFactory.createEmptyBorder(3, 3, 3, 3),
				BorderFactory.createTitledBorder(strings.getProperty("group.hsb.caption"))));
		GridLayout gl2 = new GridLayout(3, 3);
		gl2.setHgap(3);
		gl2.setVgap(3);
		colorTunePanel.setLayout(gl2);
		colorTunePanel.add(new JLabel(strings.getProperty("hue"), JLabel.CENTER)); // Hue 色相
		colorTunePanel.add(new JLabel(strings.getProperty("saturation"), JLabel.CENTER)); // Saturation 彩度
		colorTunePanel.add(new JLabel(strings.getProperty("brightness"), JLabel.CENTER)); // Brightness 明度
		SpinnerNumberModel hsbModelH = new SpinnerNumberModel(0., -1., 1., 0.01);
		SpinnerNumberModel hsbModelS = new SpinnerNumberModel(0., -1., 1., 0.01);
		SpinnerNumberModel hsbModelB = new SpinnerNumberModel(0., -1., 1., 0.01);
		hsbModelH.addChangeListener(changeEventHandler);
		hsbModelS.addChangeListener(changeEventHandler);
		hsbModelB.addChangeListener(changeEventHandler);
		txtHue = new JSpinner(hsbModelH);
		txtSaturation = new JSpinner(hsbModelS);
		txtBrightness = new JSpinner(hsbModelB);
		colorTunePanel.add(txtHue);
		colorTunePanel.add(txtSaturation);
		colorTunePanel.add(txtBrightness);
		JSlider sliderHue = new JSlider();
		JSlider sliderSaturation = new JSlider();
		JSlider sliderBrightness = new JSlider();
		sliderHue.setPreferredSize(txtHue.getPreferredSize());
		sliderSaturation.setPreferredSize(txtSaturation.getPreferredSize());
		sliderBrightness.setPreferredSize(txtBrightness.getPreferredSize());
		colorTunePanel.add(sliderHue);
		colorTunePanel.add(sliderSaturation);
		colorTunePanel.add(sliderBrightness);

		JSlider sliders[] = new JSlider[] {sliderHue, sliderSaturation, sliderBrightness};
		JSpinner spinners[] = new JSpinner[] {txtHue, txtSaturation, txtBrightness};
		
		for (int idx = 0; idx < spinners.length; idx++) {
			final JSlider sl = sliders[idx];
			final JSpinner sp = spinners[idx];
			sl.setMinimum(-100);
			sl.setMaximum(100);
			sl.setValue((int)(((Number) sp.getValue()).doubleValue() * 100.));
			final Semaphore loopBlocker = new Semaphore(1); // イベントが循環発生することを防ぐ
			sl.addChangeListener(new ChangeListener() {
				public void stateChanged(ChangeEvent e) {
					if (loopBlocker.tryAcquire()) {
						try {
							double rate = sl.getValue() / 100.;
							sp.setValue(Double.valueOf(rate));
						} finally {
							loopBlocker.release();
						}
					}
				}
			});
			sp.addChangeListener(new ChangeListener() {
				public void stateChanged(ChangeEvent e) {
					if (loopBlocker.tryAcquire()) {
						try {
							int rate = (int)(((Number) sp.getValue()).doubleValue() * 100.);
							sl.setValue(rate);
						} finally {
							loopBlocker.release();
						}
					}
				}
			});
		}
		
		// カラーグループ
		ColorGroup colorGroup = layer.getColorGroup();
		JPanel colorGroupPanel = new JPanel();
		colorGroupPanel.setBorder(BorderFactory.createCompoundBorder(
				BorderFactory.createEmptyBorder(3, 3, 3, 3),
				BorderFactory.createTitledBorder(strings.getProperty("colorgroup"))));
		GridBagLayout gbl2 = new GridBagLayout();
		colorGroupPanel.setLayout(gbl2);
		GridBagConstraints gbc2 = new GridBagConstraints();

		JLabel lblColorGroup = new JLabel(strings.getProperty("group"), JLabel.RIGHT);
		cmbColorGroup = new JComboBox(colorGroups.toArray(new ColorGroup[colorGroups.size()]));
		cmbColorGroup.setSelectedItem(colorGroup);
		cmbColorGroup.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				ColorGroup selColorGroup = (ColorGroup) cmbColorGroup.getSelectedItem();
				chkColorGroupSync.setSelected(selColorGroup.isEnabled());
				fireColorGroupChangeEvent(layer);
			}
		});
		chkColorGroupSync = new JCheckBox(strings.getProperty("synchronized"));
		chkColorGroupSync.setSelected(layer.isInitSync());

		gbc2.gridx = 0;
		gbc2.gridy = 0;
		gbc2.gridwidth = 1;
		gbc2.gridheight = 1;
		gbc2.weightx = 0.;
		gbc2.weighty = 0.;
		gbc2.anchor = GridBagConstraints.WEST;
		gbc2.fill = GridBagConstraints.BOTH;
		gbc2.ipadx = 0;
		gbc2.ipady = 0;
		gbc2.insets = new Insets(3, 3, 3, 3);
		colorGroupPanel.add(lblColorGroup, gbc2);
		
		gbc2.gridx = 1;
		gbc2.gridy = 0;
		gbc2.gridwidth = 1;
		gbc2.gridheight = 1;
		gbc2.weightx = 1.;
		gbc2.weighty = 0.;
		colorGroupPanel.add(cmbColorGroup, gbc2);
		
		gbc2.gridx = 2;
		gbc2.gridy = 0;
		gbc2.gridwidth = GridBagConstraints.REMAINDER;
		gbc2.gridheight = 1;
		gbc2.weightx = 0.;
		gbc2.weighty = 0.;
		colorGroupPanel.add(chkColorGroupSync, gbc2);
		
		if (colorGroupPanel != null) {
			container.add(colorGroupPanel);
		}
		container.add(colorLevelPanel);
		container.add(colorReplacePanel);
		container.add(colorTunePanel);
	}
	
	public void setColorConvertParameter(ColorConvertParameter param) {
		if (param == null) {
			throw new IllegalArgumentException();
		}
		ColorConv colorReplace = param.getColorReplace();
		if (colorReplace == null) {
			colorReplace = ColorConv.NONE;
		}
		disableEvent.incrementAndGet();
		try {
			cmbColorReplace.setSelectedItem(colorReplace);
			txtGray.setValue(Double.valueOf(param.getGrayLevel()));
			txtOffsetR.setValue(Integer.valueOf(param.getOffsetR()));
			txtOffsetG.setValue(Integer.valueOf(param.getOffsetG()));
			txtOffsetB.setValue(Integer.valueOf(param.getOffsetB()));
			txtOffsetA.setValue(Integer.valueOf(param.getOffsetA()));
			txtFactorR.setValue(Double.valueOf(param.getFactorR()));
			txtFactorG.setValue(Double.valueOf(param.getFactorG()));
			txtFactorB.setValue(Double.valueOf(param.getFactorB()));
			txtFactorA.setValue(Double.valueOf(param.getFactorA()));
			txtGammaR.setValue(Double.valueOf(param.getGammaR()));
			txtGammaG.setValue(Double.valueOf(param.getGammaG()));
			txtGammaB.setValue(Double.valueOf(param.getGammaB()));
			txtGammaA.setValue(Double.valueOf(param.getGammaA()));
			txtHue.setValue(Double.valueOf(param.getHue()));
			txtSaturation.setValue(Double.valueOf(param.getSaturation()));
			txtBrightness.setValue(Double.valueOf(param.getBrightness()));
		} finally {
			disableEvent.decrementAndGet();
		}
		chachedParam = param;
	}
	
	public ColorConvertParameter getColorConvertParameter() {
		if (chachedParam != null) {
			return chachedParam;
		}
		ColorConvertParameter param = new ColorConvertParameter();
		param.setColorReplace((ColorConv) cmbColorReplace.getSelectedItem());
		param.setGrayLevel(((Number) txtGray.getValue()).floatValue());
		param.setOffsetR(((Number) txtOffsetR.getValue()).intValue());
		param.setOffsetG(((Number) txtOffsetG.getValue()).intValue());
		param.setOffsetB(((Number) txtOffsetB.getValue()).intValue());
		param.setOffsetA(((Number) txtOffsetA.getValue()).intValue());
		param.setFactorR(((Number) txtFactorR.getValue()).floatValue());
		param.setFactorG(((Number) txtFactorG.getValue()).floatValue());
		param.setFactorB(((Number) txtFactorB.getValue()).floatValue());
		param.setFactorA(((Number) txtFactorA.getValue()).floatValue());
		param.setGammaR(((Number) txtGammaR.getValue()).floatValue());
		param.setGammaG(((Number) txtGammaG.getValue()).floatValue());
		param.setGammaB(((Number) txtGammaB.getValue()).floatValue());
		param.setGammaA(((Number) txtGammaA.getValue()).floatValue());
		param.setHue(((Number) txtHue.getValue()).floatValue());
		param.setSaturation(((Number) txtSaturation.getValue()).floatValue());
		param.setBrightness(((Number) txtBrightness.getValue()).floatValue());
		chachedParam = param;
		return param;
	}
	
	public ColorGroup getColorGroup() {
		return (ColorGroup) cmbColorGroup.getSelectedItem();
	}
	
	public void setColorGroup(ColorGroup colorGroup) {
		if (colorGroup == null) {
			colorGroup = ColorGroup.NA;
		}
		disableEvent.incrementAndGet();
		try {
			cmbColorGroup.setSelectedItem(colorGroup);
		} finally {
			disableEvent.decrementAndGet();
		}
	}
	
	public boolean isSyncColorGroup() {
		return chkColorGroupSync == null ? false : chkColorGroupSync.isSelected();
	}
	
	public void setSyncColorGroup(boolean selected) {
		if (chkColorGroupSync != null) {
			disableEvent.incrementAndGet();
			try {
				chkColorGroupSync.setSelected(selected);
			} finally {
				disableEvent.decrementAndGet();
			}
		}
	}
}
