package tim.prune.function.settings;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.FlowLayout;
import java.util.Objects;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.border.Border;
import javax.swing.border.EtchedBorder;

import tim.prune.App;
import tim.prune.DataSubscriber;
import tim.prune.GenericFunction;
import tim.prune.I18nManager;
import tim.prune.UpdateMessageBroker;
import tim.prune.config.Config;
import tim.prune.gui.GuiGridLayout;
import tim.prune.gui.WholeNumberField;


/**
 * Class to show the dialog for setting the display settings
 * like line width, antialiasing
 */
public class SetDisplaySettings extends GenericFunction
{
	private JDialog _dialog = null;
	private WholeNumberField _lineWidthField = null;
	private JCheckBox _antialiasCheckbox = null;
	private JCheckBox _osScalingCheckbox = null;
	private JCheckBox _doubledIconsCheckbox = null;
	private JCheckBox _showZoomLevelCheckbox = null;
	private JRadioButton[] _windowStyleRadios = null;
	private WholeNumberField _textScaleField = null;
	// settings when entering dialog in order to detect changes
	private String _previousStyle = null;
	private boolean _previousDoubleIcons = false;
	private int _previousTextScaling = 100;

	private static final String STYLEKEY_NIMBUS = "javax.swing.plaf.nimbus.NimbusLookAndFeel";
	private static final String STYLEKEY_GTK = "com.sun.java.swing.plaf.gtk.GTKLookAndFeel";


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

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

	/**
	 * @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));

		JPanel linesPanel = new JPanel();
		linesPanel.setBorder(makeEtchedBorder());
		GuiGridLayout grid = new GuiGridLayout(linesPanel);
		grid.setYPadding(8);

		// line width
		grid.add(new JLabel(I18nManager.getText("dialog.displaysettings.linewidth")));
		_lineWidthField = new WholeNumberField(1);
		grid.add(_lineWidthField);
		// Antialiasing
		_antialiasCheckbox = new JCheckBox(I18nManager.getText("dialog.displaysettings.antialias"), false);
		grid.add(_antialiasCheckbox);
		grid.add(new JLabel(""));
		// Show zoom level
		_showZoomLevelCheckbox = new JCheckBox(I18nManager.getText("dialog.displaysettings.showzoomlevel"), false);
		grid.add(_showZoomLevelCheckbox);
		grid.add(new JLabel(""));

		linesPanel.setAlignmentX(Component.CENTER_ALIGNMENT);
		midPanel.add(linesPanel);
		midPanel.add(Box.createVerticalStrut(15));

		// Panel for tiny pixels
		JPanel tinyPixelsPanel = new JPanel();
		tinyPixelsPanel.setBorder(makeEtchedBorder());
		grid = new GuiGridLayout(tinyPixelsPanel);
		grid.setYPadding(8);
		// OS scaling
		_osScalingCheckbox = new JCheckBox(I18nManager.getText("dialog.displaysettings.allowosscaling"), false);
		grid.add(_osScalingCheckbox);
		grid.add(new JLabel(""));
		// Icon size
		_doubledIconsCheckbox = new JCheckBox(I18nManager.getText("dialog.displaysettings.doublesizedicons"), false);
		grid.add(_doubledIconsCheckbox);
		grid.add(new JLabel(""));
		_textScaleField = new WholeNumberField(3);
		grid.add(new JLabel(I18nManager.getText("dialog.displaysettings.textscaling")));
		grid.add(_textScaleField);
		midPanel.add(tinyPixelsPanel);
		midPanel.add(Box.createVerticalStrut(15));

		// Panel for window style
		JPanel windowStylePanel = new JPanel();
		windowStylePanel.setBorder(makeEtchedBorder());
		windowStylePanel.setLayout(new FlowLayout(FlowLayout.CENTER));
		windowStylePanel.add(new JLabel(I18nManager.getText("dialog.displaysettings.windowstyle")));
		windowStylePanel.add(Box.createHorizontalStrut(10));
		ButtonGroup styleGroup = new ButtonGroup();
		final String[] styleKeys = {"default", "nimbus", "gtk"};
		_windowStyleRadios = new JRadioButton[3];
		for (int i=0; i<3; i++)
		{
			_windowStyleRadios[i] = new JRadioButton(
				I18nManager.getText("dialog.displaysettings.windowstyle." + styleKeys[i]));
			styleGroup.add(_windowStyleRadios[i]);
			if (i != 2 || platformHasPlaf(STYLEKEY_GTK))
			{
				windowStylePanel.add(_windowStyleRadios[i]);
			}
		}
		midPanel.add(windowStylePanel);
		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;
	}

	/** @return an etched border for a panel */
	private static Border makeEtchedBorder() {
		return BorderFactory.createCompoundBorder(
				BorderFactory.createEtchedBorder(EtchedBorder.LOWERED),
				BorderFactory.createEmptyBorder(3, 3, 3, 3));
	}

	/**
	 * @return true if the specified style name is available on this platform
	 */
	private static boolean platformHasPlaf(String styleName)
	{
		for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels())
		{
			if (info.getClassName().equals(styleName)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 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();
		int lineWidth = config.getConfigInt(Config.KEY_LINE_WIDTH);
		if (lineWidth < 1 || lineWidth > 4) {lineWidth = 2;}
		_lineWidthField.setValue(lineWidth);
		_antialiasCheckbox.setSelected(config.getConfigBoolean(Config.KEY_ANTIALIAS));
		_osScalingCheckbox.setSelected(config.getConfigBoolean(Config.KEY_OSSCALING));
		_doubledIconsCheckbox.setSelected(config.getConfigBoolean(Config.KEY_ICONS_DOUBLE_SIZE));
		_textScaleField.setValue(config.getConfigInt(Config.KEY_TEXT_SCALING_FACTOR));
		_showZoomLevelCheckbox.setSelected(config.getConfigBoolean(Config.KEY_SHOW_ZOOMLEVEL));
		selectWindowStyleRadio(config.getConfigString(Config.KEY_WINDOW_STYLE));
		// Remember what the current settings are
		_previousStyle = getSelectedStyleString();
		_previousDoubleIcons = _doubledIconsCheckbox.isSelected();
		_previousTextScaling = _textScaleField.getValue();
		_dialog.setVisible(true);
	}

	/**
	 * Select the corresponding radio button according to the selected style
	 * @param inValue style string saved in Config
	 */
	private void selectWindowStyleRadio(String inValue)
	{
		int selectedRadio = 0;
		if (inValue != null && inValue.equals(STYLEKEY_NIMBUS))
		{
			selectedRadio = 1;
		}
		else if (inValue != null && inValue.equals(STYLEKEY_GTK) && _windowStyleRadios[2] != null)
		{
			selectedRadio = 2;
		}
		_windowStyleRadios[selectedRadio].setSelected(true);
	}

	/**
	 * @return the style string according to the selected radio button
	 */
	private String getSelectedStyleString()
	{
		if (_windowStyleRadios[1].isSelected()) {
			return STYLEKEY_NIMBUS;
		}
		if (_windowStyleRadios[2] != null && _windowStyleRadios[2].isSelected()) {
			return STYLEKEY_GTK;
		}
		return null;
	}

	/**
	 * Save settings and close
	 */
	public void finish()
	{
		// update config
		int lineWidth = _lineWidthField.getValue();
		if (lineWidth < 1 || lineWidth > 4) {lineWidth = 2;}
		Config config = getConfig();
		config.setConfigInt(Config.KEY_LINE_WIDTH, lineWidth);
		config.setConfigBoolean(Config.KEY_ANTIALIAS, _antialiasCheckbox.isSelected());
		config.setConfigBoolean(Config.KEY_OSSCALING, _osScalingCheckbox.isSelected());
		config.setConfigBoolean(Config.KEY_ICONS_DOUBLE_SIZE, _doubledIconsCheckbox.isSelected());
		config.setConfigInt(Config.KEY_TEXT_SCALING_FACTOR, _textScaleField.getValue());
		config.setConfigBoolean(Config.KEY_SHOW_ZOOMLEVEL, _showZoomLevelCheckbox.isSelected());
		config.setConfigString(Config.KEY_WINDOW_STYLE, getSelectedStyleString());
		if (needsRestart())
		{
			JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("dialog.displaysettings.restart"),
				getName(), JOptionPane.INFORMATION_MESSAGE);
		}
		// refresh display
		UpdateMessageBroker.informSubscribers(DataSubscriber.MAPSERVER_CHANGED);
		_dialog.dispose();
	}

	/** @return true if the double-icons setting, the window style or the text scaling has changed */
	private boolean needsRestart()
	{
		return _doubledIconsCheckbox.isSelected() != _previousDoubleIcons
			|| !Objects.equals(_previousStyle, getSelectedStyleString())
			|| _textScaleField.getValue() != _previousTextScaling;
	}
}
