package de.renew.propertymanagementgui.controller;

import java.awt.BorderLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JOptionPane;

import org.apache.log4j.Logger;

import de.renew.plugin.Loader;
import de.renew.plugin.PluginManager;
import de.renew.propertymanagement.prop.ConfigurableProperty;
import de.renew.propertymanagement.prop.ConfigurablePropertyManager;
import de.renew.propertymanagement.prop.PropertyLoadFailedException;
import de.renew.propertymanagement.prop.PropertySaveFailedException;
import de.renew.propertymanagementgui.gui.BodyComponent;
import de.renew.propertymanagementgui.reactivecomponents.Component;


/**
 * Controller for the settings window.
 */
public class SettingsController {

    private static final int WINDOW_WIDTH = 800;
    private static final int WINDOW_HEIGHT = 500;
    private static final List<SettingsController> INSTANCE_LIST = new ArrayList<>();
    private static final Logger LOGGER = Logger.getLogger(SettingsController.class);

    private final ConfigurablePropertyManager _propertyManager;
    private final Map<String, ConfigurableProperty> _properties;
    private final Runnable _savePropertiesGlobalAction;
    private final Runnable _savePropertiesLocalAction;
    private JFrame _settingsWindow;
    private BodyComponent _body;

    public static void notifySettingsChanged() {
        for (SettingsController instance : INSTANCE_LIST) {
            instance.settingsChanged();
        }
    }

    /**
     * Creates a new SettingsController
     */
    public SettingsController() {
        _propertyManager = ConfigurablePropertyManager.getInstance();
        _properties = _propertyManager.getConfigurableProperties();
        _savePropertiesGlobalAction = () -> {
            try {
                boolean restartRequired = _propertyManager.restartRequired();
                _propertyManager.applyChanges();
                _propertyManager.saveProperties(true);
                notifySettingsChanged();
                if (restartRequired && JOptionPane.showConfirmDialog(
                    null, "Restart required, restart now?", "Restart",
                    JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
                    Thread restart = new Thread(() -> {
                        try {
                            Loader.startNewRenewInstance();
                        } catch (IOException e) {
                            LOGGER.error("Restart of Renew failed.", e);
                        }
                    });
                    Runtime.getRuntime().addShutdownHook(restart);
                    PluginManager.getInstance().stop();
                } else {
                    JOptionPane.showMessageDialog(
                        null, "Properties saved successfully in your home directory", "Saved",
                        JOptionPane.INFORMATION_MESSAGE);
                }

            } catch (PropertySaveFailedException e) {
                JOptionPane
                    .showMessageDialog(null, e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
            }
        };
        _savePropertiesLocalAction = () -> {
            try {
                boolean restartRequired = _propertyManager.restartRequired();
                _propertyManager.applyChanges();
                _propertyManager.saveProperties(false);
                notifySettingsChanged();
                if (restartRequired && JOptionPane.showConfirmDialog(
                    null, "Restart required, restart now?", "Restart",
                    JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
                    Thread restart = new Thread(() -> {
                        try {
                            Loader.startNewRenewInstance();
                        } catch (IOException e) {
                            LOGGER.error("Restart of Renew failed.", e);
                        }
                    });
                    Runtime.getRuntime().addShutdownHook(restart);
                    PluginManager.getInstance().stop();
                } else {
                    JOptionPane.showMessageDialog(
                        null, "Properties saved successfully at Renew/dist/config/renew.properties",
                        "Saved", JOptionPane.INFORMATION_MESSAGE);
                }
            } catch (PropertySaveFailedException e) {
                JOptionPane
                    .showMessageDialog(null, e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
            }
        };
        INSTANCE_LIST.add(this);
    }

    /**
     * Opens the settings window
     * This method is called when the user clicks on the settings button in the main window
     * Load global properties from file if it exists else load local properties
     */
    public void openSettingsWindow() {
        try {
            _propertyManager.loadProperties(
                new File(System.getProperty("user.home") + File.separator + ".renew.properties")
                    .exists());
        } catch (PropertyLoadFailedException e) {
            JOptionPane.showMessageDialog(null, e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
        }


        _body = new BodyComponent(
            _properties, _savePropertiesGlobalAction, _savePropertiesLocalAction,
            e -> closeWindow(_settingsWindow));

        renderWindow(_body);
    }

    private void renderWindow(Component body) {
        JFrame window = getWindow(body);
        window.setIconImage(
            new ImageIcon("Gui/src/main/resources/de/renew/gui/images/RENEW.gif").getImage());
        notifySettingsChanged();
        window.setVisible(true);
    }

    private JFrame getWindow(Component content) {
        _settingsWindow = new JFrame("Settings");
        _settingsWindow.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        _settingsWindow.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                closeWindow(_settingsWindow);
            }
        });

        //Sizing & Position
        _settingsWindow.setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
        _settingsWindow.setLocationRelativeTo(null);

        //Layout
        _settingsWindow.setLayout(new BorderLayout());
        _settingsWindow.add(content.get(), BorderLayout.CENTER);

        return _settingsWindow;
    }

    /**
     * Closes the window, if there are unsaved staged changes, opens a dialog to confirm the discarding.
     *
     * @param window the JFrame to close
     */
    public void closeWindow(JFrame window) {
        if (_propertyManager.hasStagedChanges()) {
            switch (JOptionPane.showConfirmDialog(
                null, "You have unsaved changes, save them before closing?", "unsaved changes",
                JOptionPane.YES_NO_CANCEL_OPTION)) {
                case JOptionPane.YES_OPTION -> {
                    switch (JOptionPane.showConfirmDialog(
                        null, "save globally?", "save globally", JOptionPane.YES_NO_OPTION)) {
                        case JOptionPane.YES_OPTION -> _savePropertiesGlobalAction.run();
                        case JOptionPane.NO_OPTION -> _savePropertiesLocalAction.run();
                    }
                }
                case JOptionPane.CANCEL_OPTION, JOptionPane.CLOSED_OPTION -> {
                    return;
                }
            }

        }
        _propertyManager.clearStagedChanges();
        // Close the window
        if (window != null) {
            window.dispose();
        }
    }

    private void settingsChanged() {
        _body.settingsChanged();
    }
}
