package com.haskforce.settings;


import com.haskforce.utils.ExecUtil;
import com.intellij.openapi.externalSystem.util.ExternalSystemUiUtil;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.options.SearchableConfigurable;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import java.awt.*;

/**
 * The "Haskell Tools" option in Preferences->Project Settings.
 */
public class HaskellToolsConfigurable implements SearchableConfigurable {
    public static final String HASKELL_TOOLS_ID = "Haskell Tools";

    private Project project;

    private JPanel settings;
    private TextFieldWithBrowseButton cabalPath;
    private JLabel cabalVersion;

    public HaskellToolsConfigurable(@NotNull Project inProject) {
        project = inProject;
    }

    @NotNull
    @Override
    public String getId() {
        return HASKELL_TOOLS_ID;
    }

    @Nullable
    @Override
    public Runnable enableSearch(String s) {
        return null;
    }

    @Nls
    @Override
    public String getDisplayName() {
        return HASKELL_TOOLS_ID;
    }

    @Nullable
    @Override
    public String getHelpTopic() {
        return null;
    }

    @Nullable
    @Override
    public JComponent createComponent() {
        settings = new JPanel(new GridBagLayout());
        cabalPath = createExecutableOption("Cabal");
        cabalVersion = createDisplayVersion("Cabal");
        return settings;
    }

    /**
     * Enables the apply button.
     */
    @Override
    public boolean isModified() {
        return true;
    }

    /**
     * Triggered when the user pushes the apply button.
     */
    @Override
    public void apply() throws ConfigurationException {
        cabalVersion.setText(getVersion(cabalPath.getText(), "--numeric-version"));
    }

    /**
     * Triggered when the user pushes the cancel button.
     */
    @Override
    public void reset() {

    }

    @Override
    public void disposeUIResources() {

    }

    /**
     * Creates a label and path selector and adds them to the configuration
     * window.
     *
     * @param tool Which tool to configure.
     * @return The TextFieldWithBrowseButton created.
     */
    private TextFieldWithBrowseButton createExecutableOption(String tool) {
        // Create UI elements.
        TextFieldWithBrowseButton tf = new TextFieldWithBrowseButton();
        tf.addBrowseFolderListener("Select " + tool + " path", "", null,
                FileChooserDescriptorFactory.createSingleLocalFileDescriptor());

        // Add elements to Panel.
        settings.add(new JLabel(tool + " executable path:"));
        settings.add(tf, ExternalSystemUiUtil.getFillLineConstraints(0));

        return tf;
    }

    /**
     * Creates two labels adds them to the configuration window.
     *
     * @param tool Which tool to display version for.
     * @return The label with dynamic content.
     */
    private JLabel createDisplayVersion(String tool) {
        JLabel tf = new JLabel("");

        // Add elements to Panel.
        settings.add(new JLabel(tool + " version:"));
        settings.add(tf, ExternalSystemUiUtil.getFillLineConstraints(0));

        return tf;
    }

    /**
     * Heuristically finds the version number. Current implementation is the
     * identity function since cabal plays nice.
     */
    private static String getVersion(String cmd, String versionflag) {
        return ExecUtil.run(cmd + ' ' + versionflag);
    }
}

@@ -60,24 +60,74 @@
 s.ExecUtil;%0A
+import com.intellij.ide.util.PropertiesComponent;%0A
 import com.i
@@ -887,16 +887,117 @@
 oject;%0A%0A
+    // Old values to detect user updates.%0A    private String oldCabalPath;%0A%0A    // Swing components.%0A
     priv
@@ -1195,16 +1195,107 @@
 roject;%0A
+        oldCabalPath = PropertiesComponent.getInstance(project).getValue(%22cabalPath%22, %22%22);%0A
     %7D%0A%0A 
@@ -1801,16 +1801,49 @@
 yout());
+%0A%0A        // Cabal configuration.
 %0A       
@@ -1934,32 +1934,166 @@
 rsion(%22Cabal%22);%0A
+        if (!oldCabalPath.isEmpty()) %7B%0A            cabalPath.setText(oldCabalPath);%0A            updateVersionInfoFields();%0A        %7D%0A%0A
         return s
@@ -2139,32 +2139,52 @@
 the apply button
+ if anything changed
 .%0A     */%0A    @O
@@ -2244,12 +2244,49 @@
 urn 
-true
+!cabalPath.getText().equals(oldCabalPath)
 ;%0A  
@@ -2444,81 +2444,53 @@
     
-cabalVersion.setText(getVersion(cabalPath.getText(), %22--numeric-version%22)
+updateVersionInfoFields();%0A        saveState(
 );%0A 
@@ -2604,24 +2604,47 @@
 d reset() %7B%0A
+        restoreState();
 %0A    %7D%0A%0A    
@@ -4185,11 +4185,566 @@
 );%0A    %7D
+%0A%0A    /**%0A     * Updates the version info fields for all files configured.%0A     */%0A    private void updateVersionInfoFields() %7B%0A        cabalVersion.setText(getVersion(cabalPath.getText(), %22--numeric-version%22));%0A    %7D%0A%0A    /**%0A     * Persistent save of the current state.%0A     */%0A    private void saveState() %7B%0A        PropertiesComponent.getInstance(project).setValue(%22cabalPath%22, cabalPath.getText());%0A    %7D%0A%0A    /**%0A     * Restore components to the initial state.%0A     */%0A    private void restoreState() %7B%0A        cabalPath.setText(oldCabalPath);%0A    %7D
 %0A%7D%0A

Implement persistency for cabal configuration.