package com.jetbrains.python.edu.debugger;

import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.ColoredTextContainer;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.xdebugger.XSourcePosition;
import com.intellij.xdebugger.frame.XCompositeNode;
import com.intellij.xdebugger.frame.XValue;
import com.intellij.xdebugger.frame.XValueChildrenList;
import com.intellij.xdebugger.frame.XValueGroup;
import com.jetbrains.python.debugger.PyFrameAccessor;
import com.jetbrains.python.debugger.PyStackFrame;
import com.jetbrains.python.debugger.PyStackFrameInfo;
import icons.PythonEducationalIcons;
import icons.PythonIcons;
import icons.PythonPsiApiIcons;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import java.util.HashMap;
import java.util.Map;

public class PyEduStackFrame extends PyStackFrame {
  public static final String MODULE = "<module>";
  public static final String GLOBAL_FRAME = "Globals";
  public static final String DOUBLE_UNDERSCORE = "__";
  public static final String BUILTINS_VALUE_NAME = "__builtins__";

  private final PyStackFrameInfo myFrameInfo;
  private final XSourcePosition myPosition;

  public PyEduStackFrame(@NotNull Project project,
                         @NotNull PyFrameAccessor debugProcess,
                         @NotNull PyStackFrameInfo frameInfo,
                         XSourcePosition position) {
    super(project, debugProcess, frameInfo, position);
    myFrameInfo = frameInfo;
    myPosition = position;
  }

  @Override
  public void customizePresentation(@NotNull ColoredTextContainer component) {
    if (myPosition == null) {
      component.append("<frame not available>", SimpleTextAttributes.GRAY_ATTRIBUTES);
      return;
    }
    final VirtualFile file = myPosition.getFile();
    String frameName = myFrameInfo.getName();
    component.setIcon(MODULE.equals(frameName) ? PythonPsiApiIcons.PythonFile : PythonEducationalIcons.Field);
    if (MODULE.equals(frameName)) {
      component.append(GLOBAL_FRAME, SimpleTextAttributes.REGULAR_ATTRIBUTES);
      component.append(" (" + file.getName() + ")", getGrayAttributes(SimpleTextAttributes.REGULAR_ATTRIBUTES));
    }
    else {
      component.append(frameName, SimpleTextAttributes.REGULAR_ATTRIBUTES);
    }
  }

  @Override
  protected void addChildren(@NotNull final XCompositeNode node,
                             @Nullable final XValueChildrenList children) {
    if (children == null) {
      node.addChildren(XValueChildrenList.EMPTY, true);
      return;
    }
    final Map<String, XValue> specialValues = new HashMap<>();
    XValueChildrenList filteredChildren = new XValueChildrenList();
    for (int i = 0; i < children.size(); i++) {
      String name = children.getName(i);
      XValue value = children.getValue(i);
      if (name.startsWith(DOUBLE_UNDERSCORE) && name.endsWith(DOUBLE_UNDERSCORE)) {
        specialValues.put(name, value);
        continue;
      }
      filteredChildren.add(name, value);
    }
    node.addChildren(filteredChildren, specialValues.isEmpty());
    if (specialValues.isEmpty()) {
      return;
    }
    addSpecialVars(node, specialValues);
  }

  private static void addSpecialVars(@NotNull XCompositeNode node, Map<String, XValue> specialValues) {
      XValue builtins = specialValues.get(BUILTINS_VALUE_NAME);
      if (builtins != null) {
        specialValues.remove(BUILTINS_VALUE_NAME);
        node.addChildren(XValueChildrenList.singleton("Builtins", builtins), false);
      }
      node.addChildren(XValueChildrenList.bottomGroup(createSpecialVarsGroup(specialValues)), true);
  }

  @NotNull
  private static XValueGroup createSpecialVarsGroup(final Map<String, XValue> specialValues) {
    return new XValueGroup("Special Variables") {
      @Nullable
      @Override
      public Icon getIcon() {
        return PythonIcons.Python.Debug.SpecialVar;
      }

      @Override
      public void computeChildren(@NotNull XCompositeNode node) {
        XValueChildrenList list = new XValueChildrenList();
        for (Map.Entry<String, XValue> entry : specialValues.entrySet()) {
          list.add(entry.getKey(), entry.getValue());
        }
        node.addChildren(list, true);
      }
    };
  }
}

@@ -268,209 +268,8 @@
 on;%0A
-import com.intellij.xdebugger.frame.XCompositeNode;%0Aimport com.intellij.xdebugger.frame.XValue;%0Aimport com.intellij.xdebugger.frame.XValueChildrenList;%0Aimport com.intellij.xdebugger.frame.XValueGroup;%0A
 impo
@@ -318,16 +318,16 @@
 cessor;%0A
+
 import c
@@ -465,34 +465,8 @@
 ns;%0A
-import icons.PythonIcons;%0A
 impo
@@ -538,122 +538,8 @@
 ull;
-%0Aimport org.jetbrains.annotations.Nullable;%0A%0Aimport javax.swing.*;%0Aimport java.util.HashMap;%0Aimport java.util.Map;
 %0A%0Apu
@@ -696,130 +696,8 @@
 ls%22;
-%0A  public static final String DOUBLE_UNDERSCORE = %22__%22;%0A  public static final String BUILTINS_VALUE_NAME = %22__builtins__%22;
 %0A%0A  
@@ -1884,24 +1884,24 @@
 TTRIBUTES);%0A
+
     %7D%0A  %7D%0A%0A 
@@ -1901,1956 +1901,7 @@
 %0A  %7D
-%0A%0A  @Override%0A  protected void addChildren(@NotNull final XCompositeNode node,%0A                             @Nullable final XValueChildrenList children) %7B%0A    if (children == null) %7B%0A      node.addChildren(XValueChildrenList.EMPTY, true);%0A      return;%0A    %7D%0A    final Map%3CString, XValue%3E specialValues = new HashMap%3C%3E();%0A    XValueChildrenList filteredChildren = new XValueChildrenList();%0A    for (int i = 0; i %3C children.size(); i++) %7B%0A      String name = children.getName(i);%0A      XValue value = children.getValue(i);%0A      if (name.startsWith(DOUBLE_UNDERSCORE) && name.endsWith(DOUBLE_UNDERSCORE)) %7B%0A        specialValues.put(name, value);%0A        continue;%0A      %7D%0A      filteredChildren.add(name, value);%0A    %7D%0A    node.addChildren(filteredChildren, specialValues.isEmpty());%0A    if (specialValues.isEmpty()) %7B%0A      return;%0A    %7D%0A    addSpecialVars(node, specialValues);%0A  %7D%0A%0A  private static void addSpecialVars(@NotNull XCompositeNode node, Map%3CString, XValue%3E specialValues) %7B%0A      XValue builtins = specialValues.get(BUILTINS_VALUE_NAME);%0A      if (builtins != null) %7B%0A        specialValues.remove(BUILTINS_VALUE_NAME);%0A        node.addChildren(XValueChildrenList.singleton(%22Builtins%22, builtins), false);%0A      %7D%0A      node.addChildren(XValueChildrenList.bottomGroup(createSpecialVarsGroup(specialValues)), true);%0A  %7D%0A%0A  @NotNull%0A  private static XValueGroup createSpecialVarsGroup(final Map%3CString, XValue%3E specialValues) %7B%0A    return new XValueGroup(%22Special Variables%22) %7B%0A      @Nullable%0A      @Override%0A      public Icon getIcon() %7B%0A        return PythonIcons.Python.Debug.SpecialVar;%0A      %7D%0A%0A      @Override%0A      public void computeChildren(@NotNull XCompositeNode node) %7B%0A        XValueChildrenList list = new XValueChildrenList();%0A        for (Map.Entry%3CString, XValue%3E entry : specialValues.entrySet()) %7B%0A          list.add(entry.getKey(), entry.getValue());%0A        %7D%0A        node.addChildren(list, true);%0A      %7D%0A    %7D;%0A  %7D
 %0A%7D%0A

remove special and builtins groups from edu debugger (moved to PyCharm)