package tim.prune.function.settings;

import tim.prune.*;
import tim.prune.config.Config;
import tim.prune.gui.GuiGridLayout;
import tim.prune.gui.IconManager;
import tim.prune.gui.colour.WaypointColours;
import tim.prune.gui.map.WpIconLibrary;
import tim.prune.load.GenericFileFilter;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.image.BufferedImage;
import java.io.File;

import javax.swing.*;
import javax.swing.border.EtchedBorder;


/**
 * Class to show the dialog for setting the waypoint display settings
 * like icon style, size, colours
 */
public class SetWaypointSettings extends GenericFunction
{
	/**
	 * Inner class to render waypoint icons
	 */
	static class IconComboRenderer extends JLabel implements ListCellRenderer<Integer>
	{
		/** Cached icons for each waypoint type */
		private final ImageIcon[] _icons = new ImageIcon[WpIconLibrary.WAYPT_NUMBER_OF_ICONS];
		private final IconManager _iconManager;

		/** Constructor */
		IconComboRenderer(IconManager inIconManager) {
			setOpaque(true);
			_iconManager = inIconManager;
		}

		/** Get the label text at the given index */
		private String getLabel(int inIndex) {
			return I18nManager.getText("dialog.displaysettings.wpicon." + WpIconLibrary.getIconName(inIndex));
		}

		/** Get the image icon at the given index */
		private ImageIcon getIcon(int inIndex)
		{
			if (_icons[inIndex] == null) {
				_icons[inIndex] = WpIconLibrary.getFixedIconDefinition(inIndex, _iconManager).getImageIcon();
			}
			return _icons[inIndex];
		}

		/** @return a label to display the combo box entry */
		public Component getListCellRendererComponent(
			JList<? extends Integer> inList, Integer inValue, int inIndex,
			boolean inSelected, boolean inFocus)
		{
			if (inSelected) {
				setBackground(inList.getSelectionBackground());
				setForeground(inList.getSelectionForeground());
			}
			else {
				setBackground(inList.getBackground());
				setForeground(inList.getForeground());
			}
			setIcon(getIcon(inValue));
			setText(getLabel(inValue));
			return this;
		}
	}


	// Members of SetDisplaySettings
	private JDialog _dialog = null;
	private JComboBox<Integer> _wpIconCombobox = null;
	private JRadioButton[] _sizeRadioButtons = null;
	private JCheckBox _coloursCheckbox = null;
	private JCheckBox _customIconCheckbox = null;
	private JButton _browseButton = null;
	private File _selectedIconFile = null;
	/** Slider for salt */
	private JSlider _saltSlider = null;
	private JLabel _saltLabel = null;
	private JLabel _noTypesLabel = null;
	/** Type information */
	private final WaypointTypeList _typeList = new WaypointTypeList();
	private JList<String> _typeListBox = null;
	private JScrollPane _listScroller = null;


	/**
	 * Constructor
	 * @param inApp app object
	 */
	public SetWaypointSettings(App inApp) {
		super(inApp);
	}

	/**
	 * Return the name key for this function
	 */
	public String getNameKey() {
		return "function.setwaypointdisplay";
	}

	/**
	 * @return the contents of the window as a Component
	 */
	private Component makeContents()
	{
		JPanel mainPanel = new JPanel();
		mainPanel.setLayout(new BorderLayout(0, 5));
		JPanel midPanel = new JPanel();
		midPanel.setLayout(new BoxLayout(midPanel, BoxLayout.Y_AXIS));
		midPanel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));

		// Panel for waypoint icons
		JPanel iconsPanel = new JPanel();
		iconsPanel.setBorder(BorderFactory.createCompoundBorder(
			BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(3, 3, 3, 3))
		);
		iconsPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
		iconsPanel.setLayout(new BoxLayout(iconsPanel, BoxLayout.Y_AXIS));
		// Select which waypoint icon to use
		JPanel iconPanel = new JPanel();
		GuiGridLayout iconGrid = new GuiGridLayout(iconPanel);
		JLabel headerLabel = new JLabel(I18nManager.getText("dialog.displaysettings.waypointicons"));
		headerLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
		iconGrid.add(headerLabel);
		_wpIconCombobox = new JComboBox<>(new Integer[]{0, 1, 2, 3, 4, 5});
		_wpIconCombobox.setRenderer(new IconComboRenderer(getIconManager()));
		iconGrid.add(_wpIconCombobox);
		iconsPanel.add(iconPanel);
		// Select size of waypoints
		JPanel sizePanel = new JPanel();
		sizePanel.setLayout(new FlowLayout(FlowLayout.CENTER));
		_sizeRadioButtons = new JRadioButton[3];
		ButtonGroup sizeRadioGroup = new ButtonGroup();
		final String[] sizeKeys = {"small", "medium", "large"};
		for (int i=0; i<3; i++)
		{
			_sizeRadioButtons[i] = new JRadioButton(I18nManager.getText("dialog.displaysettings.size." + sizeKeys[i]));
			sizeRadioGroup.add(_sizeRadioButtons[i]);
			sizePanel.add(_sizeRadioButtons[i]);
		}
		iconsPanel.add(sizePanel);
		// Choose a custom waypoint icon
		JPanel customIconPanel = new JPanel();
		_customIconCheckbox = new JCheckBox(I18nManager.getText("dialog.waypointsettings.usecustomicon"));
		_customIconCheckbox.addActionListener(e -> updateCustomIconCheckbox());
		customIconPanel.add(_customIconCheckbox);
		_browseButton = new JButton(I18nManager.getText("button.browse"));
		_browseButton.addActionListener(e -> chooseCustomIconFile());
		_browseButton.setEnabled(false);
		customIconPanel.add(_browseButton);
		iconsPanel.add(customIconPanel);
		midPanel.add(iconsPanel);
		midPanel.add(Box.createVerticalStrut(15));

		// Panel for colours of waypoint icons
		JPanel coloursPanel = new JPanel();
		coloursPanel.setBorder(BorderFactory.createCompoundBorder(
			BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(3, 3, 3, 3))
		);
		coloursPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
		coloursPanel.setLayout(new BoxLayout(coloursPanel, BoxLayout.Y_AXIS));
		_coloursCheckbox = new JCheckBox(I18nManager.getText("dialog.waypointsettings.usecolours"));
		coloursPanel.add(_coloursCheckbox);
		coloursPanel.add(Box.createVerticalStrut(10));
		_saltSlider = new JSlider(0, WaypointColours.getMaxSalt());
		coloursPanel.add(_saltSlider);
		_saltLabel = new JLabel("some label");
		coloursPanel.add(_saltLabel);
		_noTypesLabel = new JLabel(I18nManager.getText("dialog.waypointsettings.notypesintrack"));
		coloursPanel.add(_noTypesLabel);
		_typeListBox = new JList<String>(_typeList);
		_typeListBox.setVisibleRowCount(4);
		_typeListBox.setLayoutOrientation(JList.HORIZONTAL_WRAP);
		_typeListBox.setCellRenderer(new DefaultListCellRenderer() {
			public Component getListCellRendererComponent(JList<?> list, Object value, int index,
					boolean isSelected, boolean cellHasFocus) {
				super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
				setText(value.toString());
				setHorizontalTextPosition(JLabel.LEFT);
				setIconTextGap(7);
				setIcon(makeImageIcon(value.toString(), _saltSlider.getValue()));
				return this;
			}
		});
		_listScroller = new JScrollPane(_typeListBox);
		coloursPanel.add(_listScroller);
		midPanel.add(coloursPanel);
		// attach signals
		_saltSlider.addChangeListener(changeEvent -> showPreview(_saltSlider.getValue()));
		_coloursCheckbox.addChangeListener(changeEvent -> coloursSwitched());

		mainPanel.add(midPanel, BorderLayout.CENTER);

		// button panel at bottom
		JPanel buttonPanel = new JPanel();
		buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
		// OK button
		JButton okButton = new JButton(I18nManager.getText("button.ok"));
		okButton.addActionListener(e -> finish());
		buttonPanel.add(okButton);
		// Cancel button
		JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
		cancelButton.addActionListener(e -> _dialog.dispose());
		buttonPanel.add(cancelButton);
		mainPanel.add(buttonPanel, BorderLayout.SOUTH);
		return mainPanel;
	}

	/** React to changes in the custom waypoint icon checkbox */
	private void updateCustomIconCheckbox()
	{
		boolean showFile = _customIconCheckbox.isSelected() && _selectedIconFile != null
			&& _selectedIconFile.exists() && _selectedIconFile.isFile()
			&& _selectedIconFile.canRead();
		String text = I18nManager.getText("dialog.waypointsettings.usecustomicon");
		if (showFile) {
			text = text + " (" + _selectedIconFile.getName() + ")";
		}
		_customIconCheckbox.setText(text);
		enableIconControls(_customIconCheckbox.isSelected());
	}

	/** Enable or disable the icon controls based on the custom checkbox */
	private void enableIconControls(boolean inCustom)
	{
		_wpIconCombobox.setEnabled(!inCustom);
		for (JRadioButton radio : _sizeRadioButtons) {
			radio.setEnabled(!inCustom);
		}
		_browseButton.setEnabled(inCustom);
	}

	/** React to clicks on the browse button, to start the selection of the custom icon */
	private void chooseCustomIconFile()
	{
		JFileChooser chooser = new JFileChooser();
		chooser.addChoosableFileFilter(
			new GenericFileFilter("filetypefilter.png", new String[] {"png"}));
		chooser.setAcceptAllFileFilterUsed(false);
		// Set start path from currently selected file
		if (_selectedIconFile != null) {
			chooser.setSelectedFile(_selectedIconFile);
		}
		if (chooser.showOpenDialog(_parentFrame) == JFileChooser.APPROVE_OPTION)
		{
			_selectedIconFile = chooser.getSelectedFile();
			updateCustomIconCheckbox();
		}
	}

	/**
	 * React to enabling / disabling checkbox controlling colours of waypoints
	 */
	private void coloursSwitched()
	{
		final boolean coloursOn = _coloursCheckbox.isSelected();
		showPreview(coloursOn ? Math.max(_saltSlider.getValue(), 0) : -1);
		_saltSlider.setEnabled(coloursOn);
		_typeListBox.setEnabled(coloursOn);
	}

	/**
	 * Show window
	 */
	public void begin()
	{
		if (_dialog == null)
		{
			_dialog = new JDialog(_parentFrame, getName());
			_dialog.setLocationRelativeTo(_parentFrame);
			_dialog.getContentPane().add(makeContents());
			_dialog.pack();
		}
		// Set values from config
		Config config = getConfig();
		try {
			_wpIconCombobox.setSelectedIndex(config.getConfigInt(Config.KEY_WAYPOINT_ICONS));
		} catch (IllegalArgumentException ignored) {}
		selectIconSizeRadio(config.getConfigInt(Config.KEY_WAYPOINT_ICON_SIZE));
		_selectedIconFile = makeFile(config.getConfigString(Config.KEY_WAYPOINT_ICON_PATH));
		_customIconCheckbox.setSelected(config.getConfigBoolean(Config.KEY_WAYPOINT_ICON_CUSTOM)
				&& _selectedIconFile != null);
		updateCustomIconCheckbox();
		final int salt = config.getConfigInt(Config.KEY_WPICON_SALT);
		_coloursCheckbox.setSelected(salt >= 0);
		_saltSlider.setValue(Math.max(salt, 0));
		_saltSlider.setEnabled(salt >= 0);
		_typeList.compile(_app.getTrackInfo().getTrack());
		showPreview(salt);
		_dialog.setVisible(true);
	}

	/** @return a File object from the given path if represents a valid png file, otherwise null */
	private File makeFile(String inPath)
	{
		if (inPath == null || inPath.isEmpty()) {
			return null;
		}
		File file = new File(inPath);
		if (file.exists() && file.isFile() && file.canRead() && file.getName().toLowerCase().endsWith(".png")) {
			return file;
		}
		return null;
	}

	/**
	 * @param salt current salt value from slider
	 */
	private void showPreview(int salt)
	{
		final String label = salt < 0 ? "" : (I18nManager.getText("dialog.waypointsettings.saltvalue") + " : " + salt);
		_saltLabel.setText(label);
		_noTypesLabel.setVisible(_typeList.getSize() == 0);
		_typeListBox.repaint();
		_listScroller.setVisible(_typeList.getSize() > 0);
	}

	/**
	 * Select the corresponding radio button according to the numeric value
	 * @param inValue numeric value saved in Config
	 */
	private void selectIconSizeRadio(int inValue)
	{
		if (inValue < 0 || inValue >= _sizeRadioButtons.length) {
			inValue = 1;
		}
		if (_sizeRadioButtons[inValue] != null) {
			_sizeRadioButtons[inValue].setSelected(true);
		}
	}

	/**
	 * @return numeric value of selected icon size according to radio buttons
	 */
	private int getSelectedIconSize()
	{
		for (int i=0; i<_sizeRadioButtons.length; i++)
		{
			if (_sizeRadioButtons[i] != null && _sizeRadioButtons[i].isSelected()) {
				return i;
			}
		}
		return 1; // default is medium
	}

	/**
	 * @param typeString waypoint type string
	 * @param saltValue current salt value
	 * @return image icon for list
	 */
	private Icon makeImageIcon(String typeString, int saltValue)
	{
		final Color color = WaypointColours.getColourForType(typeString, saltValue);
		if (color == null) {
			return null;
		}
		final int rgb = color.getRGB();
		BufferedImage bi = new BufferedImage(20, 20, BufferedImage.TYPE_INT_RGB);
		for (int x=1; x<19; x++)
		{
			bi.setRGB(x, 0, 0);
			bi.setRGB(0, x, 0);
			for (int y=1; y<19; y++) {
				bi.setRGB(x, y, rgb);
			}
			bi.setRGB(x, 19, 0);
			bi.setRGB(19, x, 0);
		}
		return new ImageIcon(bi);
	}

	/**
	 * Save settings and close
	 */
	public void finish()
	{
		// update config
		Config config = getConfig();
		config.setConfigInt(Config.KEY_WAYPOINT_ICONS, _wpIconCombobox.getSelectedIndex());
		config.setConfigInt(Config.KEY_WAYPOINT_ICON_SIZE, getSelectedIconSize());
		config.setConfigBoolean(Config.KEY_WAYPOINT_ICON_CUSTOM, _customIconCheckbox.isSelected());
		config.setConfigString(Config.KEY_WAYPOINT_ICON_PATH, _selectedIconFile == null ? "" : _selectedIconFile.getAbsolutePath());
		final int saltValue = _coloursCheckbox.isSelected() ? _saltSlider.getValue() : -1;
		config.setConfigInt(Config.KEY_WPICON_SALT, saltValue);
		// refresh display
		UpdateMessageBroker.informSubscribers(DataSubscriber.MAPSERVER_CHANGED);
		_dialog.dispose();
	}
}
