/*
 * Copyright 2003-2014 JetBrains s.r.o.
 *
 * 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 jetbrains.mps.intentions;

import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.options.Configurable.Composite;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.ui.CheckboxTree;
import com.intellij.ui.CheckboxTree.CheckboxTreeCellRenderer;
import com.intellij.ui.CheckboxTreeBase.CheckPolicy;
import com.intellij.ui.CheckedTreeNode;
import com.intellij.ui.ColoredTreeCellRenderer;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.uiDesigner.core.GridConstraints;
import com.intellij.uiDesigner.core.GridLayoutManager;
import com.intellij.util.containers.SortedList;
import com.intellij.util.ui.tree.TreeUtil;
import jetbrains.mps.util.StringUtil;
import org.jetbrains.annotations.Nls;

import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JTree;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class IntentionSettingsConfigurable implements Configurable, Composite {
  private IntentionsManager myIntentionsManager;

  private CheckboxTree myTree;
  private Map<String, LanguageTreeNode> myLanguageTreeNodes = new HashMap<String, LanguageTreeNode>();

  public IntentionSettingsConfigurable(IntentionsManager intentionsManager) {
    myIntentionsManager = intentionsManager;
  }

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

  @Override
  public String getHelpTopic() {
    // MPS-13404
    return "preferences.intentionPowerPack";
  }

  @Override
  public JComponent createComponent() {
    initCheckBoxes();
    CheckedTreeNode rootNode = new CheckedTreeNode(null);
    for (LanguageTreeNode languageTreeNode : myLanguageTreeNodes.values()) {
      rootNode.add(languageTreeNode);
    }
    myTree = new CheckboxTree(getCheckboxTreeCellRenderer(), rootNode, new CheckPolicy(true, true, true, false));
    myTree.setRootVisible(false);
    TreeUtil.expandAll(myTree);

    JPanel mainPanel = new JPanel(new GridLayoutManager(1,1));

    mainPanel.add(ScrollPaneFactory.createScrollPane(myTree), new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_NORTHWEST, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null));
    return mainPanel;
  }

  private void initCheckBoxes() {
    List<IntentionDescriptor> intentionList = new SortedList<IntentionDescriptor>(new Comparator<IntentionDescriptor>() {
      @Override
      public int compare(IntentionDescriptor o1, IntentionDescriptor o2) {
        int langCompare = StringUtil.compare(o1.getLanguageFqName(), o2.getLanguageFqName());
        return langCompare != 0 ? langCompare : StringUtil.compare(o1.getPresentation().toLowerCase(), o2.getPresentation().toLowerCase());
      }
    });
    intentionList.addAll(myIntentionsManager.getAllIntentionFactories());
    for(IntentionDescriptor descriptor : intentionList) {
      final String languageFqName = descriptor.getLanguageFqName() != null ? descriptor.getLanguageFqName() : "Unspecified Language";
      LanguageTreeNode langTreeNode = myLanguageTreeNodes.get(languageFqName);
      if(langTreeNode == null) {
        langTreeNode = new LanguageTreeNode(languageFqName);
        myLanguageTreeNodes.put(languageFqName, langTreeNode);
      }
      langTreeNode.add(new IntentionTreeNode(descriptor));
    }
  }

  @Override
  public boolean isModified() {
    for(LanguageTreeNode languageTreeNode : myLanguageTreeNodes.values()) {
      Enumeration<IntentionTreeNode> intentionTreeNode = languageTreeNode.children();
      while (intentionTreeNode.hasMoreElements()) {
        if (intentionTreeNode.nextElement().isModified()) {
          return true;
        }
      }
    }
    return false;
  }

  @Override
  public void apply() throws ConfigurationException {
    for(LanguageTreeNode languageTreeNode : myLanguageTreeNodes.values()) {
      Enumeration<IntentionTreeNode> intentionTreeNode = languageTreeNode.children();
      while (intentionTreeNode.hasMoreElements()) {
        intentionTreeNode.nextElement().apply();
      }
    }
  }

  @Override
  public void reset() {
    for(LanguageTreeNode languageTreeNode : myLanguageTreeNodes.values()) {
      Enumeration<IntentionTreeNode> intentionTreeNode = languageTreeNode.children();
      while (intentionTreeNode.hasMoreElements()) {
        intentionTreeNode.nextElement().reset();
      }
    }
  }

  @Override
  public void disposeUIResources() {
    myTree = null;
  }

  @Override
  public Configurable[] getConfigurables() {
    return new Configurable[0];
  }

  private class LanguageTreeNode extends CheckedTreeNode {

    public LanguageTreeNode(final String languageFqName) {
      super(languageFqName);
    }

    @Override
    public String getUserObject() {
      return (String)super.getUserObject();
    }
  }

  private class IntentionTreeNode extends CheckedTreeNode {
    private final String myPersistentStateKey;

    public IntentionTreeNode(final IntentionDescriptor intentionDescriptor) {
      super(intentionDescriptor);
      myPersistentStateKey = intentionDescriptor.getPersistentStateKey();
    }

    @Override
    public IntentionDescriptor getUserObject() {
      return (IntentionDescriptor)super.getUserObject();
    }

    private boolean isModified() {
      return myIntentionsManager.isIntentionDisabled(myPersistentStateKey) == isChecked();
    }

    private void apply() {
      myIntentionsManager.setIntentionState(myPersistentStateKey, !isChecked());
    }

    private void reset() {
      setChecked(!myIntentionsManager.isIntentionDisabled(myPersistentStateKey));
    }
  }

  private static CheckboxTreeCellRenderer getCheckboxTreeCellRenderer() {
    return new CheckboxTreeCellRenderer() {
      @Override
      public void customizeRenderer(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
        super.customizeRenderer(tree, value, selected, expanded, leaf, row, hasFocus);
        ColoredTreeCellRenderer renderer = getTextRenderer();
        JCheckBox checkBox = getCheckbox();

        if (value instanceof IntentionTreeNode) {
          IntentionTreeNode intentionTreeNode = (IntentionTreeNode) value;
          renderer.append(intentionTreeNode.getUserObject().getPresentation());
          checkBox.setSelected(intentionTreeNode.isChecked());
        } else if (value instanceof LanguageTreeNode) {
          LanguageTreeNode languageTreeNode = (LanguageTreeNode) value;
          renderer.append(languageTreeNode.getUserObject());
          checkBox.setSelected(languageTreeNode.isChecked());
        }

      }
    };
  }
}
