/* eslint-env node */
"use strict";

/*
  This is the beginning of an experimental code transform for cardstack templates.

  It runs on any template that appears under
  templatse/components/cardstack. It transforms direct property
  bindings like:

    {{content.title}}

  Into simple cs-field calls like:

    {{cs-field content "title"}}

  It also transforms attribute assigments into cs-field calls. For example:

    <img src={{content.imageUrl}} />

  becomes

    {{#cs-field content "imageUrl" as |param1|}}<img src={{param1}}></img>{{/cs-field}}

  It does this in order to annotate those fields for editable by the cardstack tools.

*/

class BindTransform {
  constructor({ moduleName }) {
    this.moduleName = moduleName;
    this.syntax = null;
  }

  transform(ast) {
    if (!/\btemplates\/components\/cardstack\//.test(this.moduleName) &&
      !/\btemplates\/(embedded|isolated).hbs/.test(this.moduleName)) {
      return ast;
    }

    let b = this.syntax.builders;
    let foundBlockParams = this._collectBlockParams(ast);

    this.syntax.traverse(ast, {
      ElementNode(node) {
        let contentProperty,
          unusedBlockParam,
          foundDynamicContent = false,
          newAttributes = [];

        let tag = node.tag;
        for (let i = 0; i < node.attributes.length; i++) {
          let nodeAttributes = node.attributes[i];
          let name = nodeAttributes.name;
          let value = nodeAttributes.value;
          if (!value) {
            break;
          }

          if (value.type === 'MustacheStatement') {
            let path = value.path;
            let parts = path.parts;
            if (parts && parts.length === 2 && parts[0] === 'content') {
              foundDynamicContent = true;
              // contentProperty is the property that is looked up on content
              // (e.g `imageUrl` in the case of `content.imageUrl`)
              unusedBlockParam = getUnusedBlockParam(foundBlockParams);
              contentProperty = parts[1];
              newAttributes.push(b.attr(name, b.mustache(b.path(unusedBlockParam))));
            } else {
              newAttributes.push(b.attr(name, value));
            }
          } else if (nodeAttributes.type === 'AttrNode') {
            newAttributes.push(b.attr(name, value));
          } else if (nodeAttributes.type === 'TextNode') {
            newAttributes.push(b.attr(name, value.chars));
          }
        }

        if (foundDynamicContent) {
          let newTag = b.element(tag, newAttributes, []);
          let blockWithParam = b.program([newTag], [unusedBlockParam]);
          let block = b.block(b.path('cs-field'), [
            b.path("content"), b.string(contentProperty)
          ], b.hash(), blockWithParam);
          return block;
        }

        return node;
      },

      MustacheStatement(node) {
        if (node.path.parts && node.path.parts.length === 2 && node.path.parts[0] === 'content') {
          return b.mustache(b.path("cs-field"), [b.path("content"), b.string(node.path.parts[1])]);
        }
      }
    });

    return ast;
  }

  _collectBlockParams(ast) {
    let blockParams = [];

    this.syntax.traverse(ast, {
      Program(node) {
        blockParams.push(...node.blockParams);
      }
    });

    return blockParams;
  }
}

function getUnusedBlockParam(foundNames) {
  let foundName = false;
  let i = 0;
  let paramName;

  do {
    i++;
    paramName = 'param' + i;
    if (foundNames.indexOf(paramName) === -1) {
      foundName = true;
    }
  } while (!foundName);

  return paramName;
}

module.exports = BindTransform;

@@ -3346,82 +3346,40 @@
 %7B%0A  
-let foundName = false;%0A  let i = 0;%0A  let paramName;%0A%0A  do %7B%0A   
+for (let i = 1;;
  i++
-;
+) %7B
 %0A    
+let 
 para
@@ -3403,24 +3403,25 @@
  i;%0A    if (
+!
 foundNames.i
@@ -3425,13 +3425,14 @@
 s.in
-dexOf
+cludes
 (par
@@ -3442,15 +3442,8 @@
 ame)
- === -1
 ) %7B%0A
@@ -3452,59 +3452,8 @@
     
-foundName = true;%0A    %7D%0A  %7D while (!foundName);%0A%0A  
 retu
@@ -3465,16 +3465,26 @@
 ramName;
+%0A    %7D%0A  %7D
 %0A%7D%0A%0Amodu

Simplify `getUnusedBlockParam()` function