import {
  AST_Binary,
  AST_Conditional,
  AST_Chain,
  AST_Dot,
  AST_Object,
  AST_Sequence,
  AST_Statement,
  AST_Sub,
  AST_UnaryPostfix,
  AST_PrefixedTemplateString,
} from '../ast.js';

// return true if the node at the top of the stack (that means the
// innermost node in the current output) is lexically the first in
// a statement.
function first_in_statement(stack) {
  let node = stack.parent(-1);
  for (let i = 0, p; (p = stack.parent(i)); i++) {
    if (p instanceof AST_Statement && p.body === node) return true;
    if (
      (p instanceof AST_Sequence && p.expressions[0] === node) ||
      (p.TYPE === 'Call' && p.expression === node) ||
      (p instanceof AST_PrefixedTemplateString && p.prefix === node) ||
      (p instanceof AST_Dot && p.expression === node) ||
      (p instanceof AST_Sub && p.expression === node) ||
      (p instanceof AST_Chain && p.expression === node) ||
      (p instanceof AST_Conditional && p.condition === node) ||
      (p instanceof AST_Binary && p.left === node) ||
      (p instanceof AST_UnaryPostfix && p.expression === node)
    ) {
      node = p;
    } else {
      return false;
    }
  }
}

// Returns whether the leftmost item in the expression is an object
function left_is_object(node) {
  if (node instanceof AST_Object) return true;
  if (node instanceof AST_Sequence) return left_is_object(node.expressions[0]);
  if (node.TYPE === 'Call') return left_is_object(node.expression);
  if (node instanceof AST_PrefixedTemplateString)
    return left_is_object(node.prefix);
  if (node instanceof AST_Dot || node instanceof AST_Sub)
    return left_is_object(node.expression);
  if (node instanceof AST_Chain) return left_is_object(node.expression);
  if (node instanceof AST_Conditional) return left_is_object(node.condition);
  if (node instanceof AST_Binary) return left_is_object(node.left);
  if (node instanceof AST_UnaryPostfix) return left_is_object(node.expression);
  return false;
}

export { first_in_statement, left_is_object };