package de.renew.propertymanagementgui.gui;

import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.util.HashSet;
import java.util.List;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.border.EmptyBorder;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

import de.renew.propertymanagement.prop.ConfigurableHotkeyProperty;
import de.renew.propertymanagement.prop.ConfigurableProperty;
import de.renew.propertymanagement.prop.ConfigurablePropertyManager;
import de.renew.propertymanagementgui.reactivecomponents.Component;


/**
 * The _page of the settings window. Shows the settings of the selected plugin.
 */
public class PageComponent extends Component {

    private static final Font START_TEXT_FONT = new Font("Arial", Font.BOLD, 24);
    private static final String START_TEXT = "<html>Click on a plugin to see its settings.</html>";
    private static final Dimension PAGE_DIMENSION = new Dimension(200, 200);
    private static final EmptyBorder PAGE_BORDER = new EmptyBorder(10, 10, 10, 10);
    private static final Insets PROPERTY_LABEL_INSETS = new Insets(10, 0, 10, 0);
    private static final Insets PROPERTY_COMPONENT_INSETS = new Insets(0, 0, 0, 0);

    private final List<ConfigurableProperty> _properties;
    private final ConfigurablePropertyManager _propertyManager =
        ConfigurablePropertyManager.getInstance();
    private GridBagConstraints _constraints;
    private JComponent _page;

    /**
     * Creates a new PageComponent.
     *
     * @param properties The properties of the selected plugin.
     */
    public PageComponent(List<ConfigurableProperty> properties) {
        _properties = properties;
    }

    @Override
    protected JComponent render() {
        _page = new JPanel();
        _page.setPreferredSize(PAGE_DIMENSION);
        _page.setBorder(PAGE_BORDER);
        _page.setLayout(new GridBagLayout());
        _constraints = new GridBagConstraints();
        _constraints.fill = GridBagConstraints.HORIZONTAL;
        _constraints.weightx = 1.0;
        _constraints.gridx = 0;

        if (_properties != null) {
            createPropertiesGui();
        } else {
            JComponent startText = new JLabel(START_TEXT);
            startText.setFont(START_TEXT_FONT);
            _page.add(startText);
        }

        JScrollPane scrollPane = new JScrollPane(_page);
        scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
        return scrollPane;
    }

    /**
     * Iterates over the properties of the selected plugin and adds them to the page.
     */
    private void createPropertiesGui() {
        for (int i = 0; i < _properties.size(); i++) {
            ConfigurableProperty property = _properties.get(i);

            JLabel label = createPropertyLabel(property);

            _constraints.insets = PROPERTY_LABEL_INSETS;
            _constraints.gridy = i;
            _constraints.gridx = 0;
            _page.add(label, _constraints);
            _constraints.insets = PROPERTY_COMPONENT_INSETS;
            _constraints.gridx = 1;

            if ("true".equalsIgnoreCase(property.getDefaultValue())
                || "false".equalsIgnoreCase(property.getDefaultValue())) {
                // Add checkbox if the property is a boolean
                JCheckBox checkBox = createCheckBox(property);
                _page.add(checkBox, _constraints);
            } else if (property instanceof ConfigurableHotkeyProperty hotkeyProperty) {
                // Add configurable hotkey field if property can be interpreted as a hotkey
                HotkeyField hotkeyField = new HotkeyField(hotkeyProperty);
                _page.add(hotkeyField, _constraints);
            } else if (property.getSelectionChoices() == null) {
                // Add text field for non-boolean values if no selection choices are available
                JTextField textField = createTextField(property);
                _page.add(textField, _constraints);
            } else {
                // Add combobox for non-boolean values if selection choices are available
                JComboBox<String> comboBox = createComboBox(property);
                _page.add(comboBox, _constraints);
            }
        }
    }

    /**
     * Create a label for a given property.
     *
     * @param property The property to add a label for.
     * @return The created label.
     */
    private JLabel createPropertyLabel(ConfigurableProperty property) {
        JLabel label;
        if (!(property.getDisplayName() == null || property.getDisplayName().isEmpty())) {
            label = new JLabel(property.getDisplayName());
        } else {
            label = new JLabel(property.getKey());
        }
        return label;
    }

    /**
     * Creates a checkbox for a given property and adds an item listener to it.
     *
     * @param property The property to create a checkbox for.
     * @return The created checkbox.
     */
    private JCheckBox createCheckBox(ConfigurableProperty property) {
        JCheckBox checkBox = new JCheckBox();
        if (property.getCurrentValue() == null) {
            checkBox.setSelected(Boolean.parseBoolean(property.getDefaultValue()));
        } else {
            checkBox.setSelected(Boolean.parseBoolean(property.getCurrentValue()));
        }
        checkBox.addItemListener(
            e -> _propertyManager
                .changeProperty(property.getKey(), String.valueOf(checkBox.isSelected())));
        return checkBox;
    }

    /**
     * Creates a text field for a given property and adds a document listener to it.
     *
     * @param property The property to create a text field for.
     * @return The created text field.
     */
    private JTextField createTextField(ConfigurableProperty property) {
        JTextField textField;
        if (property.getCurrentValue() != null) {
            textField = new JTextField(property.getCurrentValue());
        } else {
            textField = new JTextField(property.getDefaultValue());
        }

        textField.getDocument().addDocumentListener(new DocumentListener() {

            @Override
            public void insertUpdate(DocumentEvent e) {
                _propertyManager.changeProperty(property.getKey(), textField.getText());
            }

            @Override
            public void removeUpdate(DocumentEvent e) {
                _propertyManager.changeProperty(property.getKey(), textField.getText());
            }

            @Override
            public void changedUpdate(DocumentEvent e) {
                _propertyManager.changeProperty(property.getKey(), textField.getText());
            }
        });
        return textField;
    }

    /**
     * Creates a combobox for a given property and adds an action listener to it.
     *
     * @param property The property to create a combobox for.
     * @return The created combobox.
     */
    private JComboBox<String> createComboBox(ConfigurableProperty property) {
        HashSet<String> optionsList = new HashSet<>();
        optionsList.add(property.getCurrentValue());
        optionsList.add(property.getDefaultValue());
        optionsList.addAll(List.of(property.getSelectionChoices()));

        JComboBox<String> comboBox = new JComboBox<>(optionsList.toArray(new String[0]));
        if (property.getCurrentValue() != null) {
            comboBox.setSelectedItem(property.getCurrentValue());
        } else {
            comboBox.setSelectedItem(property.getDefaultValue());
        }

        comboBox.addActionListener(
            e -> _propertyManager
                .changeProperty(property.getKey(), (String) comboBox.getSelectedItem()));
        return comboBox;
    }
}
