content: 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();
  	}
  }
  