/**
 * @author Alberto Cruz Luis <alu0101217734@ull.edu.es>
 * @fileoverview constant-folding
 */

/**
 * Constant Folding Module - Evaluate Expressions in compile time
 * @module constant-folding
 */

const fs = require("fs");
const deb = require('../src/deb.js');
const escodegen = require("escodegen");
const espree = require("espree");
const estraverse = require("estraverse");

"use strict";

module.exports = constantFolding;

/**
 * A function that evaluating expressions
 * @function constantFolding
 * @param {string} code A string that contain a expression
 * @returns {string} Returns result of expression
 */
function constantFolding(code) {
  const t = espree.parse(code, { ecmaVersion: 6, loc: false });
  estraverse.traverse(t, {
    leave: function (n, p) {
      if (
        n.type == "BinaryExpression" && 
          n.left.type == "Literal" && n.right.type == "Literal"
      ) { replaceByLiteral(n); }
      if (
        n.type == "CallExpression" && 
          n.callee.type == "MemberExpression"
      ) { replaceByCallExpression(n); }
    },
  });
  let c = escodegen.generate(t);
  return c
}

/**
 * A function that evaluate a Literal
 * @function replaceByLiteral
 * @param {Object} node Node of AST
 */
function replaceByLiteral(node) {
  node.type = "Literal";

  node.value = eval(`${node.left.raw} ${node.operator} ${node.right.raw}`);
  node.raw = String(node.value);

  delete node.left;
  delete node.right;
}

/**
 * A function that evalute a ArrayExpression
 * @function replaceByArrayExpression
 * @param {Object} node  Node of AST
 */
function replaceByCallExpression(node) {
  if (node.callee.object.type == "ArrayExpression") {
    node.type = node.callee.object.elements[0].type;

    const node_arguments = node.arguments.map((arg) => arg.raw)
    
    if (node.type == "Identifier") {
      const elements = node.callee.object.elements.map((element) => element.name)

      node.name = eval(`[${elements}].${node.callee.property.name}(${node_arguments})`)
    }

    if (node.type == "Literal") {
      const elements = node.callee.object.elements.map((element) => element.raw)

      node.value = eval(`[${elements}].${node.callee.property.name}(${node_arguments})`)
      node.raw = String(node.value);
    }

    delete node.expression;
  }
}