package jetbrains.mps.build.mps.pluginSolution.plugin;

/*Generated by MPS */

import jetbrains.mps.project.Project;
import javax.swing.JComponent;
import java.util.List;
import jetbrains.mps.internal.collections.runtime.ListSequence;
import org.jetbrains.mps.openapi.module.SModule;
import jetbrains.mps.project.Solution;
import jetbrains.mps.smodel.Language;
import jetbrains.mps.ide.ui.tree.module.NamespaceTreeBuilder;
import java.util.Set;
import jetbrains.mps.internal.collections.runtime.SetSequence;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import javax.swing.JPanel;
import java.awt.GridBagLayout;
import javax.swing.JLabel;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EtchedBorder;
import javax.swing.border.EmptyBorder;
import jetbrains.mps.ide.common.LayoutUtil;
import jetbrains.mps.ide.ui.tree.MPSTreeNode;
import jetbrains.mps.project.MPSProject;

public class LanguagesStep extends AbstractStep {
  private final AbstractBuildGenerator myGenerator;
  private CheckBoxTree myCheckTree;
  private final Project myProject;
  private final IErrorHandler myHandler;

  public LanguagesStep(Project project, AbstractBuildGenerator generator, IErrorHandler handler) {
    this.myGenerator = generator;
    this.myProject = project;
    this.myHandler = handler;
  }

  @Override
  public void _init() {
    super._init();
    String errorText = null;
    if (!(this.myGenerator.isValid())) {
      errorText = "Invalid input in previous steps.";
    }
    this.myHandler.setErrorText(errorText);
  }

  @Override
  public JComponent createMainComponent() {
    this.myCheckTree = this.createCheckTree();
    return this.myCheckTree;
  }

  public CheckBoxTree createCheckTree() {
    ModulesListData data = new ModulesListData(this.myProject.getProjectModules());
    List<ModuleData> children = data.getModules();
    ListSequence.fromList(children).sort((data1, data2) -> {
      SModule module1 = data1.getModule();
      SModule module2 = data2.getModule();
      if (module1.getClass().getName().equals(module2.getClass().getName())) {
        return data1.getText().compareToIgnoreCase(data2.getText());
      } else if (module1 instanceof Solution) {
        return -1;
      } else if (module2 instanceof Solution) {
        return 1;
      } else if (module1 instanceof Language) {
        return -1;
      }
      return 1;
    }, true);
    NamespaceTreeBuilder builder = new MyTreeBuilder(this.myProject);
    for (ModuleData moduleData : ListSequence.fromList(children)) {
      builder.addNode(new CheckBoxNode(moduleData, false));
    }
    CheckBoxNode allModulesNode = new CheckBoxNode(data, false);
    builder.fillNode(allModulesNode);
    this.fillChildren(allModulesNode);
    CheckBoxTree tree = new CheckBoxTree(allModulesNode);
    tree.checkNodeRecursively(allModulesNode, true);
    return tree;
  }

  public <N extends NodeData> void fillChildren(CheckBoxNode<N> node) {
    int childCount = node.getChildCount();
    for (int i = 0; i < childCount; i++) {
      N data = node.getData();
      CheckBoxNode<N> child = (CheckBoxNode<N>) node.getChildAt(i);
      NodeData childData = child.getData();
      data.addChildren(childData);
      childData.setParent(data);
      this.fillChildren(child);
    }
  }

  @Override
  public String getDescription() {
    return "Select languages and solutions included in build script.";
  }

  @Override
  public void _commit(boolean finish) {
    Set<NodeData> selectedItems = this.myCheckTree.getSelectedItems();
    Set<NodeData> modules = SetSequence.fromSet(new LinkedHashSet<NodeData>());
    for (NodeData item : SetSequence.fromSet(selectedItems)) {
      this.fillWithParents(item, modules);
    }
    List<NodeData> toSort = ListSequence.fromListWithValues(new LinkedList<NodeData>(), modules);
    ListSequence.fromList(toSort).sort((a, b) -> {
      if ((a instanceof NamespaceData) && (b instanceof ModuleData)) {
        return -1;
      } else if ((a instanceof ModuleData) && (b instanceof NamespaceData)) {
        return 1;
      }
      return a.getText().compareToIgnoreCase(b.getText());
    }, true);
    this.myGenerator.setModules(toSort);
  }

  public void fillWithParents(NodeData item, Set<NodeData> collection) {
    SetSequence.fromSet(collection).addElement(item);
    if (item.getParent() != null) {
      this.fillWithParents(item.getParent(), collection);
    }
  }

  @Override
  protected JPanel createStepPanel() {
    JPanel stepPanel = new JPanel(new GridBagLayout());
    stepPanel.add(new JLabel(this.getDescription()), this.createConstraint(0, 0));
    JComponent mainComponent = this.createMainComponent();
    mainComponent.setBorder(new CompoundBorder(new EtchedBorder(), new EmptyBorder(2, 2, 2, 2)));
    stepPanel.add(mainComponent, LayoutUtil.createPanelConstraints(1));
    return stepPanel;
  }

  @Override
  protected boolean doLimitStepPanelHeight() {
    return true;
  }

  public static class MyTreeBuilder extends NamespaceTreeBuilder<CheckBoxNode, CheckBoxNamespaceNode> {
    private final Project myProject;
    public MyTreeBuilder(Project mpsProject) {
      super(new NamespaceTreeBuilder.NamespaceNodeBuilder<CheckBoxNamespaceNode>() {
        @Override
        public CheckBoxNamespaceNode createNamespaceNode(String text) {
          return new CheckBoxNamespaceNode(new NamespaceData(text));
        }
        @Override
        public String getName(CheckBoxNamespaceNode p0) {
          return p0.getName();
        }
        @Override
        public boolean isNamespaceNode(MPSTreeNode p0) {
          return p0 instanceof CheckBoxNamespaceNode;
        }
        @Override
        public void setName(CheckBoxNamespaceNode p0, String p1) {
          p0.setName(p1);
        }
      });
      myProject = mpsProject;
    }
    @Override
    protected String getNamespace(CheckBoxNode node) {
      NodeData data = node.getData();
      String namespace = "";
      if (data instanceof ModuleData) {
        ModuleData moduleData = (ModuleData) data;
        namespace = ((MPSProject) this.myProject).getVirtualFolder(moduleData.getModule());
      }
      return namespace;
    }
  }
}
