// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/**
 * @fileoverview Expression mutator.
 */

'use strict';

const babelTemplate = require('@babel/template').default;

const common = require('./common.js');
const random = require('../random.js');
const mutator = require('./mutator.js');
const sourceHelpers = require('../source_helpers.js');

class CrossOverMutator extends mutator.Mutator {
  constructor(settings, db) {
    super();
    this.settings = settings;
    this.db = db;
  }

  get visitor() {
    const thisMutator = this;

    return [{
      ExpressionStatement(path) {
        if (!random.choose(thisMutator.settings.MUTATE_CROSSOVER_INSERT)) {
          return;
        }

        const canHaveSuper = Boolean(path.findParent(x => x.isClassMethod()));
        const randomExpression = thisMutator.db.getRandomStatement(
            {canHaveSuper: canHaveSuper});

        // Insert the statement.
        var templateOptions = Object.assign({}, sourceHelpers.BABYLON_OPTIONS);
        templateOptions['placeholderPattern'] = /^VAR_[0-9]+$/;

        let toInsert = babelTemplate(
            randomExpression.source,
            templateOptions);
        const dependencies = {};

        if (randomExpression.dependencies) {
          const variables = common.availableVariables(path);
          if (!variables.length) {
            return;
          }
          for (const dependency of randomExpression.dependencies) {
            dependencies[dependency] = random.single(variables);
          }
        }

        try {
          toInsert = toInsert(dependencies);
        } catch (e) {
          if (thisMutator.settings.testing) {
            // Fail early in tests.
            throw e;
          }
          console.log('ERROR: Failed to parse:', randomExpression.source);
          console.log(e);
          return;
        }

        thisMutator.annotate(
            toInsert,
            'Crossover from ' + randomExpression.originalPath);

        if (random.choose(0.5)) {
          thisMutator.insertBeforeSkip(path, toInsert);
        } else {
          thisMutator.insertAfterSkip(path, toInsert);
        }

        path.skip();
      },
    }, {
    }];
  }
}

module.exports = {
  CrossOverMutator: CrossOverMutator,
};
