/*******************************************************************************
 * Copyright (c) MOBAC developers
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/
package mobac.gui.panels;

import java.awt.Dimension;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;
import java.util.TreeSet;

import javax.swing.AbstractListModel;
import javax.swing.ComboBoxModel;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

import mobac.gui.components.JCollapsiblePanel;
import mobac.gui.components.JTileSizeCombo;
import mobac.program.annotations.SupportedParameters;
import mobac.program.atlascreators.AtlasCreator;
import mobac.program.model.AtlasOutputFormat;
import mobac.program.model.Settings;
import mobac.program.model.TileImageFormat;
import mobac.program.model.TileImageParameters;
import mobac.program.model.TileImageParameters.Name;
import mobac.program.tiledatawriter.TileImageJpegDataWriter;
import mobac.utilities.GBC;
import mobac.utilities.I18nUtils;
import mobac.utilities.Utilities;

public class JTileImageParametersPanel extends JCollapsiblePanel {

	private static final long serialVersionUID = 1L;
	private static boolean JPEG_TESTED = false;

	private JCheckBox enableCustomTileProcessingCheckButton;
	private JLabel tileSizeWidthLabel;
	private JLabel tileSizeHeightLabel;
	private JLabel tileImageFormatLabel;
	private JTileSizeCombo tileSizeWidth;
	private JTileSizeCombo tileSizeHeight;
	private JComboBox<TileImageFormat> tileImageFormat;

	private boolean widthEnabled = true;
	private boolean heightEnabled = true;
	private boolean formatPngEnabled = true;
	private boolean formatJpgEnabled = true;

	public JTileImageParametersPanel() {
		super(I18nUtils.localizedStringForKey("lp_tile_param_title"), new GridBagLayout());
		setName("TileImageParameters");

		enableCustomTileProcessingCheckButton = new JCheckBox(
				I18nUtils.localizedStringForKey("lp_tile_param_recreate_checkbox_title"));
		enableCustomTileProcessingCheckButton.addActionListener(new EnableCustomTileSizeCheckButtonListener());
		enableCustomTileProcessingCheckButton.setToolTipText(I18nUtils
				.localizedStringForKey("lp_tile_param_recreate_checkbox_tips"));

		tileSizeWidthLabel = new JLabel(I18nUtils.localizedStringForKey("lp_tile_param_width_title"));
		tileSizeWidth = new JTileSizeCombo();
		tileSizeWidth.setToolTipText(I18nUtils.localizedStringForKey("lp_tile_param_width_tips"));

		tileSizeHeightLabel = new JLabel(I18nUtils.localizedStringForKey("lp_tile_param_height_title"));
		tileSizeHeight = new JTileSizeCombo();
		tileSizeHeight.setToolTipText(I18nUtils.localizedStringForKey("lp_tile_param_height_tips"));

		tileImageFormatLabel = new JLabel(I18nUtils.localizedStringForKey("lp_tile_param_image_fmt_title"));
		tileImageFormat = new JComboBox<TileImageFormat>(new TileFormatComboModel(TileImageFormat.values()));
		tileImageFormat.setMaximumRowCount(tileImageFormat.getItemCount());
		tileImageFormat.addActionListener(new TileImageFormatListener());

		GBC gbc_std = GBC.std().insets(5, 2, 5, 3);
		GBC gbc_eol = GBC.eol().insets(5, 2, 5, 3);

		JPanel tileSizePanel = new JPanel(new GridBagLayout());
		tileSizePanel.add(tileSizeWidthLabel, gbc_std);
		tileSizePanel.add(tileSizeWidth, gbc_std);
		tileSizePanel.add(tileSizeHeightLabel, gbc_std);
		tileSizePanel.add(tileSizeHeight, gbc_eol);
		JPanel tileColorDepthPanel = new JPanel();
		tileColorDepthPanel.add(tileImageFormatLabel);
		tileColorDepthPanel.add(tileImageFormat);
		contentContainer.add(enableCustomTileProcessingCheckButton, gbc_eol);
		contentContainer.add(tileSizePanel, GBC.eol());
		contentContainer.add(tileColorDepthPanel, GBC.eol());
	}

	public void loadSettings() {
		Settings settings = Settings.getInstance();
		enableCustomTileProcessingCheckButton.setSelected(settings.isCustomTileSize());
		updateControlsState();
		tileImageFormat.setSelectedItem(settings.getTileImageFormat());
		tileSizeHeight.setValue(settings.getTileSize().height);
		tileSizeWidth.setValue(settings.getTileSize().width);
	}

	public void saveSettings() {
		Settings settings = Settings.getInstance();
		settings.setCustomTileSize(enableCustomTileProcessingCheckButton.isSelected());
		Dimension tileSize = new Dimension(tileSizeWidth.getValue(), tileSizeHeight.getValue());
		settings.setTileSize(tileSize);
		settings.setTileImageFormat((TileImageFormat) tileImageFormat.getSelectedItem());
	}

	public TileImageParameters getSelectedTileImageParameters() {
		TileImageParameters customTileParameters = null;
		boolean customTileSize = enableCustomTileProcessingCheckButton.isSelected();
		if (customTileSize) {
			int width = tileSizeWidth.getValue();
			int height = tileSizeHeight.getValue();
			TileImageFormat format = (mobac.program.model.TileImageFormat) tileImageFormat.getSelectedItem();
			customTileParameters = new TileImageParameters(width, height, format);
		}
		return customTileParameters;
	}

	public void atlasFormatChanged(AtlasOutputFormat newAtlasOutputFormat) {
		Class<? extends AtlasCreator> atlasCreatorClass = newAtlasOutputFormat.getMapCreatorClass();
		SupportedParameters params = atlasCreatorClass.getAnnotation(SupportedParameters.class);
		if (params != null) {
			TreeSet<TileImageParameters.Name> paramNames = new TreeSet<TileImageParameters.Name>(Arrays.asList(params
					.names()));
			if (paramNames.contains(Name.format)) {
				formatPngEnabled = true;
				formatJpgEnabled = true;
			} else {
				formatPngEnabled = paramNames.contains(Name.format_png);
				formatJpgEnabled = paramNames.contains(Name.format_jpg);
			}
			widthEnabled = paramNames.contains(Name.width);
			heightEnabled = paramNames.contains(Name.height);
			enableCustomTileProcessingCheckButton.setEnabled(true);
		} else {
			formatPngEnabled = false;
			formatJpgEnabled = false;
			widthEnabled = false;
			heightEnabled = false;
			enableCustomTileProcessingCheckButton.setEnabled(false);
		}
		updateControlsState();
	}

	public void updateControlsState() {
		boolean b = false;
		if (enableCustomTileProcessingCheckButton.isEnabled())
			b = enableCustomTileProcessingCheckButton.isSelected();
		tileSizeWidth.setEnabled(b && widthEnabled);
		tileSizeWidthLabel.setEnabled(b && widthEnabled);
		tileSizeHeightLabel.setEnabled(b && heightEnabled);
		tileSizeHeight.setEnabled(b && heightEnabled);
		boolean formatEnabled = formatJpgEnabled || formatPngEnabled;
		tileImageFormatLabel.setEnabled(b && formatEnabled);
		tileImageFormat.setEnabled(b && formatEnabled);
		if (formatPngEnabled && !formatJpgEnabled)
			updateFormatComboModel(TileImageFormat.getPngFormats());
		else if (!formatPngEnabled && formatJpgEnabled)
			updateFormatComboModel(TileImageFormat.getJpgFormats());
		else
			updateFormatComboModel(TileImageFormat.values());
	}

	private void updateFormatComboModel(TileImageFormat[] values) {
		TileFormatComboModel model = (TileFormatComboModel) tileImageFormat.getModel();
		model.changeValues(values);
	}

	public String getValidationErrorMessages() {
		String errorText = "";
		if (!enableCustomTileProcessingCheckButton.isSelected())
			return errorText;
		if (!tileSizeHeight.isValueValid())
			errorText += String.format(I18nUtils.localizedStringForKey("lp_tile_param_msg_valid_height"),
					JTileSizeCombo.MIN, JTileSizeCombo.MAX);

		if (!tileSizeWidth.isValueValid())
			errorText += String.format(I18nUtils.localizedStringForKey("lp_tile_param_msg_valid_width"),
					JTileSizeCombo.MIN, JTileSizeCombo.MAX);
		return errorText;
	}

	private class EnableCustomTileSizeCheckButtonListener implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			updateControlsState();
		}
	}

	private class TileImageFormatListener implements ActionListener {

		public void actionPerformed(ActionEvent event) {
			if (!tileImageFormat.isEnabled())
				return;
			TileImageFormat tif = (TileImageFormat) tileImageFormat.getSelectedItem();
			if (tif == null)
				return;
			if (!JPEG_TESTED && (tif.getDataWriter() instanceof TileImageJpegDataWriter)) {
				if (!TileImageJpegDataWriter.performOpenJDKJpegTest())
					JOptionPane.showMessageDialog(null, "<html>The JPEG image format is not supported by OpenJDK.<br>"
							+ "Please select a different tile format.</html>", "Image format not available on OpenJDK",
							JOptionPane.ERROR_MESSAGE);
				JPEG_TESTED = true;
			} else if (tif == TileImageFormat.PNG4Bit || tif == TileImageFormat.PNG8Bit) {
				if (Utilities.testJaiColorQuantizerAvailable())
					return;
				JOptionPane.showMessageDialog(null,
						"<html>This image format is requires additional libraries to be installed:<br>"
								+ "<b>Java Advanced Image library</b> (jai_core.jar & jai_codec.jar)<br>"
								+ "For more details please see the file <b>README.HTM</b> "
								+ "in section <b>Requirements</b>.</html>",
						"Image format not available - libraries missing", JOptionPane.ERROR_MESSAGE);
				tileImageFormat.setSelectedIndex(0);
			}
		}
	}

	private class TileFormatComboModel extends AbstractListModel<TileImageFormat> implements
			ComboBoxModel<TileImageFormat> {

		TileImageFormat[] values;
		Object selectedObject = null;

		public TileFormatComboModel(TileImageFormat[] values) {
			super();
			this.values = values;
			if (values.length > 0)
				selectedObject = values[0];
		}

		public void changeValues(TileImageFormat[] values) {
			this.values = values;
			boolean found = false;
			for (TileImageFormat format : values) {
				if (format.equals(selectedObject)) {
					found = true;
					break;
				}
			}
			if (!found)
				selectedObject = values[0];
			fireContentsChanged(this, -1, -1);
		}

		public int getSize() {
			return values.length;
		}

		public TileImageFormat getElementAt(int index) {
			return values[index];
		}

		@Override
		public void setSelectedItem(Object anItem) {
			if ((selectedObject != null && !selectedObject.equals(anItem)) || selectedObject == null && anItem != null) {
				selectedObject = anItem;
				fireContentsChanged(this, -1, -1);
			}
		}

		@Override
		public Object getSelectedItem() {
			return selectedObject;
		}

	}

}
