content: /*
   * Copyright 2012 Andrew C. Dvorak.
   *
   * 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 net.andydvorak.intellij.lessc.ui.configurable;
  
  import com.intellij.openapi.actionSystem.AnAction;
  import com.intellij.openapi.actionSystem.AnActionEvent;
  import com.intellij.openapi.actionSystem.CommonShortcuts;
  import com.intellij.openapi.actionSystem.CustomShortcutSet;
  import com.intellij.openapi.options.ConfigurationException;
  import com.intellij.openapi.options.SearchableConfigurable;
  import com.intellij.openapi.project.Project;
  import com.intellij.openapi.ui.*;
  import com.intellij.openapi.util.Conditions;
  import com.intellij.util.PlatformIcons;
  import net.andydvorak.intellij.lessc.LessManager;
  import net.andydvorak.intellij.lessc.state.CssDirectory;
  import net.andydvorak.intellij.lessc.ui.messages.UIBundle;
  import net.andydvorak.intellij.lessc.state.LessProfile;
  import org.apache.commons.lang3.StringUtils;
  import org.jetbrains.annotations.Nls;
  import org.jetbrains.annotations.NonNls;
  import org.jetbrains.annotations.NotNull;
  import org.jetbrains.annotations.Nullable;
  
  import javax.swing.*;
  import java.awt.event.KeyEvent;
  import java.io.File;
  import java.util.*;
  import java.util.concurrent.atomic.AtomicBoolean;
  
  class LessProfilesPanel extends MasterDetailsComponent implements SearchableConfigurable {
  
      @NotNull private final Project project;
      @NotNull private final LessManager lessManager;
      @NotNull private final AtomicBoolean isInitialized = new AtomicBoolean(false);
  
      private final List<LessProfileConfigurableForm> profileForms = new ArrayList<LessProfileConfigurableForm>();
  
      public LessProfilesPanel(@NotNull final Project project) {
          this.project = project;
          this.lessManager = LessManager.getInstance(project);
          initTree();
      }
  
      protected boolean wasObjectStored(final Object o) {
          if (o == null || LessProfile.class != o.getClass()) return false;
          final LessProfile profile = (LessProfile) o;
          return lessManager.getProfileMap().containsKey(profile.getId());
      }
  
      public void apply() throws ConfigurationException {
          final Set<String> names = new HashSet<String>();
  
          // Check for duplicate profile names
          for (int i = 0; i < myRoot.getChildCount(); i++) {
              final MyNode node = (MyNode) myRoot.getChildAt(i);
              final LessProfileConfigurableForm form = (LessProfileConfigurableForm) node.getConfigurable();
              final LessProfile profile = form.getCurrentState();
              final String name = profile.getName();
              final String dirPath = profile.getLessDirPath();
  
              if (StringUtils.isBlank(name)) {
                  selectNodeInTree(name);
                  throw new ConfigurationException(UIBundle.message("blank.less.profile.name"));
              }
  
              if (names.contains(name)) {
                  selectNodeInTree(name);
                  throw new ConfigurationException(UIBundle.message("duplicate.less.profile.name", name));
              }
  
              if (StringUtils.isBlank(dirPath)) {
                  selectNodeInTree(name);
                  throw new ConfigurationException(UIBundle.message("blank.less.profile.source.dir"));
              }
  
              if (!profile.getLessDir().exists()) {
                  if (!confirmWarning(UIBundle.message("nonexistent.less.profile.source.dir.title"),
                                      UIBundle.message("nonexistent.less.profile.source.dir.prompt", dirPath))) {
                      selectNodeInTree(name);
                      throw new ConfigurationException(UIBundle.message("nonexistent.less.profile.source.dir.error", dirPath));
                  }
              }
  
              if (profile.getCssDirectories().isEmpty()) {
                  selectNodeInTree(name);
                  throw new ConfigurationException(UIBundle.message("no.less.profile.css.dirs"));
              }
  
              for (final CssDirectory cssDirectory : profile.getCssDirectories()) {
                  if (!new File(cssDirectory.getPath()).exists()) {
                      if (!confirmWarning(UIBundle.message("nonexistent.less.profile.css.dir.title"),
                                          UIBundle.message("nonexistent.less.profile.css.dir.prompt", dirPath))) {
                          selectNodeInTree(name);
                          throw new ConfigurationException(UIBundle.message("nonexistent.less.profile.css.dir.error", dirPath));
                      }
                  }
              }
  
              names.add(name);
          }
  
          super.apply();
      }
  
      protected void processRemovedItems() {
          final Map<Integer, LessProfile> profiles = getProfileMap();
          final List<Integer> deletedIds = new ArrayList<Integer>();
  
          // Compile a list of all profiles that are no longer present in the UI
          for (final LessProfile profile : lessManager.getProfiles()) {
              if (!profiles.containsKey(profile.getId())) {
                  deletedIds.add(profile.getId());
              }
          }
  
          // Remove the deleted profiles from the manager
          for (final int id : deletedIds) {
              lessManager.removeProfile(id);
          }
      }
  
      public void reset() {
          reloadTree();
          super.reset();
      }
  
      @Override
      public void disposeUIResources() {
          super.disposeUIResources();
          isInitialized.set(false);
      }
  
      @Nullable
      protected ArrayList<AnAction> createActions(final boolean fromPopup) {
          final ArrayList<AnAction> result = new ArrayList<AnAction>();
  
          final String addText = UIBundle.message("action.add.less.profile.text");
          final String addDescription = UIBundle.message("action.add.less.profile.description");
          final String addPromptTitle = UIBundle.message("action.add.less.profile.prompt.title");
  
          result.add(new AnAction(addText, addDescription, PlatformIcons.ADD_ICON) {
              {
                  registerCustomShortcutSet(CommonShortcuts.INSERT, myTree);
              }
  
              public void actionPerformed(final AnActionEvent event) {
                  final String name = askForProfileName(addPromptTitle, "");
                  if (name == null) return;
                  final LessProfile lessProfile = new LessProfile(getNextId(), name);
                  addProfileNode(lessProfile);
              }
          });
  
          result.add(new MyDeleteAction(forAll(Conditions.alwaysTrue())));
  
          final String copyText = UIBundle.message("action.copy.less.profile.text");
          final String copyDescription = UIBundle.message("action.copy.less.profile.description");
          final String copyPromptTitle = UIBundle.message("action.copy.less.profile.prompt.title");
  
          result.add(new AnAction(copyText, copyDescription, PlatformIcons.COPY_ICON) {
              {
                  registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_D, KeyEvent.CTRL_MASK)), myTree);
              }
              public void actionPerformed(final AnActionEvent event) {
                  final String profileName = askForProfileName(copyPromptTitle, "");
                  if (profileName == null) return;
                  final LessProfile clone = new LessProfile(getNextId(), (LessProfile) getSelectedObject());
                  clone.setName(profileName);
                  addProfileNode(clone);
              }
  
              public void update(final AnActionEvent event) {
                  super.update(event);
                  event.getPresentation().setEnabled(getSelectedObject() != null);
              }
          });
  
          return result;
      }
  
      private void addProfileNode(final LessProfile lessProfile) {
          final LessProfileConfigurableForm profileForm = new LessProfileConfigurableForm(project, lessProfile, this, TREE_UPDATER);
          profileForm.setModified(true);
          profileForms.add(profileForm);
          final MyNode node = new MyNode(profileForm);
          addNode(node, myRoot);
          selectNodeInTree(node);
      }
  
      private void reloadTree() {
          myRoot.removeAllChildren();
          profileForms.clear();
          final Collection<LessProfile> profiles = lessManager.getProfiles();
          for (final LessProfile profile : profiles) {
              final LessProfile clone = new LessProfile(profile.getId(), profile);
              final LessProfileConfigurableForm profileForm = new LessProfileConfigurableForm(project, clone, this, TREE_UPDATER);
              profileForms.add(profileForm);
              addNode(new MyNode(profileForm), myRoot);
          }
          isInitialized.set(true);
      }
  
      private Map<Integer, LessProfile> getProfileMap() {
          if (!isInitialized.get()) {
              return getManagerProfileMap();
          } else {
              return getUIProfileMap();
          }
      }
  
      private Map<Integer, LessProfile> getManagerProfileMap() {
          return lessManager.getProfileMap();
      }
  
      private Map<Integer, LessProfile> getUIProfileMap() {
          final Map<Integer, LessProfile> profiles = new com.intellij.util.containers.HashMap<Integer, LessProfile>();
          for (int i = 0; i < myRoot.getChildCount(); i++) {
              final MyNode node = (MyNode) myRoot.getChildAt(i);
              final LessProfile lessProfile = ((LessProfileConfigurableForm) node.getConfigurable()).getEditableObject();
              profiles.put(lessProfile.getId(), lessProfile);
          }
          return profiles;
      }
  
      private int getNextId() {
          int id = -1;
          for (final LessProfile profile : getManagerProfileMap().values()) {
              if (profile.getId() > id) {
                  id = profile.getId();
              }
          }
          for (final LessProfile profile : getUIProfileMap().values()) {
              if (profile.getId() > id) {
                  id = profile.getId();
              }
          }
          return id + 1;
      }
  
      @Nullable
      private String askForProfileName(final String title, final String initialName) {
          final String message = UIBundle.message("action.new.less.profile.prompt.message");
          return Messages.showInputDialog(message, title, Messages.getQuestionIcon(), initialName, new InputValidator() {
              public boolean checkInput(final String newName) {
                  if (newName == null || newName.length() == 0) {
                      return false;
                  }
                  // Don't allow duplicate profile names
                  for (final LessProfile profile : getProfileMap().values()) {
                      if (StringUtils.equals(newName, profile.getName())) {
                          return false;
                      }
                  }
                  return true;
              }
  
              public boolean canClose(final String s) {
                  return checkInput(s);
              }
          });
      }
  
      private boolean confirmWarning(final String title, final String message) {
          return Messages.YES == Messages.showYesNoDialog(message, title, Messages.getWarningIcon());
      }
  
      public void setPromptButtonsEnabled(final boolean enabled) {
          for (final LessProfileConfigurableForm profileForm : profileForms) {
              profileForm.setPromptButtonEnabled(enabled);
          }
      }
      /*
       * Method overrides and interface implementations
       */
  
      @NotNull
      public String getId() {
          return "lessc.profiles";
      }
  
      @Nls
      public String getDisplayName() {
          return UIBundle.message("pref.display.name");
      }
  
      @Nullable
      @NonNls
      public String getHelpTopic() {
          return null;
      }
  
      public Runnable enableSearch(final String option) {
          return null;
      }
  
      @Override
      protected String getEmptySelectionString() {
          return UIBundle.message("profile.empty.selection");
      }
  
      @Override
      protected String getComponentStateKey() {
          return "LessCompiler.UI";
      }
  
      @Override
      protected MasterDetailsStateService getStateService() {
          return MasterDetailsStateService.getInstance(project);
      }
  
  }
  