var fs = require("fs");
var t = require("../packages/babel-types");

var NODE_PREFIX = "BabelNode";

var code = `// NOTE: This file is autogenerated. Do not modify.
// See scripts/generate-interfaces.js for script used.

declare class ${NODE_PREFIX}Comment {
  value: string;
  start: number;
  end: number;
  loc: ${NODE_PREFIX}SourceLocation;
}

declare class ${NODE_PREFIX}BlockComment extends ${NODE_PREFIX}Comment {
  type: "BlockComment";
}

declare class ${NODE_PREFIX}LineComment extends ${NODE_PREFIX}Comment {
  type: "LineComment";
}

declare class ${NODE_PREFIX}SourceLocation {
  start: {
    line: number;
    column: number;
  };

  end: {
    line: number;
    column: number;
  };
}

declare class ${NODE_PREFIX} {
  leadingComments: ?Array<${NODE_PREFIX}Comment>;
  innerComments: ?Array<${NODE_PREFIX}Comment>;
  trailingComments: ?Array<${NODE_PREFIX}Comment>;
  start: ?number;
  end: ?number;
  loc: ?${NODE_PREFIX}SourceLocation;
}\n\n`;

//

var lines = [];

for (var type in t.NODE_FIELDS) {
  var fields = t.NODE_FIELDS[type];

  var struct = ['type: "' + type + '";'];
  var args = [];

  for (var fieldName in fields) {
    var field = fields[fieldName];

    var suffix = "";
    if (field.optional || field.default != null) suffix += "?";

    var typeAnnotation = "any";

    var validate = field.validate;
    if (validate) {
      if (validate.oneOf) {
        typeAnnotation = validate.oneOf.map(function (val) {
          return JSON.stringify(val);
        }).join(" | ");
      }

      if (validate.type) {
        typeAnnotation = validate.type;

        if (typeAnnotation === "array") {
          typeAnnotation = "Array<any>";
        }
      }

      if (validate.oneOfNodeTypes) {
        var types = validate.oneOfNodeTypes.map(type => `${NODE_PREFIX}${type}`);
        typeAnnotation = types.join(" | ");
        if (suffix === "?") typeAnnotation = "?" + typeAnnotation;
      }
    }

    if (typeAnnotation) {
      suffix += ": " + typeAnnotation;
    }

    args.push(t.toBindingIdentifierName(fieldName) + suffix);
    if (!t.isValidIdentifier(fieldName)) continue;
    struct.push(fieldName + suffix + ";");
  }

  code += `declare class ${NODE_PREFIX}${type} extends ${NODE_PREFIX} {
  ${struct.join("\n  ").trim()}
}\n\n`;

  // Flow chokes on super() :/
  if (type !== 'Super') {
    lines.push(`declare function ${type[0].toLowerCase() + type.slice(1)}(${args.join(", ")}): ${NODE_PREFIX}${type};`);
  }
}

for (var i = 0; i < t.TYPES.length; i++) {
  lines.push(`declare function is${t.TYPES[i]}(node: Object, opts?: Object): boolean;`);
}

for (var type in t.FLIPPED_ALIAS_KEYS) {
  var types = t.FLIPPED_ALIAS_KEYS[type];
  code += `type ${NODE_PREFIX}${type} = ${types.map(type => `${NODE_PREFIX}${type}`).join(" | ")};\n`;
}

code += `\ndeclare module "babel-types" {
  ${lines.join("\n").replace(/\n/g, "\n  ").trim()}
}\n`;

//

fs.writeFileSync(__dirname + "/../lib/types.js", code);
