package jetbrains.mps.debugger.api.ui.breakpoints;

/*Generated by MPS */

import jetbrains.mps.annotations.GeneratedClass;
import jetbrains.mps.ide.platform.ui.CheckBoxNodeRenderer;
import jetbrains.mps.ide.ui.tree.MPSTree;
import jetbrains.mps.ide.ui.tree.MPSTreeNode;
import java.util.Collection;
import org.jetbrains.annotations.Nullable;
import javax.swing.Icon;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
import java.util.Set;
import java.util.HashMap;
import java.util.HashSet;
import javax.swing.UIManager;

@GeneratedClass(node = "r:e8d15a56-f89d-47fc-ac9f-8a35d3539ac3(jetbrains.mps.debugger.api.ui.breakpoints)/4474271214083111858", model = "r:e8d15a56-f89d-47fc-ac9f-8a35d3539ac3(jetbrains.mps.debugger.api.ui.breakpoints)")
/*package*/ abstract class GroupedTree<D extends CheckBoxNodeRenderer.NodeData> extends MPSTree {
  public GroupedTree() {
    setCellRenderer(new CheckBoxNodeRenderer(true, getCellRenderer()));
    setCellEditor(new CheckBoxNodeRenderer.CheckBoxNodeEditor<CheckBoxNodeRenderer.NodeData>(true) {
      @Override
      protected CheckBoxNodeRenderer.NodeData createNodeData(boolean selected) {
        CheckBoxNodeRenderer.NodeData data = getObject();
        data.setSelected(selected);
        repaint();
        return data;
      }
    });
    setEditable(true);
  }
  protected abstract MPSTreeNode createDataNode(D data);
  protected abstract GroupKind<D, Object> createRootGroupKind();
  protected abstract Collection<D> getData();

  @Override
  protected MPSTreeNode rebuild() {
    return new GroupTreeNode(createRootGroupKind(), new Object(), getData());
  }

  @Nullable
  public MPSTreeNode findNodeForData(D nodeData) {
    if (this.isEmpty()) {
      return null;
    }
    return (MPSTreeNode) findNodeWith(nodeData);
  }

  public static abstract class GroupKind<D, T> {
    private boolean myIsVisible = true;
    public GroupKind() {
    }
    @Nullable
    public abstract T getGroup(D data);
    @Nullable
    public Icon getIcon(T group) {
      return null;
    }
    public String getText(@NotNull T group) {
      return group.toString();
    }
    @Nullable
    public GroupKind getSubGroupKind() {
      return null;
    }
    public Map<T, Set<D>> sortByGroups(Collection<D> dataToSort) {
      Map<T, Set<D>> result = new HashMap<T, Set<D>>();
      for (D data : dataToSort) {
        T group = getGroup(data);
        Set<D> dataForGroup = result.get(group);
        if (dataForGroup == null) {
          dataForGroup = new HashSet<D>();
          result.put(group, dataForGroup);
        }
        dataForGroup.add(data);
      }
      return result;
    }
    public boolean isVisible() {
      return myIsVisible;
    }
    public void setVisible(boolean isVisible) {
      myIsVisible = isVisible;
    }
  }

  private class GroupTreeNode<T> extends MPSTreeNode {

    public GroupTreeNode(@NotNull GroupKind<D, T> kind, @NotNull T group, Collection<D> data) {
      // populate tree element with all data necessary for rendering, so that CheckBoxNodeRenderer shall not care to grab a model lock
      // CheckBoxNodeRenderer deals with
      String text = kind.getText(group);
      setNodeIdentifier(text);
      setText(text);
      // XXX any reason to set it here provided there's renderer that likely does its own coloring?
      setColor(UIManager.getColor("Tree.textForeground"));
      setIcon(kind.getIcon(group));

      // CheckBoxNodeRenderer uses NodeData to represent checked state, therefore we have to attach NodeData to group node to
      // represent composite state of its children
      setUserObject(new CheckBoxNodeRenderer.NodeData() {
        @Override
        public Icon getIcon() {
          return GroupTreeNode.this.getIcon();
        }

        @Override
        public String getText() {
          return GroupTreeNode.this.getText();
        }

        @Override
        public boolean isSelected() {
          for (MPSTreeNode c : GroupTreeNode.this.getChildren()) {
            if (c.getUserObject() instanceof CheckBoxNodeRenderer.NodeData) {
              if (!(((CheckBoxNodeRenderer.NodeData) c.getUserObject()).isSelected())) {
                return false;
              }
            }
            // ignore other tree nodes
          }
          return true;
        }

        @Override
        public void setSelected(boolean selected) {
          for (MPSTreeNode c : GroupTreeNode.this.getChildren()) {
            if (c.getUserObject() instanceof CheckBoxNodeRenderer.NodeData) {
              ((CheckBoxNodeRenderer.NodeData) c.getUserObject()).setSelected(selected);
            }
          }
        }
      });

      GroupKind<D, Object> subGroupKind = kind.getSubGroupKind();
      while (subGroupKind != null && !(subGroupKind.isVisible())) {
        subGroupKind = subGroupKind.getSubGroupKind();
      }
      if (subGroupKind == null) {
        for (D d : data) {
          add(createDataNode(d));
        }
      } else {
        Map<Object, Set<D>> sorted = subGroupKind.sortByGroups(data);
        for (Object subGroup : sorted.keySet()) {
          if (subGroup != null) {
            add(new GroupTreeNode(subGroupKind, subGroup, sorted.get(subGroup)));
          } else {
            for (D d : sorted.get(subGroup)) {
              add(createDataNode(d));
            }
          }
        }
      }
    }
  }
}
