package org.jetbrains.debugger;

import com.intellij.openapi.util.ActionCallback;
import com.intellij.util.Consumer;
import com.intellij.xdebugger.XDebuggerBundle;
import com.intellij.xdebugger.frame.XCompositeNode;
import com.intellij.xdebugger.frame.XValueChildrenList;
import com.intellij.xdebugger.frame.XValueGroup;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public class ScopeVariablesGroup extends XValueGroup {
  private final Scope scope;
  private final VariableContext context;
  private final CallFrame callFrame;

  public ScopeVariablesGroup(@NotNull final Scope scope, @NotNull final VariableContext context, @Nullable CallFrame callFrame) {
    super(createScopeNodeName(scope));

    this.scope = scope;
    this.callFrame = callFrame;

    if (callFrame == null) {
      this.context = new VariableContextBase() {
        @NotNull
        @Override
        public EvaluateContext getEvaluateContext() {
          return context.getEvaluateContext();
        }

        @NotNull
        @Override
        public DebugProcessEx getDebugProcess() {
          return context.getDebugProcess();
        }

        @Override
        public boolean watchableAsEvaluationExpression() {
          // functions scopes - we can watch variables only from global scope
          return scope.getType() == Scope.Type.GLOBAL;
        }
      };
    }
    else {
      this.context = context;
    }
  }

  private static String createScopeNodeName(@NotNull Scope scope) {
    switch (scope.getType()) {
      case GLOBAL:
        return XDebuggerBundle.message("scope.global");
      case LOCAL:
        return XDebuggerBundle.message("scope.local");
      case WITH:
        return XDebuggerBundle.message("scope.with");
      case CLOSURE:
        return XDebuggerBundle.message("scope.closure");
      case CATCH:
        return XDebuggerBundle.message("scope.catch");
      case LIBRARY:
        return XDebuggerBundle.message("scope.library");
      case INSTANCE:
        return XDebuggerBundle.message("scope.instance");
      case CLASS:
        return XDebuggerBundle.message("scope.class");
      case UNKNOWN:
        return XDebuggerBundle.message("scope.unknown");
      default:
        throw new IllegalArgumentException(scope.getType().name());
    }
  }

  @Override
  public boolean isAutoExpand() {
    return scope.getType() == Scope.Type.LOCAL || scope.getType() == Scope.Type.CATCH;
  }

  @Nullable
  @Override
  public String getComment() {
    String className = scope.getClassName();
    return "Object".equals(className) ? null : className;
  }

  @Override
  public void computeChildren(final @NotNull XCompositeNode node) {
    ActionCallback callback = callFrame == null || scope.getType() != Scope.Type.LOCAL ? null : new ActionCallback().doWhenDone(new Runnable() {
      @Override
      public void run() {
        if (node.isObsolete()) {
          return;
        }

        callFrame.getReceiverVariable().doWhenDone(new Consumer<Variable>() {
          @Override
          public void consume(Variable variable) {
            if (!node.isObsolete()) {
              node.addChildren(variable == null ? XValueChildrenList.EMPTY : XValueChildrenList.singleton(CallFrameBase.RECEIVER_NAME, new VariableView(context, variable)), true);
            }
          }
        }).doWhenRejected(new Consumer<String>() {
          @Override
          public void consume(@Nullable String error) {
            if (!node.isObsolete()) {
              node.addChildren(XValueChildrenList.EMPTY, true);
            }
          }
        });
      }
    });
    Variables.consume(scope, node, context, callback);
  }
}
@@ -2539,17 +2539,19 @@
 .get
-ClassName
+Description
 ();%0A

rename scope.className to scope.description — actually, it is description