import { NodePath } from "@babel/traverse";
import * as t from "@babel/types";
import { FilterPattern } from "unplugin";
import { transpileFn } from "tinyest-for-wgsl";

//#region src/core/common.d.ts

interface Options {
  /** @default [/\.m?[jt]sx?$/] */
  include?: FilterPattern;
  /** @default undefined */
  exclude?: FilterPattern;
  /** @default undefined */
  enforce?: 'post' | 'pre' | undefined;
  /** @default undefined */
  forceTgpuAlias?: string | undefined;
  /** @default true */
  autoNamingEnabled?: boolean | undefined;
  /**
   * Skipping files that don't contain "typegpu", "tgpu" or "use gpu".
   * In case this early pruning hinders transformation, you
   * can disable it.
   *
   * @default true
   */
  earlyPruning?: boolean | undefined;
}
type MetadatableFunction = t.FunctionDeclaration | t.FunctionExpression | t.ArrowFunctionExpression;
interface TransformMethods {
  warn(message: string): void;
  /**
   * For function statements to have metadata assigned, they first have to be manually hoisted
   * to the top of their scope, then replaced with a variable declaration.
   *
   * @example
   * ```ts
   * const value = mod(13, 10);
   * function mod(a: number, b: number): number {
   *   'use gpu';
   *   return a % b;
   * }
   * ```
   *
   * Should be transformed to:
   *
   * ```ts
   * const mod = (a: number, b: number): number => {
   *   'use gpu';
   *   return a % b;
   * };
   * const value = mod(13, 10);
   * ```
   *
   * Note that named function expressions aren't hoisted, so they don't pose a problem.
   * ```ts
   * double(2); // Uncaught ReferenceError: double is not defined
   * console.log(function double(a) { return a * 2; });
   * ```
   */
  assignMetadata(this: PluginState, path: NodePath<MetadatableFunction>, name: string | undefined, ast: ReturnType<typeof transpileFn>): void;
  wrapInAutoName(this: PluginState, path: NodePath<t.Expression>, name: string): void;
  replaceWithAssignmentOverload(path: NodePath<t.AssignmentExpression>, runtimeFn: string): void;
  replaceWithBinaryOverload(path: NodePath<t.BinaryExpression>, runtimeFn: string): void;
}
interface PluginState extends TransformMethods {
  /**
   * How the `tgpu` object is used in code. Since it can be aliased, we
   * need to catch that and act accordingly.
   */
  tgpuAliases: Set<string>;
  autoNamingEnabled: boolean;
  /**
   * Populated by Babel
   */
  filename?: string | undefined;
  /**
   * In Babel, options are assigned to the property `opts` on the plugin state.
   * We use this pattern everywhere for consistency.
   */
  opts: Options;
  inUseGpuScope: boolean;
  alreadyTransformed: WeakSet<t.Node>;
}
//#endregion
export { PluginState as n, Options as t };