text:
import is from "../validators/is";
import isValidIdentifier from "../validators/isValidIdentifier";
import { isKeyword, isReservedWord } from "@babel/helper-validator-identifier";

import {
  BINARY_OPERATORS,
  LOGICAL_OPERATORS,
  ASSIGNMENT_OPERATORS,
  UNARY_OPERATORS,
  UPDATE_OPERATORS,
} from "../constants";

import {
  defineAliasedType,
  assertShape,
  assertOptionalChainStart,
  assertValueType,
  assertNodeType,
  assertNodeOrValueType,
  assertEach,
  chain,
  assertOneOf,
  validateOptional,
} from "./utils";

const defineType = defineAliasedType("Standardized");

defineType("ArrayExpression", {
  fields: {
    elements: {
      validate: chain(
        assertValueType("array"),
        assertEach(
          assertNodeOrValueType("null", "Expression", "SpreadElement"),
        ),
      ),
      default: !process.env.BABEL_TYPES_8_BREAKING ? [] : undefined,
    },
  },
  visitor: ["elements"],
  aliases: ["Expression"],
});

defineType("AssignmentExpression", {
  fields: {
    operator: {
      validate: (function () {
        if (!process.env.BABEL_TYPES_8_BREAKING) {
          return assertValueType("string");
        }

        const identifier = assertOneOf(...ASSIGNMENT_OPERATORS);
        const pattern = assertOneOf("=");

        return function (node, key, val) {
          const validator = is("Pattern", node.left) ? pattern : identifier;
          validator(node, key, val);
        };
      })(),
    },
    left: {
      validate: !process.env.BABEL_TYPES_8_BREAKING
        ? assertNodeType("LVal")
        : assertNodeType(
            "Identifier",
            "MemberExpression",
            "ArrayPattern",
            "ObjectPattern",
          ),
    },
    right: {
      validate: assertNodeType("Expression"),
    },
  },
  builder: ["operator", "left", "right"],
  visitor: ["left", "right"],
  aliases: ["Expression"],
});

defineType("BinaryExpression", {
  builder: ["operator", "left", "right"],
  fields: {
    operator: {
      validate: assertOneOf(...BINARY_OPERATORS),
    },
    left: {
      validate: (function () {
        const expression = assertNodeType("Expression");
        const inOp = assertNodeType("Expression", "PrivateName");

        const validator = function (node, key, val) {
          const validator = node.operator === "in" ? inOp : expression;
          validator(node, key, val);
        };
        // todo(ts): can be discriminated union by `operator` property
        validator.oneOfNodeTypes = ["Expression", "PrivateName"];
        return validator;
      })(),
    },
    right: {
      validate: assertNodeType("Expression"),
    },
  },
  visitor: ["left", "right"],
  aliases: ["Binary", "Expression"],
});

defineType("InterpreterDirective", {
  builder: ["value"],
  fields: {
    value: {
      validate: assertValueType("string"),
    },
  },
});

defineType("Directive", {
  visitor: ["value"],
  fields: {
    value: {
      validate: assertNodeType("DirectiveLiteral"),
    },
  },
});

defineType("DirectiveLiteral", {
  builder: ["value"],
  fields: {
    value: {
      validate: assertValueType("string"),
    },
  },
});

defineType("BlockStatement", {
  builder: ["body", "directives"],
  visitor: ["directives", "body"],
  fields: {
    directives: {
      validate: chain(
        assertValueType("array"),
        assertEach(assertNodeType("Directive")),
      ),
      default: [],
    },
    body: {
      validate: chain(
        assertValueType("array"),
        assertEach(assertNodeType("Statement")),
      ),
    },
  },
  aliases: ["Scopable", "BlockParent", "Block", "Statement"],
});

defineType("BreakStatement", {
  visitor: ["label"],
  fields: {
    label: {
      validate: assertNodeType("Identifier"),
      optional: true,
    },
  },
  aliases: ["Statement", "Terminatorless", "CompletionStatement"],
});

defineType("CallExpression", {
  visitor: ["callee", "arguments", "typeParameters", "typeArguments"],
  builder: ["callee", "arguments"],
  aliases: ["Expression"],
  fields: {
    callee: {
      validate: assertNodeType("Expression", "V8IntrinsicIdentifier"),
    },
    arguments: {
      validate: chain(
        assertValueType("array"),
        assertEach(
          assertNodeType(
            "Expression",
            "SpreadElement",
            "JSXNamespacedName",
            "ArgumentPlaceholder",
          ),
        ),
      ),
    },
    ...(!process.env.BABEL_TYPES_8_BREAKING
      ? {
          optional: {
            validate: assertOneOf(true, false),
            optional: true,
          },
        }
      : {}),
    typeArguments: {
      validate: assertNodeType("TypeParameterInstantiation"),
      optional: true,
    },
    typeParameters: {
      validate: assertNodeType("TSTypeParameterInstantiation"),
      optional: true,
    },
  },
});

defineType("CatchClause", {
  visitor: ["param", "body"],
  fields: {
    param: {
      validate: assertNodeType("Identifier", "ArrayPattern", "ObjectPattern"),
      optional: true,
    },
    body: {
      validate: assertNodeType("BlockStatement"),
    },
  },
  aliases: ["Scopable", "BlockParent"],
});

defineType("ConditionalExpression", {
  visitor: ["test", "consequent", "alternate"],
  fields: {
    test: {
      validate: assertNodeType("Expression"),
    },
    consequent: {
      validate: assertNodeType("Expression"),
    },
    alternate: {
      validate: assertNodeType("Expression"),
    },
  },
  aliases: ["Expression", "Conditional"],
});

defineType("ContinueStatement", {
  visitor: ["label"],
  fields: {
    label: {
      validate: assertNodeType("Identifier"),
      optional: true,
    },
  },
  aliases: ["Statement", "Terminatorless", "CompletionStatement"],
});

defineType("DebuggerStatement", {
  aliases: ["Statement"],
});

defineType("DoWhileStatement", {
  visitor: ["test", "body"],
  fields: {
    test: {
      validate: assertNodeType("Expression"),
    },
    body: {
      validate: assertNodeType("Statement"),
    },
  },
  aliases: ["Statement", "BlockParent", "Loop", "While", "Scopable"],
});

defineType("EmptyStatement", {
  aliases: ["Statement"],
});

defineType("ExpressionStatement", {
  visitor: ["expression"],
  fields: {
    expression: {
      validate: assertNodeType("Expression"),
    },
  },
  aliases: ["Statement", "ExpressionWrapper"],
});

defineType("File", {
  builder: ["program", "comments", "tokens"],
  visitor: ["program"],
  fields: {
    program: {
      validate: assertNodeType("Program"),
    },
    comments: {
      validate: !process.env.BABEL_TYPES_8_BREAKING
        ? Object.assign(() => {}, {
            each: { oneOfNodeTypes: ["CommentBlock", "CommentLine"] },
          })
        : assertEach(assertNodeType("CommentBlock", "CommentLine")),
      optional: true,
    },
    tokens: {
      // todo(ts): add Token type
      validate: assertEach(Object.assign(() => {}, { type: "any" })),
      optional: true,
    },
  },
});

defineType("ForInStatement", {
  visitor: ["left", "right", "body"],
  aliases: [
    "Scopable",
    "Statement",
    "For",
    "BlockParent",
    "Loop",
    "ForXStatement",
  ],
  fields: {
    left: {
      validate: !process.env.BABEL_TYPES_8_BREAKING
        ? assertNodeType("VariableDeclaration", "LVal")
        : assertNodeType(
            "VariableDeclaration",
            "Identifier",
            "MemberExpression",
            "ArrayPattern",
            "ObjectPattern",
          ),
    },
    right: {
      validate: assertNodeType("Expression"),
    },
    body: {
      validate: assertNodeType("Statement"),
    },
  },
});

defineType("ForStatement", {
  visitor: ["init", "test", "update", "body"],
  aliases: ["Scopable", "Statement", "For", "BlockParent", "Loop"],
  fields: {
    init: {
      validate: assertNodeType("VariableDeclaration", "Expression"),
      optional: true,
    },
    test: {
      validate: assertNodeType("Expression"),
      optional: true,
    },
    update: {
      validate: assertNodeType("Expression"),
      optional: true,
    },
    body: {
      validate: assertNodeType("Statement"),
    },
  },
});

export const functionCommon = {
  params: {
    validate: chain(
      assertValueType("array"),
      assertEach(assertNodeType("Identifier", "Pattern", "RestElement")),
    ),
  },
  generator: {
    default: false,
  },
  async: {
    default: false,
  },
};

export const functionTypeAnnotationCommon = {
  returnType: {
    validate: process.env.BABEL_8_BREAKING
      ? assertNodeType("TypeAnnotation", "TSTypeAnnotation")
      : assertNodeType("TypeAnnotation", "TSTypeAnnotation", "Noop"),
    optional: true,
  },
  typeParameters: {
    validate: process.env.BABEL_8_BREAKING
      ? assertNodeType("TypeParameterDeclaration", "TSTypeParameterDeclaration")
      : assertNodeType(
          "TypeParameterDeclaration",
          "TSTypeParameterDeclaration",
          "Noop",
        ),
    optional: true,
  },
};

export const functionDeclarationCommon = {
  ...functionCommon,
  declare: {
    validate: assertValueType("boolean"),
    optional: true,
  },
  id: {
    validate: assertNodeType("Identifier"),
    optional: true, // May be null for `export default function`
  },
};

defineType("FunctionDeclaration", {
  builder: ["id", "params", "body", "generator", "async"],
  visitor: ["id", "params", "body", "returnType", "typeParameters"],
  fields: {
    ...functionDeclarationCommon,
    ...functionTypeAnnotationCommon,
    body: {
      validate: assertNodeType("BlockStatement"),
    },
  },
  aliases: [
    "Scopable",
    "Function",
    "BlockParent",
    "FunctionParent",
    "Statement",
    "Pureish",
    "Declaration",
  ],
  validate: (function () {
    if (!process.env.BABEL_TYPES_8_BREAKING) return () => {};

    const identifier = assertNodeType("Identifier");

    return function (parent, key, node) {
      if (!is("ExportDefaultDeclaration", parent)) {
        identifier(node, "id", node.id);
      }
    };
  })(),
});

defineType("FunctionExpression", {
  inherits: "FunctionDeclaration",
  aliases: [
    "Scopable",
    "Function",
    "BlockParent",
    "FunctionParent",
    "Expression",
    "Pureish",
  ],
  fields: {
    ...functionCommon,
    ...functionTypeAnnotationCommon,
    id: {
      validate: assertNodeType("Identifier"),
      optional: true,
    },
    body: {
      validate: assertNodeType("BlockStatement"),
    },
  },
});

export const patternLikeCommon = {
  typeAnnotation: {
    validate: process.env.BABEL_8_BREAKING
      ? assertNodeType("TypeAnnotation", "TSTypeAnnotation")
      : assertNodeType("TypeAnnotation", "TSTypeAnnotation", "Noop"),
    optional: true,
  },
  decorators: {
    validate: chain(
      assertValueType("array"),
      assertEach(assertNodeType("Decorator")),
    ),
  },
};

defineType("Identifier", {
  builder: ["name"],
  visitor: ["typeAnnotation", "decorators" /* for legacy param decorators */],
  aliases: ["Expression", "PatternLike", "LVal", "TSEntityName"],
  fields: {
    ...patternLikeCommon,
    name: {
      validate: chain(
        assertValueType("string"),
        Object.assign(
          function (node, key, val) {
            if (!process.env.BABEL_TYPES_8_BREAKING) return;

            if (!isValidIdentifier(val, false)) {
              throw new TypeError(`"${val}" is not a valid identifier name`);
            }
          },
          { type: "string" },
        ),
      ),
    },
    optional: {
      validate: assertValueType("boolean"),
      optional: true,
    },
  },
  validate(parent, key, node) {
    if (!process.env.BABEL_TYPES_8_BREAKING) return;

    const match = /\.(\w+)$/.exec(key);
    if (!match) return;

    const [, parentKey] = match;
    const nonComp = { computed: false };

    // We can't check if `parent.property === node`, because nodes are validated
    // before replacing them in the AST.
    if (parentKey === "property") {
      if (is("MemberExpression", parent, nonComp)) return;
      if (is("OptionalMemberExpression", parent, nonComp)) return;
    } else if (parentKey === "key") {
      if (is("Property", parent, nonComp)) return;
      if (is("Method", parent, nonComp)) return;
    } else if (parentKey === "exported") {
      if (is("ExportSpecifier", parent)) return;
    } else if (parentKey === "imported") {
      if (is("ImportSpecifier", parent, { imported: node })) return;
    } else if (parentKey === "meta") {
      if (is("MetaProperty", parent, { meta: node })) return;
    }

    if (
      // Ideally we should call isStrictReservedWord if this node is a descendant
      // of a block in strict mode. Also, we should pass the inModule option so
      // we can disable "await" in module.
      (isKeyword(node.name) || isReservedWord(node.name, false)) &&
      // Even if "this" is a keyword, we are using the Identifier
      // node to represent it.
      node.name !== "this"
    ) {
      throw new TypeError(`"${node.name}" is not a valid identifier`);
    }
  },
});

defineType("IfStatement", {
  visitor: ["test", "consequent", "alternate"],
  aliases: ["Statement", "Conditional"],
  fields: {
    test: {
      validate: assertNodeType("Expression"),
    },
    consequent: {
      validate: assertNodeType("Statement"),
    },
    alternate: {
      optional: true,
      validate: assertNodeType("Statement"),
    },
  },
});

defineType("LabeledStatement", {
  visitor: ["label", "body"],
  aliases: ["Statement"],
  fields: {
    label: {
      validate: assertNodeType("Identifier"),
    },
    body: {
      validate: assertNodeType("Statement"),
    },
  },
});

defineType("StringLiteral", {
  builder: ["value"],
  fields: {
    value: {
      validate: assertValueType("string"),
    },
  },
  aliases: ["Expression", "Pureish", "Literal", "Immutable"],
});

defineType("NumericLiteral", {
  builder: ["value"],
  deprecatedAlias: "NumberLiteral",
  fields: {
    value: {
      validate: assertValueType("number"),
    },
  },
  aliases: ["Expression", "Pureish", "Literal", "Immutable"],
});

defineType("NullLiteral", {
  aliases: ["Expression", "Pureish", "Literal", "Immutable"],
});

defineType("BooleanLiteral", {
  builder: ["value"],
  fields: {
    value: {
      validate: assertValueType("boolean"),
    },
  },
  aliases: ["Expression", "Pureish", "Literal", "Immutable"],
});

defineType("RegExpLiteral", {
  builder: ["pattern", "flags"],
  deprecatedAlias: "RegexLiteral",
  aliases: ["Expression", "Pureish", "Literal"],
  fields: {
    pattern: {
      validate: assertValueType("string"),
    },
    flags: {
      validate: chain(
        assertValueType("string"),
        Object.assign(
          function (node, key, val) {
            if (!process.env.BABEL_TYPES_8_BREAKING) return;

            const invalid = /[^gimsuy]/.exec(val);
            if (invalid) {
              throw new TypeError(`"${invalid[0]}" is not a valid RegExp flag`);
            }
          },
          { type: "string" },
        ),
      ),
      default: "",
    },
  },
});

defineType("LogicalExpression", {
  builder: ["operator", "left", "right"],
  visitor: ["left", "right"],
  aliases: ["Binary", "Expression"],
  fields: {
    operator: {
      validate: assertOneOf(...LOGICAL_OPERATORS),
    },
    left: {
      validate: assertNodeType("Expression"),
    },
    right: {
      validate: assertNodeType("Expression"),
    },
  },
});

defineType("MemberExpression", {
  builder: [
    "object",
    "property",
    "computed",
    ...(!process.env.BABEL_TYPES_8_BREAKING ? ["optional"] : []),
  ],
  visitor: ["object", "property"],
  aliases: ["Expression", "LVal"],
  fields: {
    object: {
      validate: assertNodeType("Expression"),
    },
    property: {
      validate: (function () {
        const normal = assertNodeType("Identifier", "PrivateName");
        const computed = assertNodeType("Expression");

        const validator = function (node, key, val) {
          const validator = node.computed ? computed : normal;
          validator(node, key, val);
        };
        // todo(ts): can be discriminated union by `computed` property
        validator.oneOfNodeTypes = ["Expression", "Identifier", "PrivateName"];
        return validator;
      })(),
    },
    computed: {
      default: false,
    },
    ...(!process.env.BABEL_TYPES_8_BREAKING
      ? {
          optional: {
            validate: assertOneOf(true, false),
            optional: true,
          },
        }
      : {}),
  },
});

defineType("NewExpression", { inherits: "CallExpression" });

defineType("Program", {
  // Note: We explicitly leave 'interpreter' out here because it is
  // conceptually comment-like, and Babel does not traverse comments either.
  visitor: ["directives", "body"],
  builder: ["body", "directives", "sourceType", "interpreter"],
  fields: {
    sourceFile: {
      validate: assertValueType("string"),
    },
    sourceType: {
      validate: assertOneOf("script", "module"),
      default: "script",
    },
    interpreter: {
      validate: assertNodeType("InterpreterDirective"),
      default: null,
      optional: true,
    },
    directives: {
      validate: chain(
        assertValueType("array"),
        assertEach(assertNodeType("Directive")),
      ),
      default: [],
    },
    body: {
      validate: chain(
        assertValueType("array"),
        assertEach(assertNodeType("Statement")),
      ),
    },
  },
  aliases: ["Scopable", "BlockParent", "Block"],
});

defineType("ObjectExpression", {
  visitor: ["properties"],
  aliases: ["Expression"],
  fields: {
    properties: {
      validate: chain(
        assertValueType("array"),
        assertEach(
          assertNodeType("ObjectMethod", "ObjectProperty", "SpreadElement"),
        ),
      ),
    },
  },
});

defineType("ObjectMethod", {
  builder: ["kind", "key", "params", "body", "computed", "generator", "async"],
  fields: {
    ...functionCommon,
    ...functionTypeAnnotationCommon,
    kind: {
      validate: assertOneOf("method", "get", "set"),
      ...(!process.env.BABEL_TYPES_8_BREAKING ? { default: "method" } : {}),
    },
    computed: {
      default: false,
    },
    key: {
      validate: (function () {
        const normal = assertNodeType(
          "Identifier",
          "StringLiteral",
          "NumericLiteral",
        );
        const computed = assertNodeType("Expression");

        const validator = function (node, key, val) {
          const validator = node.computed ? computed : normal;
          validator(node, key, val);
        };
        // todo(ts): can be discriminated union by `computed` property
        validator.oneOfNodeTypes = [
          "Expression",
          "Identifier",
          "StringLiteral",
          "NumericLiteral",
        ];
        return validator;
      })(),
    },
    decorators: {
      validate: chain(
        assertValueType("array"),
        assertEach(assertNodeType("Decorator")),
      ),
      optional: true,
    },
    body: {
      validate: assertNodeType("BlockStatement"),
    },
  },
  visitor: [
    "key",
    "params",
    "body",
    "decorators",
    "returnType",
    "typeParameters",
  ],
  aliases: [
    "UserWhitespacable",
    "Function",
    "Scopable",
    "BlockParent",
    "FunctionParent",
    "Method",
    "ObjectMember",
  ],
});

defineType("ObjectProperty", {
  builder: [
    "key",
    "value",
    "computed",
    "shorthand",
    ...(!process.env.BABEL_TYPES_8_BREAKING ? ["decorators"] : []),
  ],
  fields: {
    computed: {
      default: false,
    },
    key: {
      validate: (function () {
        const normal = assertNodeType(
          "Identifier",
          "StringLiteral",
          "NumericLiteral",
        );
        const computed = assertNodeType("Expression");

        const validator = function (node, key, val) {
          const validator = node.computed ? computed : normal;
          validator(node, key, val);
        };
        // todo(ts): can be discriminated union by `computed` property
        validator.oneOfNodeTypes = [
          "Expression",
          "Identifier",
          "StringLiteral",
          "NumericLiteral",
        ];
        return validator;
      })(),
    },
    value: {
      // Value may be PatternLike if this is an AssignmentProperty
      // https://github.com/babel/babylon/issues/434
      validate: assertNodeType("Expression", "PatternLike"),
    },
    shorthand: {
      validate: chain(
        assertValueType("boolean"),
        Object.assign(
          function (node, key, val) {
            if (!process.env.BABEL_TYPES_8_BREAKING) return;

            if (val && node.computed) {
              throw new TypeError(
                "Property shorthand of ObjectProperty cannot be true if computed is true",
              );
            }
          },
          { type: "boolean" },
        ),
        function (node, key, val) {
          if (!process.env.BABEL_TYPES_8_BREAKING) return;

          if (val && !is("Identifier", node.key)) {
            throw new TypeError(
              "Property shorthand of ObjectProperty cannot be true if key is not an Identifier",
            );
          }
        },
      ),
      default: false,
    },
    decorators: {
      validate: chain(
        assertValueType("array"),
        assertEach(assertNodeType("Decorator")),
      ),
      optional: true,
    },
  },
  visitor: ["key", "value", "decorators"],
  aliases: ["UserWhitespacable", "Property", "ObjectMember"],
  validate: (function () {
    const pattern = assertNodeType("Identifier", "Pattern");
    const expression = assertNodeType("Expression");

    return function (parent, key, node) {
      if (!process.env.BABEL_TYPES_8_BREAKING) return;

      const validator = is("ObjectPattern", parent) ? pattern : expression;
      validator(node, "value", node.value);
    };
  })(),
});

defineType("RestElement", {
  visitor: ["argument", "typeAnnotation"],
  builder: ["argument"],
  aliases: ["LVal", "PatternLike"],
  deprecatedAlias: "RestProperty",
  fields: {
    ...patternLikeCommon,
    argument: {
      validate: !process.env.BABEL_TYPES_8_BREAKING
        ? assertNodeType("LVal")
        : assertNodeType(
            "Identifier",
            "ArrayPattern",
            "ObjectPattern",
            "MemberExpression",
          ),
    },
    // For Flow
    optional: {
      validate: assertValueType("boolean"),
      optional: true,
    },
  },
  validate(parent, key) {
    if (!process.env.BABEL_TYPES_8_BREAKING) return;

    const match = /(\w+)\[(\d+)\]/.exec(key);
    if (!match) throw new Error("Internal Babel error: malformed key.");

    const [, listKey, index] = match;
    if (parent[listKey].length > index + 1) {
      throw new TypeError(`RestElement must be last element of ${listKey}`);
    }
  },
});

defineType("ReturnStatement", {
  visitor: ["argument"],
  aliases: ["Statement", "Terminatorless", "CompletionStatement"],
  fields: {
    argument: {
      validate: assertNodeType("Expression"),
      optional: true,
    },
  },
});

defineType("SequenceExpression", {
  visitor: ["expressions"],
  fields: {
    expressions: {
      validate: chain(
        assertValueType("array"),
        assertEach(assertNodeType("Expression")),
      ),
    },
  },
  aliases: ["Expression"],
});

defineType("ParenthesizedExpression", {
  visitor: ["expression"],
  aliases: ["Expression", "ExpressionWrapper"],
  fields: {
    expression: {
      validate: assertNodeType("Expression"),
    },
  },
});

defineType("SwitchCase", {
  visitor: ["test", "consequent"],
  fields: {
    test: {
      validate: assertNodeType("Expression"),
      optional: true,
    },
    consequent: {
      validate: chain(
        assertValueType("array"),
        assertEach(assertNodeType("Statement")),
      ),
    },
  },
});

defineType("SwitchStatement", {
  visitor: ["discriminant", "cases"],
  aliases: ["Statement", "BlockParent", "Scopable"],
  fields: {
    discriminant: {
      validate: assertNodeType("Expression"),
    },
    cases: {
      validate: chain(
        assertValueType("array"),
        assertEach(assertNodeType("SwitchCase")),
      ),
    },
  },
});

defineType("ThisExpression", {
  aliases: ["Expression"],
});

defineType("ThrowStatement", {
  visitor: ["argument"],
  aliases: ["Statement", "Terminatorless", "CompletionStatement"],
  fields: {
    argument: {
      validate: assertNodeType("Expression"),
    },
  },
});

defineType("TryStatement", {
  visitor: ["block", "handler", "finalizer"],
  aliases: ["Statement"],
  fields: {
    block: {
      validate: chain(
        assertNodeType("BlockStatement"),
        Object.assign(
          function (node) {
            if (!process.env.BABEL_TYPES_8_BREAKING) return;

            // This validator isn't put at the top level because we can run it
            // even if this node doesn't have a parent.

            if (!node.handler && !node.finalizer) {
              throw new TypeError(
                "TryStatement expects either a handler or finalizer, or both",
              );
            }
          },
          {
            oneOfNodeTypes: ["BlockStatement"],
          },
        ),
      ),
    },
    handler: {
      optional: true,
      validate: assertNodeType("CatchClause"),
    },
    finalizer: {
      optional: true,
      validate: assertNodeType("BlockStatement"),
    },
  },
});

defineType("UnaryExpression", {
  builder: ["operator", "argument", "prefix"],
  fields: {
    prefix: {
      default: true,
    },
    argument: {
      validate: assertNodeType("Expression"),
    },
    operator: {
      validate: assertOneOf(...UNARY_OPERATORS),
    },
  },
  visitor: ["argument"],
  aliases: ["UnaryLike", "Expression"],
});

defineType("UpdateExpression", {
  builder: ["operator", "argument", "prefix"],
  fields: {
    prefix: {
      default: false,
    },
    argument: {
      validate: !process.env.BABEL_TYPES_8_BREAKING
        ? assertNodeType("Expression")
        : assertNodeType("Identifier", "MemberExpression"),
    },
    operator: {
      validate: assertOneOf(...UPDATE_OPERATORS),
    },
  },
  visitor: ["argument"],
  aliases: ["Expression"],
});

defineType("VariableDeclaration", {
  builder: ["kind", "declarations"],
  visitor: ["declarations"],
  aliases: ["Statement", "Declaration"],
  fields: {
    declare: {
      validate: assertValueType("boolean"),
      optional: true,
    },
    kind: {
      validate: assertOneOf("var", "let", "const"),
    },
    declarations: {
      validate: chain(
        assertValueType("array"),
        assertEach(assertNodeType("VariableDeclarator")),
      ),
    },
  },
  validate(parent, key, node) {
    if (!process.env.BABEL_TYPES_8_BREAKING) return;

    if (!is("ForXStatement", parent, { left: node })) return;
    if (node.declarations.length !== 1) {
      throw new TypeError(
        `Exactly one VariableDeclarator is required in the VariableDeclaration of a ${parent.type}`,
      );
    }
  },
});

defineType("VariableDeclarator", {
  visitor: ["id", "init"],
  fields: {
    id: {
      validate: (function () {
        if (!process.env.BABEL_TYPES_8_BREAKING) {
          return assertNodeType("LVal");
        }

        const normal = assertNodeType(
          "Identifier",
          "ArrayPattern",
          "ObjectPattern",
        );
        const without = assertNodeType("Identifier");

        return function (node, key, val) {
          const validator = node.init ? normal : without;
          validator(node, key, val);
        };
      })(),
    },
    definite: {
      optional: true,
      validate: assertValueType("boolean"),
    },
    init: {
      optional: true,
      validate: assertNodeType("Expression"),
    },
  },
});

defineType("WhileStatement", {
  visitor: ["test", "body"],
  aliases: ["Statement", "BlockParent", "Loop", "While", "Scopable"],
  fields: {
    test: {
      validate: assertNodeType("Expression"),
    },
    body: {
      validate: assertNodeType("Statement"),
    },
  },
});

defineType("WithStatement", {
  visitor: ["object", "body"],
  aliases: ["Statement"],
  fields: {
    object: {
      validate: assertNodeType("Expression"),
    },
    body: {
      validate: assertNodeType("Statement"),
    },
  },
});

// --- ES2015 ---
defineType("AssignmentPattern", {
  visitor: ["left", "right", "decorators" /* for legacy param decorators */],
  builder: ["left", "right"],
  aliases: ["Pattern", "PatternLike", "LVal"],
  fields: {
    ...patternLikeCommon,
    left: {
      validate: assertNodeType(
        "Identifier",
        "ObjectPattern",
        "ArrayPattern",
        "MemberExpression",
      ),
    },
    right: {
      validate: assertNodeType("Expression"),
    },
    // For TypeScript
    decorators: {
      validate: chain(
        assertValueType("array"),
        assertEach(assertNodeType("Decorator")),
      ),
      optional: true,
    },
  },
});

defineType("ArrayPattern", {
  visitor: ["elements", "typeAnnotation"],
  builder: ["elements"],
  aliases: ["Pattern", "PatternLike", "LVal"],
  fields: {
    ...patternLikeCommon,
    elements: {
      validate: chain(
        assertValueType("array"),
        assertEach(assertNodeOrValueType("null", "PatternLike")),
      ),
    },
    // For TypeScript
    decorators: {
      validate: chain(
        assertValueType("array"),
        assertEach(assertNodeType("Decorator")),
      ),
      optional: true,
    },
    optional: {
      validate: assertValueType("boolean"),
      optional: true,
    },
  },
});

defineType("ArrowFunctionExpression", {
  builder: ["params", "body", "async"],
  visitor: ["params", "body", "returnType", "typeParameters"],
  aliases: [
    "Scopable",
    "Function",
    "BlockParent",
    "FunctionParent",
    "Expression",
    "Pureish",
  ],
  fields: {
    ...functionCommon,
    ...functionTypeAnnotationCommon,
    expression: {
      // https://github.com/babel/babylon/issues/505
      validate: assertValueType("boolean"),
    },
    body: {
      validate: assertNodeType("BlockStatement", "Expression"),
    },
  },
});

defineType("ClassBody", {
  visitor: ["body"],
  fields: {
    body: {
      validate: chain(
        assertValueType("array"),
        assertEach(
          assertNodeType(
            "ClassMethod",
            "ClassPrivateMethod",
            "ClassProperty",
            "ClassPrivateProperty",
            "ClassAccessorProperty",
            "TSDeclareMethod",
            "TSIndexSignature",
            "StaticBlock",
          ),
        ),
      ),
    },
  },
});

defineType("ClassExpression", {
  builder: ["id", "superClass", "body", "decorators"],
  visitor: [
    "id",
    "body",
    "superClass",
    "mixins",
    "typeParameters",
    "superTypeParameters",
    "implements",
    "decorators",
  ],
  aliases: ["Scopable", "Class", "Expression"],
  fields: {
    id: {
      validate: assertNodeType("Identifier"),
      // In declarations, this is missing if this is the
      // child of an ExportDefaultDeclaration.
      optional: true,
    },
    typeParameters: {
      validate: process.env.BABEL_8_BREAKING
        ? assertNodeType(
            "TypeParameterDeclaration",
            "TSTypeParameterDeclaration",
          )
        : assertNodeType(
            "TypeParameterDeclaration",
            "TSTypeParameterDeclaration",
            "Noop",
          ),
      optional: true,
    },
    body: {
      validate: assertNodeType("ClassBody"),
    },
    superClass: {
      optional: true,
      validate: assertNodeType("Expression"),
    },
    superTypeParameters: {
      validate: assertNodeType(
        "TypeParameterInstantiation",
        "TSTypeParameterInstantiation",
      ),
      optional: true,
    },
    implements: {
      validate: chain(
        assertValueType("array"),
        assertEach(
          assertNodeType("TSExpressionWithTypeArguments", "ClassImplements"),
        ),
      ),
      optional: true,
    },
    decorators: {
      validate: chain(
        assertValueType("array"),
        assertEach(assertNodeType("Decorator")),
      ),
      optional: true,
    },
    mixins: {
      validate: assertNodeType("InterfaceExtends"),
      optional: true,
    },
  },
});

defineType("ClassDeclaration", {
  inherits: "ClassExpression",
  aliases: ["Scopable", "Class", "Statement", "Declaration"],
  fields: {
    id: {
      validate: assertNodeType("Identifier"),
    },
    typeParameters: {
      validate: process.env.BABEL_8_BREAKING
        ? assertNodeType(
            "TypeParameterDeclaration",
            "TSTypeParameterDeclaration",
          )
        : assertNodeType(
            "TypeParameterDeclaration",
            "TSTypeParameterDeclaration",
            "Noop",
          ),
      optional: true,
    },
    body: {
      validate: assertNodeType("ClassBody"),
    },
    superClass: {
      optional: true,
      validate: assertNodeType("Expression"),
    },
    superTypeParameters: {
      validate: assertNodeType(
        "TypeParameterInstantiation",
        "TSTypeParameterInstantiation",
      ),
      optional: true,
    },
    implements: {
      validate: chain(
        assertValueType("array"),
        assertEach(
          assertNodeType("TSExpressionWithTypeArguments", "ClassImplements"),
        ),
      ),
      optional: true,
    },
    decorators: {
      validate: chain(
        assertValueType("array"),
        assertEach(assertNodeType("Decorator")),
      ),
      optional: true,
    },
    mixins: {
      validate: assertNodeType("InterfaceExtends"),
      optional: true,
    },
    declare: {
      validate: assertValueType("boolean"),
      optional: true,
    },
    abstract: {
      validate: assertValueType("boolean"),
      optional: true,
    },
  },
  validate: (function () {
    const identifier = assertNodeType("Identifier");

    return function (parent, key, node) {
      if (!process.env.BABEL_TYPES_8_BREAKING) return;

      if (!is("ExportDefaultDeclaration", parent)) {
        identifier(node, "id", node.id);
      }
    };
  })(),
});

defineType("ExportAllDeclaration", {
  visitor: ["source"],
  aliases: [
    "Statement",
    "Declaration",
    "ModuleDeclaration",
    "ExportDeclaration",
  ],
  fields: {
    source: {
      validate: assertNodeType("StringLiteral"),
    },
    exportKind: validateOptional(assertOneOf("type", "value")),
    assertions: {
      optional: true,
      validate: chain(
        assertValueType("array"),
        assertEach(assertNodeType("ImportAttribute")),
      ),
    },
  },
});

defineType("ExportDefaultDeclaration", {
  visitor: ["declaration"],
  aliases: [
    "Statement",
    "Declaration",
    "ModuleDeclaration",
    "ExportDeclaration",
  ],
  fields: {
    declaration: {
      validate: assertNodeType(
        "FunctionDeclaration",
        "TSDeclareFunction",
        "ClassDeclaration",
        "Expression",
      ),
    },
    exportKind: validateOptional(assertOneOf("value")),
  },
});

defineType("ExportNamedDeclaration", {
  visitor: ["declaration", "specifiers", "source"],
  aliases: [
    "Statement",
    "Declaration",
    "ModuleDeclaration",
    "ExportDeclaration",
  ],
  fields: {
    declaration: {
      optional: true,
      validate: chain(
        assertNodeType("Declaration"),
        Object.assign(
          function (node, key, val) {
            if (!process.env.BABEL_TYPES_8_BREAKING) return;

            // This validator isn't put at the top level because we can run it
            // even if this node doesn't have a parent.

            if (val && node.specifiers.length) {
              throw new TypeError(
                "Only declaration or specifiers is allowed on ExportNamedDeclaration",
              );
            }
          },
          { oneOfNodeTypes: ["Declaration"] },
        ),
        function (node, key, val) {
          if (!process.env.BABEL_TYPES_8_BREAKING) return;

          // This validator isn't put at the top level because we can run it
          // even if this node doesn't have a parent.

          if (val && node.source) {
            throw new TypeError("Cannot export a declaration from a source");
          }
        },
      ),
    },
    assertions: {
      optional: true,
      validate: chain(
        assertValueType("array"),
        assertEach(assertNodeType("ImportAttribute")),
      ),
    },
    specifiers: {
      default: [],
      validate: chain(
        assertValueType("array"),
        assertEach(
          (function () {
            const sourced = assertNodeType(
              "ExportSpecifier",
              "ExportDefaultSpecifier",
              "ExportNamespaceSpecifier",
            );
            const sourceless = assertNodeType("ExportSpecifier");

            if (!process.env.BABEL_TYPES_8_BREAKING) return sourced;

            return function (node, key, val) {
              const validator = node.source ? sourced : sourceless;
              validator(node, key, val);
            };
          })(),
        ),
      ),
    },
    source: {
      validate: assertNodeType("StringLiteral"),
      optional: true,
    },
    exportKind: validateOptional(assertOneOf("type", "value")),
  },
});

defineType("ExportSpecifier", {
  visitor: ["local", "exported"],
  aliases: ["ModuleSpecifier"],
  fields: {
    local: {
      validate: assertNodeType("Identifier"),
    },
    exported: {
      validate: assertNodeType("Identifier", "StringLiteral"),
    },
    exportKind: {
      // And TypeScript's "export { type foo } from"
      validate: assertOneOf("type", "value"),
      optional: true,
    },
  },
});

defineType("ForOfStatement", {
  visitor: ["left", "right", "body"],
  builder: ["left", "right", "body", "await"],
  aliases: [
    "Scopable",
    "Statement",
    "For",
    "BlockParent",
    "Loop",
    "ForXStatement",
  ],
  fields: {
    left: {
      validate: (function () {
        if (!process.env.BABEL_TYPES_8_BREAKING) {
          return assertNodeType("VariableDeclaration", "LVal");
        }

        const declaration = assertNodeType("VariableDeclaration");
        const lval = assertNodeType(
          "Identifier",
          "MemberExpression",
          "ArrayPattern",
          "ObjectPattern",
        );

        return function (node, key, val) {
          if (is("VariableDeclaration", val)) {
            declaration(node, key, val);
          } else {
            lval(node, key, val);
          }
        };
      })(),
    },
    right: {
      validate: assertNodeType("Expression"),
    },
    body: {
      validate: assertNodeType("Statement"),
    },
    await: {
      default: false,
    },
  },
});

defineType("ImportDeclaration", {
  visitor: ["specifiers", "source"],
  aliases: ["Statement", "Declaration", "ModuleDeclaration"],
  fields: {
    assertions: {
      optional: true,
      validate: chain(
        assertValueType("array"),
        assertEach(assertNodeType("ImportAttribute")),
      ),
    },
    specifiers: {
      validate: chain(
        assertValueType("array"),
        assertEach(
          assertNodeType(
            "ImportSpecifier",
            "ImportDefaultSpecifier",
            "ImportNamespaceSpecifier",
          ),
        ),
      ),
    },
    source: {
      validate: assertNodeType("StringLiteral"),
    },
    importKind: {
      // Handle TypeScript/Flowtype's extension "import type foo from"
      // TypeScript doesn't support typeof
      validate: assertOneOf("type", "typeof", "value"),
      optional: true,
    },
  },
});

defineType("ImportDefaultSpecifier", {
  visitor: ["local"],
  aliases: ["ModuleSpecifier"],
  fields: {
    local: {
      validate: assertNodeType("Identifier"),
    },
  },
});

defineType("ImportNamespaceSpecifier", {
  visitor: ["local"],
  aliases: ["ModuleSpecifier"],
  fields: {
    local: {
      validate: assertNodeType("Identifier"),
    },
  },
});

defineType("ImportSpecifier", {
  visitor: ["local", "imported"],
  aliases: ["ModuleSpecifier"],
  fields: {
    local: {
      validate: assertNodeType("Identifier"),
    },
    imported: {
      validate: assertNodeType("Identifier", "StringLiteral"),
    },
    importKind: {
      // Handle Flowtype's extension "import {typeof foo} from"
      // And TypeScript's "import { type foo } from"
      validate: assertOneOf("type", "typeof", "value"),
      optional: true,
    },
  },
});

defineType("MetaProperty", {
  visitor: ["meta", "property"],
  aliases: ["Expression"],
  fields: {
    meta: {
      validate: chain(
        assertNodeType("Identifier"),
        Object.assign(
          function (node, key, val) {
            if (!process.env.BABEL_TYPES_8_BREAKING) return;

            let property;
            switch (val.name) {
              case "function":
                property = "sent";
                break;
              case "new":
                property = "target";
                break;
              case "import":
                property = "meta";
                break;
            }
            if (!is("Identifier", node.property, { name: property })) {
              throw new TypeError("Unrecognised MetaProperty");
            }
          },
          { oneOfNodeTypes: ["Identifier"] },
        ),
      ),
    },
    property: {
      validate: assertNodeType("Identifier"),
    },
  },
});

export const classMethodOrPropertyCommon = {
  abstract: {
    validate: assertValueType("boolean"),
    optional: true,
  },
  accessibility: {
    validate: assertOneOf("public", "private", "protected"),
    optional: true,
  },
  static: {
    default: false,
  },
  override: {
    default: false,
  },
  computed: {
    default: false,
  },
  optional: {
    validate: assertValueType("boolean"),
    optional: true,
  },
  key: {
    validate: chain(
      (function () {
        const normal = assertNodeType(
          "Identifier",
          "StringLiteral",
          "NumericLiteral",
        );
        const computed = assertNodeType("Expression");

        return function (node: any, key: string, val: any) {
          const validator = node.computed ? computed : normal;
          validator(node, key, val);
        };
      })(),
      assertNodeType(
        "Identifier",
        "StringLiteral",
        "NumericLiteral",
        "Expression",
      ),
    ),
  },
};

export const classMethodOrDeclareMethodCommon = {
  ...functionCommon,
  ...classMethodOrPropertyCommon,
  params: {
    validate: chain(
      assertValueType("array"),
      assertEach(
        assertNodeType(
          "Identifier",
          "Pattern",
          "RestElement",
          "TSParameterProperty",
        ),
      ),
    ),
  },
  kind: {
    validate: assertOneOf("get", "set", "method", "constructor"),
    default: "method",
  },
  access: {
    validate: chain(
      assertValueType("string"),
      assertOneOf("public", "private", "protected"),
    ),
    optional: true,
  },
  decorators: {
    validate: chain(
      assertValueType("array"),
      assertEach(assertNodeType("Decorator")),
    ),
    optional: true,
  },
};

defineType("ClassMethod", {
  aliases: ["Function", "Scopable", "BlockParent", "FunctionParent", "Method"],
  builder: [
    "kind",
    "key",
    "params",
    "body",
    "computed",
    "static",
    "generator",
    "async",
  ],
  visitor: [
    "key",
    "params",
    "body",
    "decorators",
    "returnType",
    "typeParameters",
  ],
  fields: {
    ...classMethodOrDeclareMethodCommon,
    ...functionTypeAnnotationCommon,
    body: {
      validate: assertNodeType("BlockStatement"),
    },
  },
});

defineType("ObjectPattern", {
  visitor: [
    "properties",
    "typeAnnotation",
    "decorators" /* for legacy param decorators */,
  ],
  builder: ["properties"],
  aliases: ["Pattern", "PatternLike", "LVal"],
  fields: {
    ...patternLikeCommon,
    properties: {
      validate: chain(
        assertValueType("array"),
        assertEach(assertNodeType("RestElement", "ObjectProperty")),
      ),
    },
  },
});

defineType("SpreadElement", {
  visitor: ["argument"],
  aliases: ["UnaryLike"],
  deprecatedAlias: "SpreadProperty",
  fields: {
    argument: {
      validate: assertNodeType("Expression"),
    },
  },
});

defineType("Super", {
  aliases: ["Expression"],
});

defineType("TaggedTemplateExpression", {
  visitor: ["tag", "quasi", "typeParameters"],
  builder: ["tag", "quasi"],
  aliases: ["Expression"],
  fields: {
    tag: {
      validate: assertNodeType("Expression"),
    },
    quasi: {
      validate: assertNodeType("TemplateLiteral"),
    },
    typeParameters: {
      validate: assertNodeType(
        "TypeParameterInstantiation",
        "TSTypeParameterInstantiation",
      ),
      optional: true,
    },
  },
});

defineType("TemplateElement", {
  builder: ["value", "tail"],
  fields: {
    value: {
      validate: assertShape({
        raw: {
          validate: assertValueType("string"),
        },
        cooked: {
          validate: assertValueType("string"),
          optional: true,
        },
      }),
    },
    tail: {
      default: false,
    },
  },
});

defineType("TemplateLiteral", {
  visitor: ["quasis", "expressions"],
  aliases: ["Expression", "Literal"],
  fields: {
    quasis: {
      validate: chain(
        assertValueType("array"),
        assertEach(assertNodeType("TemplateElement")),
      ),
    },
    expressions: {
      validate: chain(
        assertValueType("array"),
        assertEach(
          assertNodeType(
            "Expression",
            // For TypeScript template literal types
            "TSType",
          ),
        ),
        function (node, key, val) {
          if (node.quasis.length !== val.length + 1) {
            throw new TypeError(
              `Number of ${
                node.type
              } quasis should be exactly one more than the number of expressions.\nExpected ${
                val.length + 1
              } quasis but got ${node.quasis.length}`,
            );
          }
        },
      ),
    },
  },
});

defineType("YieldExpression", {
  builder: ["argument", "delegate"],
  visitor: ["argument"],
  aliases: ["Expression", "Terminatorless"],
  fields: {
    delegate: {
      validate: chain(
        assertValueType("boolean"),
        Object.assign(
          function (node, key, val) {
            if (!process.env.BABEL_TYPES_8_BREAKING) return;

            if (val && !node.argument) {
              throw new TypeError(
                "Property delegate of YieldExpression cannot be true if there is no argument",
              );
            }
          },
          { type: "boolean" },
        ),
      ),
      default: false,
    },
    argument: {
      optional: true,
      validate: assertNodeType("Expression"),
    },
  },
});

// --- ES2017 ---
defineType("AwaitExpression", {
  builder: ["argument"],
  visitor: ["argument"],
  aliases: ["Expression", "Terminatorless"],
  fields: {
    argument: {
      validate: assertNodeType("Expression"),
    },
  },
});

// --- ES2019 ---
defineType("Import", {
  aliases: ["Expression"],
});

// --- ES2020 ---
defineType("BigIntLiteral", {
  builder: ["value"],
  fields: {
    value: {
      validate: assertValueType("string"),
    },
  },
  aliases: ["Expression", "Pureish", "Literal", "Immutable"],
});

defineType("ExportNamespaceSpecifier", {
  visitor: ["exported"],
  aliases: ["ModuleSpecifier"],
  fields: {
    exported: {
      validate: assertNodeType("Identifier"),
    },
  },
});

defineType("OptionalMemberExpression", {
  builder: ["object", "property", "computed", "optional"],
  visitor: ["object", "property"],
  aliases: ["Expression"],
  fields: {
    object: {
      validate: assertNodeType("Expression"),
    },
    property: {
      validate: (function () {
        const normal = assertNodeType("Identifier");
        const computed = assertNodeType("Expression");

        const validator = function (node, key, val) {
          const validator = node.computed ? computed : normal;
          validator(node, key, val);
        };
        // todo(ts): can be discriminated union by `computed` property
        validator.oneOfNodeTypes = ["Expression", "Identifier"];
        return validator;
      })(),
    },
    computed: {
      default: false,
    },
    optional: {
      validate: !process.env.BABEL_TYPES_8_BREAKING
        ? assertValueType("boolean")
        : chain(assertValueType("boolean"), assertOptionalChainStart()),
    },
  },
});

defineType("OptionalCallExpression", {
  visitor: ["callee", "arguments", "typeParameters", "typeArguments"],
  builder: ["callee", "arguments", "optional"],
  aliases: ["Expression"],
  fields: {
    callee: {
      validate: assertNodeType("Expression"),
    },
    arguments: {
      validate: chain(
        assertValueType("array"),
        assertEach(
          assertNodeType(
            "Expression",
            "SpreadElement",
            "JSXNamespacedName",
            "ArgumentPlaceholder",
          ),
        ),
      ),
    },
    optional: {
      validate: !process.env.BABEL_TYPES_8_BREAKING
        ? assertValueType("boolean")
        : chain(assertValueType("boolean"), assertOptionalChainStart()),
    },
    typeArguments: {
      validate: assertNodeType("TypeParameterInstantiation"),
      optional: true,
    },
    typeParameters: {
      validate: assertNodeType("TSTypeParameterInstantiation"),
      optional: true,
    },
  },
});

// --- ES2022 ---
defineType("ClassProperty", {
  visitor: ["key", "value", "typeAnnotation", "decorators"],
  builder: [
    "key",
    "value",
    "typeAnnotation",
    "decorators",
    "computed",
    "static",
  ],
  aliases: ["Property"],
  fields: {
    ...classMethodOrPropertyCommon,
    value: {
      validate: assertNodeType("Expression"),
      optional: true,
    },
    definite: {
      validate: assertValueType("boolean"),
      optional: true,
    },
    typeAnnotation: {
      validate: process.env.BABEL_8_BREAKING
        ? assertNodeType("TypeAnnotation", "TSTypeAnnotation")
        : assertNodeType("TypeAnnotation", "TSTypeAnnotation", "Noop"),
      optional: true,
    },
    decorators: {
      validate: chain(
        assertValueType("array"),
        assertEach(assertNodeType("Decorator")),
      ),
      optional: true,
    },
    readonly: {
      validate: assertValueType("boolean"),
      optional: true,
    },
    declare: {
      validate: assertValueType("boolean"),
      optional: true,
    },
    variance: {
      validate: assertNodeType("Variance"),
      optional: true,
    },
  },
});

defineType("ClassAccessorProperty", {
  visitor: ["key", "value", "typeAnnotation", "decorators"],
  builder: [
    "key",
    "value",
    "typeAnnotation",
    "decorators",
    "computed",
    "static",
  ],
  aliases: ["Property", "Accessor"],
  fields: {
    ...classMethodOrPropertyCommon,
    key: {
      validate: chain(
        (function () {
          const normal = assertNodeType(
            "Identifier",
            "StringLiteral",
            "NumericLiteral",
            "PrivateName",
          );
          const computed = assertNodeType("Expression");

          return function (node: any, key: string, val: any) {
            const validator = node.computed ? computed : normal;
            validator(node, key, val);
          };
        })(),
        assertNodeType(
          "Identifier",
          "StringLiteral",
          "NumericLiteral",
          "Expression",
          "PrivateName",
        ),
      ),
    },
    value: {
      validate: assertNodeType("Expression"),
      optional: true,
    },
    definite: {
      validate: assertValueType("boolean"),
      optional: true,
    },
    typeAnnotation: {
      validate: process.env.BABEL_8_BREAKING
        ? assertNodeType("TypeAnnotation", "TSTypeAnnotation")
        : assertNodeType("TypeAnnotation", "TSTypeAnnotation", "Noop"),
      optional: true,
    },
    decorators: {
      validate: chain(
        assertValueType("array"),
        assertEach(assertNodeType("Decorator")),
      ),
      optional: true,
    },
    readonly: {
      validate: assertValueType("boolean"),
      optional: true,
    },
    declare: {
      validate: assertValueType("boolean"),
      optional: true,
    },
    variance: {
      validate: assertNodeType("Variance"),
      optional: true,
    },
  },
});

defineType("ClassPrivateProperty", {
  visitor: ["key", "value", "decorators", "typeAnnotation"],
  builder: ["key", "value", "decorators", "static"],
  aliases: ["Property", "Private"],
  fields: {
    key: {
      validate: assertNodeType("PrivateName"),
    },
    value: {
      validate: assertNodeType("Expression"),
      optional: true,
    },
    typeAnnotation: {
      validate: process.env.BABEL_8_BREAKING
        ? assertNodeType("TypeAnnotation", "TSTypeAnnotation")
        : assertNodeType("TypeAnnotation", "TSTypeAnnotation", "Noop"),
      optional: true,
    },
    decorators: {
      validate: chain(
        assertValueType("array"),
        assertEach(assertNodeType("Decorator")),
      ),
      optional: true,
    },
    readonly: {
      validate: assertValueType("boolean"),
      optional: true,
    },
    definite: {
      validate: assertValueType("boolean"),
      optional: true,
    },
    variance: {
      validate: assertNodeType("Variance"),
      optional: true,
    },
  },
});

defineType("ClassPrivateMethod", {
  builder: ["kind", "key", "params", "body", "static"],
  visitor: [
    "key",
    "params",
    "body",
    "decorators",
    "returnType",
    "typeParameters",
  ],
  aliases: [
    "Function",
    "Scopable",
    "BlockParent",
    "FunctionParent",
    "Method",
    "Private",
  ],
  fields: {
    ...classMethodOrDeclareMethodCommon,
    ...functionTypeAnnotationCommon,
    key: {
      validate: assertNodeType("PrivateName"),
    },
    body: {
      validate: assertNodeType("BlockStatement"),
    },
  },
});

defineType("PrivateName", {
  visitor: ["id"],
  aliases: ["Private"],
  fields: {
    id: {
      validate: assertNodeType("Identifier"),
    },
  },
});

defineType("StaticBlock", {
  visitor: ["body"],
  fields: {
    body: {
      validate: chain(
        assertValueType("array"),
        assertEach(assertNodeType("Statement")),
      ),
    },
  },
  aliases: ["Scopable", "BlockParent", "FunctionParent"],
});


text:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/selimg_white"
    tools:context=".activity.PhotoActivity">

    <io.fotoapparat.view.CameraView
        android:id="@+id/camera_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <ProgressBar
        android:id="@+id/progress"
        style="@style/Widget.AppCompat.ProgressBar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:progressDrawable="@color/selimg_white"
        android:theme="@style/CircularProgress"
        android:visibility="gone" />

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/btn_shot"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_margin="32dp"
        android:src="@drawable/ic_camera_iris"
        app:backgroundTint="@color/selimg_white"
        app:fabSize="normal" />

</RelativeLayout>

redpajama_set_name: RedPajamaGithub


text:
@interface AresFstLogSession : JSONModel

@property (strong , nonatomic) NSString *userId;
@property (strong , nonatomic) NSString *orgId;
@property (strong , nonatomic) NSString *orgName;

@end


text:
<!doctype html>
<html lang="en" ng-app="myApp">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta name="description" content="cophea 最好用的会员系统">
  <meta name="author" content="cophea">
  <title>Cophea</title>
  <link href="http://cdn.bootcss.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet">
  <link href="http://cdn.bootcss.com/font-awesome/4.0.3/css/font-awesome.min.css" rel="stylesheet">
  <link rel="stylesheet" href="css/app.css"/>
  <link rel="apple-touch-icon-precomposed" sizes="144x144" href="../docs-assets/ico/apple-touch-icon-144-precomposed.png">
  <link rel="shortcut icon" href="../docs-assets/ico/favicon.png">
</head>
<body ng-controller="globleCtrl">
  <div ng-view></div>
  <script src="http://cdn.bootcss.com/jquery/2.0.3/jquery.min.js"></script>
  <script src="http://cdn.bootcss.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
  <script src="lib/angular/angular.js"></script>
  <script src="lib/angular/angular-route.js"></script>
  <script src="js/app.js"></script>
  <script src="js/services.js"></script>
  <script src="js/controllers.js"></script>
  <script src="js/filters.js"></script>
  <script src="js/directives.js"></script>
</body>
</html>


text:
package controllers

import config.ApplicationConfig
import connectors._
import controllers.actions.SuccessfulAuthAction
import generators.submission.SubscriptionResponseGenerator
import generators.{AmlsReferenceNumberGenerator, PaymentGenerator}
import models.businesscustomer.{Address, ReviewDetails}
import models.businessdetails.{BusinessDetails, PreviouslyRegisteredNo}
import models.businessmatching.BusinessMatching
import models.payments._
import models.registrationdetails.RegistrationDetails
import models.status._
import models.{status => _, _}
import org.joda.time.DateTime
import org.mockito.Matchers.{eq => eqTo, _}
import org.mockito.Mockito._
import play.api.test.Helpers._
import services._
import uk.gov.hmrc.http.HttpResponse
import uk.gov.hmrc.http.cache.client.CacheMap
import utils.AmlsSpec

import scala.concurrent.Future

// scalastyle:off magic.number
class RetryPaymentControllerSpec extends AmlsSpec
  with AmlsReferenceNumberGenerator
  with PaymentGenerator
  with SubscriptionResponseGenerator {

  trait Fixture {
    self =>
    val baseUrl = "http://localhost"
    val request = addToken(authRequest)
    val controller = new RetryPaymentController(
      SuccessfulAuthAction,
      statusService = mock[StatusService],
      dataCacheConnector = mock[DataCacheConnector],
      amlsConnector = mock[AmlsConnector],
      paymentsService = mock[PaymentsService],
      ds = commonDependencies,
      cc = mockMcc)

    val response = subscriptionResponseGen(hasFees = true).sample.get

    protected val mockCacheMap = mock[CacheMap]
    val companyNameFromCache = "My Test Company Name From Cache"
    val companyNameFromRegistration = "My Test Company Name From Registration"

    setupBusinessMatching(companyNameFromCache)

    when {
      controller.amlsConnector.refreshPaymentStatus(any(), any())(any(), any())
    } thenReturn Future.successful(paymentStatusResultGen.sample.get.copy(currentStatus = PaymentStatuses.Successful))

    when {
      controller.amlsConnector.getPaymentByPaymentReference(any(), any())(any(), any())
    } thenReturn Future.successful(paymentGen.sample)

    when {
      controller.amlsConnector.savePayment(any(), any(), any(), any())(any(), any())
    } thenReturn Future.successful(HttpResponse(CREATED, ""))

    when {
      controller.amlsConnector.registrationDetails(any(), any())(any(), any())
    } thenReturn Future.successful(RegistrationDetails(companyNameFromRegistration, isIndividual = false))

    when {
      controller.dataCacheConnector.fetch[SubmissionRequestStatus](any(), eqTo(SubmissionRequestStatus.key))(any(), any())
    } thenReturn Future.successful(Some(SubmissionRequestStatus(true)))

    def feeResponse(responseType: ResponseType) = FeeResponse(
      responseType,
      amlsRegistrationNumber,
      100,
      None,
      None,
      0,
      200,
      Some(paymentReferenceNumber),
      Some(115),
      DateTime.now
    )

    val businessDetails = BusinessDetails(previouslyRegistered = Some(PreviouslyRegisteredNo))
    when {
      controller.dataCacheConnector.fetch[BusinessDetails](any(), eqTo(BusinessDetails.key))(any(), any())
    } thenReturn Future.successful(Some(businessDetails))

    val applicationConfig = app.injector.instanceOf[ApplicationConfig]

    def paymentsReturnLocation(ref: String) = ReturnLocation(controllers.routes.PaymentConfirmationController.paymentConfirmation(ref))(applicationConfig)

    def setupBusinessMatching(companyName: String) = {

      val model = BusinessMatching(
        reviewDetails = Some(ReviewDetails(companyName, None, mock[Address], ""))
      )

      when {
        controller.dataCacheConnector.fetch[BusinessMatching](any(), eqTo(BusinessMatching.key))(any(), any())
      } thenReturn Future.successful(Some(model))

    }

    def setupStatus(status: SubmissionStatus): Unit = {

      when {
        controller.statusService.getStatus(any[Option[String]](), any(), any())(any(), any())
      } thenReturn Future.successful(status)

      val statusResponse = mock[ReadStatusResponse]
      when(statusResponse.safeId) thenReturn safeIdGen.sample

      when {
        controller.statusService.getDetailedStatus(any[Option[String]](), any(), any())(any(), any())
      } thenReturn Future.successful((status, Some(statusResponse)))
    }
  }

  "ConfirmationController" must {

    "allow a payment to be retried" in new Fixture {
      val amountInPence = 8765
      val postData = "paymentRef" -> paymentReferenceNumber
      val payment = paymentGen.sample.get
      val paymentResponse = paymentResponseGen.sample.get

      when {
        controller.amlsConnector.getPaymentByPaymentReference(eqTo(paymentReferenceNumber), any())(any(), any())
      } thenReturn Future.successful(Some(payment.copy(reference = paymentReferenceNumber, amountInPence = amountInPence)))

      when {
        controller.paymentsService.paymentsUrlOrDefault(any(), any(), any(), any(), any(), any())(any(), any(), any())
      } thenReturn Future.successful(paymentResponse.nextUrl)

      val result = controller.retryPayment()(requestWithUrlEncodedBody(postData))

      val expectedUrl: Option[String] = Some(paymentResponse.nextUrl.value)
      val actualUrl: Option[String] = redirectLocation(result)

      status(result) mustBe SEE_OTHER
      actualUrl mustEqual expectedUrl
    }

    "fail if a payment cannot be retried" in new Fixture {

      val postData = "paymentRef" -> paymentReferenceNumber

      when {
        controller.amlsConnector.getPaymentByPaymentReference(eqTo(paymentReferenceNumber), any())(any(), any())
      } thenReturn Future.successful(None)

      val result = controller.retryPayment()(requestWithUrlEncodedBody(postData))

      status(result) mustBe INTERNAL_SERVER_ERROR
    }
  }
}


text:
// {{{ Copyright notice

// }}}

#include "StackLevelClientData.h"

// {{{ StackLevelClientData::StackLevelClientData(DBGp::StackLevel *level)
StackLevelClientData::StackLevelClientData(DBGp::StackLevel *level) : wxClientData() {
	this->level = new DBGp::StackLevel(*level);
}
// }}}
// {{{ StackLevelClientData::~StackLevelClientData()
StackLevelClientData::~StackLevelClientData() {
	delete level;
}
// }}}

// {{{ DBGp::StackLevel *StackLevelClientData::GetStackLevel()
DBGp::StackLevel *StackLevelClientData::GetStackLevel() {
	return level;
}
// }}}

// vim:set fdm=marker ts=8 sw=8 noet cin:


text:
from __future__ import absolute_import
from __future__ import print_function
from traits.api import Button, Event, Property, \
    Any, Bool, Enum, Instance, cached_property, String
from traitsui.api import View, Item, ButtonEditor, HGroup, VGroup
# ============= standard library imports ========================
import yaml
import os
# ============= local library imports  ==========================
from pychron.experiment.utilities.position_regex import TRANSECT_REGEX
from pychron.paths import paths
from pychron.managers.manager import Manager
from pychron.lasers.points.maker import BaseMaker, LineMaker, PointMaker, \
    PolygonMaker, TransectMaker, GridMaker
import six
# from pychron.canvas.scene_viewer import LaserMineViewer
# from pychron.regex import TRANSECT_REGEX
maker_dict = dict(polygon=PolygonMaker,
                  point=PointMaker,
                  line=LineMaker,
                  transect=TransectMaker,
                  grid=GridMaker)

class PointsProgrammer(Manager):
    maker = Property(Instance(BaseMaker), depends_on='mode')

    # ===============================================================================
    #
    # ===============================================================================

    stage_manager = Any
    canvas = Any
    program_points = Event
    show_hide = Event
    is_programming = Bool
    is_visible = Bool

    program_points_label = Property(depends_on='is_programming')
    show_hide_label = Property(depends_on='is_visible')

    #    use_simple_render = Bool(False)
    #    spot_color = Color('yellow')
    #    spot_size = Int
    load_points = Button
    save_points = Button

    stage_map_klass = Any
    mode = Enum('polygon', 'point', 'line', 'transect', 'grid')

    point = Any
    line = Any
    polygon = Any

    position_entry = String(enter_set=True, auto_set=False)

    #    show_scene_viewer = Button('Scene Viewer')

    _path = None

    def accept_point(self):
        if self.is_programming:
            self.maker._accept_point_fired()

    def load_stage_map(self, sm):

        canvas = self.canvas
        canvas.clear_all()

        ptargs = dict(radius=0.05,
                      vline_length=0.1, hline_length=0.1)
        if hasattr(sm, 'lines'):
            self._load_lines(sm.lines, ptargs)
        if hasattr(sm, 'points'):
            self._load_points(sm.points, ptargs)
        if hasattr(sm, 'polygons'):
            self._load_polygons(sm.polygons, ptargs)
        if hasattr(sm, 'transects'):
            self._load_transects(sm.transects, ptargs)

        self.is_visible = True

        canvas.show_all()
        canvas.invalidate_and_redraw()

    def _set_entry(self, v):
        canvas = self.canvas
        v = v.lower()
        if v.startswith('l'):
            self.line = canvas.get_line(v)
        elif v.startswith('t'):
            if TRANSECT_REGEX.match(v):
                point = canvas.get_transect_point(v)
                if point:
                    self.point = point

        elif v.startswith('r'):
            self.polygon = canvas.get_polygon(v[1:])
        elif v.startswith('p'):
            self.point = canvas.get_point(v[1:])
        else:
            try:
                int(v)
                self.point = canvas.get_point(v)
                print(v, self.point)
            except ValueError as e:
                print('exception', e)

                # ===============================================================================
                # handlers
                # ===============================================================================

    def _position_entry_changed(self):
        self._set_entry(self.position_entry)

    def _show_hide_fired(self):
        canvas = self.canvas
        if self.is_visible:
            canvas.hide_all()
        else:
            canvas.show_all()

        canvas.request_redraw()
        self.is_visible = not self.is_visible

    def _program_points_fired(self):
        if self.is_programming:
            self.canvas.hide_all()
            self.is_programming = False
            self.is_visible = False
            self.maker.deinitialize()
        else:
            self.canvas.show_all()
            self.is_programming = True
            self.is_visible = True
            self.maker.initialize()

        self.canvas.request_redraw()

    def _load_points_fired(self):
        self._load()

    def _save_points_fired(self):
        self._dump()

    # ===============================================================================
    # persistence
    # ===============================================================================
    def _load(self, p=None):
        if p is None:
            p = self.open_file_dialog(default_directory=paths.user_points_dir)
        if p:
            sm = self.stage_map_klass(file_path=p)
            self.load_stage_map(sm)

            self.is_programming = True
            self._path = p

    def _dump(self, p=None):
        if p is None:
            p = self.save_file_dialog(default_directory=paths.user_points_dir)

        if p:
            if not p.endswith('.yaml'):
                p = '{}.yaml'.format(p)
            d = self.maker.save()
            with open(p, 'w') as f:
                f.write(yaml.dump(d, default_flow_style=False))

            self.stage_manager.refresh_stage_map_names()
            head, _tail = os.path.splitext(os.path.basename(p))

            self.stage_manager.save_calibration(head)

            self.stage_manager.set_stage_map(head)

            # ===============================================================================
            # property get/set
            # ===============================================================================

    @cached_property
    def _get_maker(self):
        if self.mode in maker_dict:
            maker = maker_dict[self.mode](canvas=self.canvas,
                                          stage_manager=self.stage_manager)
            return maker

    def _load_lines(self, lines, ptargs):
        canvas = self.canvas
        point_color = self.maker.point_color

        for li in lines:
            canvas._new_line = True
            for si in li:
                # mi = si['mask'] if 'mask' in si else 0
                # ai = si['attenuator'] if 'attenuator' in si else 0
                # if 'offset_x' in si:
                #     ptargs['offset_x'] = si['offset_x']
                #     ptargs['offset_y'] = si['offset_y']
                mi, ai = self._get_mask_attenuator(si)
                self._set_offset(si, ptargs)
                canvas.new_line_point(xy=si['xy'],
                                      z=si['z'],
                                      mask=mi, attenuator=ai,
                                      point_color=point_color,
                                      line_color=point_color,
                                      velocity=si['velocity'],
                                      **ptargs)

    def _load_points(self, points, ptargs):
        canvas = self.canvas
        point_color = self.maker.point_color
        #         ptargs['spot_color'] = convert_color(self.maker.spot_color, output='rgbF')
        ptargs['spot_color'] = self.maker.spot_color
        for pi in points:
            # mi = pi['mask'] if 'mask' in pi else 0
            # ai = pi['attenuator'] if 'attenuator' in pi else 0
            # if 'offset_x' in pi:
            #     ptargs['offset_x'] = pi['offset_x']
            #     ptargs['offset_y'] = pi['offset_y']
            mi, ai = self._get_mask_attenuator(pi)
            self._set_offset(pi, ptargs)

            canvas.new_point(xy=pi['xy'],
                             z=pi['z'],
                             mask=mi, attenuator=ai,
                             default_color=point_color,
                             **ptargs)

    def _load_polygons(self, polygons, ptargs):
        point_color = self.maker.point_color
        canvas = self.canvas
        for k, po in six.iteritems(polygons):
            canvas._new_polygon = True
            v = po['velocity']
            use_convex_hull = po['use_convex_hull']
            scan_size = po['scan_size']
            # mi = po['mask'] if po.has_key('mask') else 0
            # ai = po['attenuator'] if po.has_key('attenuator') else 0
            mi, ai = self._get_mask_attenuator(po)
            for pi in po['points']:
                self._set_offset(pi, ptargs)
                canvas.new_polygon_point(xy=pi['xy'],
                                         z=pi['z'],
                                         velocity=v,
                                         identifier=str(int(k) + 1),
                                         point_color=point_color,
                                         mask=mi, attenuator=ai,
                                         scan_size=scan_size,
                                         use_convex_hull=use_convex_hull,
                                         **ptargs)

    def _set_offset(self, item, ptargs):
        if 'offset_x' in item:
            ptargs['offset_x'] = item['offset_x']
            ptargs['offset_y'] = item['offset_y']

    def _get_mask_attenuator(self, item):
        mi = item['mask'] if 'mask' in item else 0
        ai = item['attenuator'] if 'attenuator' in item else 0
        return mi, ai

    def _load_transects(self, trans, ptargs):
        canvas = self.canvas
        point_color = self.maker.point_color
        for ti in trans:
            canvas._new_transect = True
            points = ti['points']
            step = ti['step']
            for pi in points:
                mi, ai = self._get_mask_attenuator(pi)
                ptargs['mask'] = mi
                ptargs['attenuator'] = ai
                self._set_offset(pi, ptargs)

                canvas.new_transect_point(xy=pi['xy'],
                                          z=pi['z'],
                                          step=step,
                                          point_color=point_color,
                                          **ptargs)

    def _get_program_points_label(self):
        return 'End Program' if self.is_programming else 'Program Positions'

    def _get_show_hide_label(self):
        return 'Hide' if self.is_visible else 'Show'

    def _mode_default(self):
        return 'point'

    def _clear_mode_default(self):
        return 'all'

    def traits_view(self):
        v = View(VGroup(
            Item('mode')),
            HGroup(Item('show_hide', show_label=False,
                        editor=ButtonEditor(label_value='show_hide_label')),
                   Item('program_points', show_label=False,
                        editor=ButtonEditor(label_value='program_points_label'))),
            Item('maker', style='custom',
                 enabled_when='is_programming',
                 show_label=False),
            HGroup(Item('load_points', show_label=False),
                   Item('save_points', show_label=False)))
        return v

# ============= EOF =============================================
#    def _show_scene_viewer_fired(self):
# #        from pychron.canvas.canvas2D.video_laser_tray_canvas import VideoLaserTrayCanvas
#
#        xlim = self.canvas.get_mapper_limits('index')
#        ylim = self.canvas.get_mapper_limits('value')
#
#        canvas = self.canvas.__class__(use_pan=False, show_grids=True)
#        if hasattr(self.canvas, 'video'):
#            img = self.canvas.video.get_image_data()
#            canvas.video_underlay._cached_image = img
#
#        canvas.set_mapper_limits('index', xlim)
#        canvas.set_mapper_limits('value', ylim)
#        canvas.scene = self.canvas.scene.clone_traits()
#        canvas.scene.set_canvas(canvas)
#
#        sv = LaserMineViewer(canvas=canvas)
#
#        sv.canvas.freeze()
#        info = sv.edit_traits(kind='livemodal')
#        if info.result:
#            save = True
#            if self._path is None:
#                save = self.confirmation_dialog('Would you like to save these points?', title='Save Changes')
#
#            if save:
#                self.maker.canvas = canvas
#                self._dump(self._path)
#                self.canvas.scene = canvas.scene
#        self.maker.canvas = self.canvas
#        del sv
#    def _plot_scan_fired(self):
#        polygons = self.canvas.polygons
#        if self.polygon_entry is not None and self.scan_size:
#            pp = next((pi for pi in polygons if pi.identifier == str(self.polygon_entry)), None)
#            scan_size = None
#            if hasattr(self.maker, 'scan_size'):
#                scan_size = self.maker.scan_size
#            else:
#                scan_size = pp.scan_size
#
#            poly = [(pi.x * 1000, pi.y * 1000) for pi in pp.points]
#
#            from pychron.core.geometry.polygon_offset import polygon_offset
#            from pychron.core.geometry.scan_line import make_raster_polygon
#            use_offset = True
#            if use_offset:
#                opoly = polygon_offset(poly, -10)
#                opoly = array(opoly, dtype=int)
#                opoly = opoly[:, (0, 1)]
#
# #            print opoly, scan_size
#            lines = make_raster_polygon(opoly, step=scan_size)
#            from pychron.graph.graph import Graph
#
#            g = Graph()
#            g.new_plot()
#
#            for po in (poly, opoly):
#                po = array(po, dtype=int)
#                try:
#                    xs, ys = po.T
#                except ValueError:
#                    xs, ys, _ = po.T
#                xs = hstack((xs, xs[0]))
#                ys = hstack((ys, ys[0]))
#                g.new_series(xs, ys)
#
#            for p1, p2 in lines:
#                xi, yi = (p1[0], p2[0]), (p1[1], p2[1])
#                g.new_series(xi, yi, color='green')
#
#            g.edit_traits()

# def _plot_raster(self, poly, scan_size, scale):
#        use_convex_hull = False
#        from pychron.core.geometry.scan_line import raster
#        from pychron.core.geometry.convex_hull import convex_hull
#        from pychron.core.geometry.polygon_offset import polygon_offset
#        from pychron.core.geometry.geometry import sort_clockwise
#
#        poly = sort_clockwise(poly, poly)
#        poly = array(poly)
#        poly *= scale
#
#        npoints, lens = raster(poly,
#                         step=200,
#                         offset= -500,
#                         use_convex_hull=use_convex_hull, find_min=True)
#
#        from pychron.graph.graph import Graph
#        g = Graph(window_height=700,
#                  window_title='Scan Line Minimization',
#                  window_y=30)
#
#        g.plotcontainer.padding = 5
#        g.new_plot(padding=[60, 30, 30, 50],
#                   bounds=[400, 400],
#                   resizable='h',
#                   xtitle='X (microns)',
#                   ytitle='Y (microns)')
#
#        if use_convex_hull:
#            poly = convex_hull(poly)
#            xs, ys = poly.T
#            xs = hstack((xs, xs[0]))
#            ys = hstack((ys, ys[0]))
#        else:
#            xs, ys = poly.T
#            xs = hstack((xs, xs[0]))
#            ys = hstack((ys, ys[0]))
#
#        # plot original
#        g.new_series(xs, ys)
#        g.set_x_limits(min(xs), max(xs), pad='0.1')
#        g.set_y_limits(min(ys), max(ys), pad='0.1')
#        for ps in npoints:
#            for i in range(0, len(ps), 2):
#                p1, p2 = ps[i], ps[i + 1]
#                g.new_series((p1[0], p2[0]),
#                             (p1[1], p2[1]), color='black')
#
#        # plot offset polygon
#        opoly = polygon_offset(poly, -500)
#        if use_convex_hull:
#            opoly = convex_hull(opoly)
#            opoly = vstack(opoly, opoly[:1])
#            xs, ys, _ = opoly.T
#
# #            xs = hstack((xs, xs[0]))
# #            ys = hstack((ys, ys[0]))
#        else:
#            opoly = array(opoly, dtype=int)
#            xs, ys, _ = opoly.T
#
#        g.new_series(xs, ys)
#
#        # plot theta vs num lines ie the scan line minimization
#        g.new_plot(padding=[50, 30, 30, 30],
#               xtitle='Theta (degrees)',
#               ytitle='Num. Scan Lines',
#               bounds=[400, 100],
#               resizable='h'
#               )
#
#        ts, ls = zip(*lens)
#        g.new_series(ts, ls)
#
#        g.edit_traits()