/*
 * Copyright 2013-2015 must-be.org
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.consulo.module.extension.ui;

import com.intellij.openapi.module.Module;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.SdkType;
import com.intellij.openapi.projectRoots.SdkTypeId;
import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable;
import com.intellij.openapi.roots.ui.configuration.SdkComboBox;
import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel;
import com.intellij.openapi.ui.LabeledComponent;
import com.intellij.openapi.util.Condition;
import com.intellij.util.Consumer;
import com.intellij.util.NullableFunction;
import lombok.val;
import org.consulo.module.extension.MutableModuleExtension;
import org.consulo.module.extension.MutableModuleExtensionWithSdk;
import org.consulo.module.extension.MutableModuleInheritableNamedPointer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.mustbe.consulo.RequiredReadAction;

import javax.swing.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;

/**
 * @author VISTALL
 * @since 15.03.2015
 */
public class ModuleExtensionSdkBoxBuilder<T extends MutableModuleExtension<?>> {
  @NotNull
  public static <T extends MutableModuleExtensionWithSdk<?>> ModuleExtensionSdkBoxBuilder createAndDefine(@NotNull T extension,
                                                                                                          @Nullable Runnable updater) {
    ModuleExtensionSdkBoxBuilder<T> builder = create(extension, updater);
    builder.sdkTypeClass(extension.getSdkTypeClass());
    builder.sdkPointerFunc(new NullableFunction<T, MutableModuleInheritableNamedPointer<Sdk>>() {
      @Override
      public MutableModuleInheritableNamedPointer<Sdk> fun(T dom) {
        return dom.getInheritableSdk();
      }
    });
    return builder;
  }

  @NotNull
  public static <T extends MutableModuleExtension<?>> ModuleExtensionSdkBoxBuilder<T> create(@NotNull T extension, @Nullable Runnable updater) {
    return new ModuleExtensionSdkBoxBuilder<T>(extension).laterUpdater(updater);
  }

  @NotNull
  private NullableFunction<T, MutableModuleInheritableNamedPointer<Sdk>> mySdkPointerFunction;
  @NotNull
  private Class<? extends SdkType> mySdkTypeClass;

  private final T myMutableModuleExtension;

  private String myLabelText = "SDK";

  private Runnable myLaterUpdater;

  private Consumer<Sdk> myPostConsumer;

  private ModuleExtensionSdkBoxBuilder(@NotNull T mutableModuleExtension) {
    myMutableModuleExtension = mutableModuleExtension;
  }

  @NotNull
  public ModuleExtensionSdkBoxBuilder<T> sdkTypeClass(@NotNull Class<? extends SdkType> clazz) {
    mySdkTypeClass = clazz;
    return this;
  }

  @NotNull
  public ModuleExtensionSdkBoxBuilder<T> sdkType(@NotNull SdkType clazz) {
    return sdkTypeClass(clazz.getClass());
  }

  @NotNull
  public ModuleExtensionSdkBoxBuilder<T> sdkPointerFunc(@NotNull NullableFunction<T, MutableModuleInheritableNamedPointer<Sdk>> function) {
    mySdkPointerFunction = function;
    return this;
  }

  @NotNull
  public ModuleExtensionSdkBoxBuilder<T> labelText(@NotNull String labelText) {
    myLabelText = labelText;
    return this;
  }

  @NotNull
  public ModuleExtensionSdkBoxBuilder<T> laterUpdater(@Nullable Runnable runnable) {
    myLaterUpdater = runnable;
    return this;
  }

  @NotNull
  public ModuleExtensionSdkBoxBuilder<T> postConsumer(@NotNull Consumer<Sdk> consumer) {
    myPostConsumer = consumer;
    return this;
  }

  @NotNull
  @RequiredReadAction
  public JComponent build() {
    final ProjectSdksModel projectSdksModel = ProjectStructureConfigurable.getInstance(myMutableModuleExtension.getProject()).getProjectSdksModel();

    val comboBox = new SdkComboBox(projectSdksModel, new Condition<SdkTypeId>() {
      @Override
      public boolean value(SdkTypeId sdkTypeId) {
        return mySdkTypeClass.isAssignableFrom(sdkTypeId.getClass());
      }
    }, true);

    comboBox.insertModuleItems(myMutableModuleExtension, mySdkPointerFunction);

    final MutableModuleInheritableNamedPointer<Sdk> inheritableSdk = mySdkPointerFunction.fun(myMutableModuleExtension);
    assert inheritableSdk != null;
    if (inheritableSdk.isNull()) {
      comboBox.setSelectedNoneSdk();
    }
    else {
      final String sdkInheritModuleName = inheritableSdk.getModuleName();
      if (sdkInheritModuleName != null) {
        final Module sdkInheritModule = inheritableSdk.getModule();
        if (sdkInheritModule == null) {
          comboBox.addInvalidModuleItem(sdkInheritModuleName);
        }
        comboBox.setSelectedModule(sdkInheritModuleName);
      }
      else {
        comboBox.setSelectedSdk(inheritableSdk.getName());
      }
    }

    comboBox.addItemListener(new ItemListener() {
      @Override
      public void itemStateChanged(ItemEvent e) {
        if (e.getStateChange() == ItemEvent.SELECTED) {
          inheritableSdk.set(comboBox.getSelectedModuleName(), comboBox.getSelectedSdkName());

          if (myPostConsumer != null) {
            Sdk sdk = inheritableSdk.get();
            myPostConsumer.consume(sdk);
          }

          if (myLaterUpdater != null) {
            SwingUtilities.invokeLater(myLaterUpdater);
          }
        }
      }
    });

    return LabeledComponent.left(comboBox, myLabelText);
  }
}

@@ -1166,24 +1166,32 @@
 ij.util.
-Consumer
+NullableFunction
 ;%0Aimport
@@ -1209,32 +1209,28 @@
 ij.util.
-NullableFunction
+PairConsumer
 ;%0Aimport
@@ -3004,16 +3004,20 @@
 private 
+Pair
 Consumer
@@ -3013,24 +3013,29 @@
 airConsumer%3C
+Sdk, 
 Sdk%3E myPostC
@@ -4047,16 +4047,20 @@
 NotNull 
+Pair
 Consumer
@@ -4060,16 +4060,21 @@
 onsumer%3C
+Sdk, 
 Sdk%3E con
@@ -5533,24 +5533,72 @@
 SELECTED) %7B%0A
+          Sdk oldValue = inheritableSdk.get();%0A%0A
           in
@@ -5761,24 +5761,24 @@
 eSdk.get();%0A
-
             
@@ -5800,16 +5800,26 @@
 consume(
+oldValue, 
 sdk);%0A  

use pair consumer - oldSdkValue + newSdkValue