import "unplugin";
import { Span } from "mini-parse";

//#region ../wesl/src/LiveDeclarations.d.ts
/** decls currently visible in this scope */
/** decls currently visible in this scope */
interface LiveDecls {
  /** decls currently visible in this scope */
  decls: Map<string, DeclIdent>;
  /** live decls in the parent scope. null for the modue root scope */
  parent?: LiveDecls | null;
}

//#endregion
//#region ../wesl/src/ParseWESL.d.ts
/** create a LiveDecls */

/** result of a parse for one wesl module (e.g. one .wesl file)
 *
 * The parser constructs the AST constructed into three sections
 * for convenient access by the binding stage.
 *  - import statements
 *  - language elements (fn, struct, etc)
 *  - scopes
 *
 */
interface WeslAST {
  /** source text for this module */
  srcModule: SrcModule;
  /** root module element */
  moduleElem: ModuleElem;
  /** root scope for this module */
  rootScope: Scope;
  /** imports found in this module */
  imports: ImportStatement[];
  /** module level const_assert statements */
  moduleAsserts?: ConstAssertElem[];
}

//#endregion
//#region ../wesl/src/Scope.d.ts
/** an extended version of the AST */
interface SrcModule {
  /** module path "rand_pkg::sub::foo", or "package::main" */
  modulePath: string;
  /** file path to the module for user error reporting e.g "rand_pkg:sub/foo.wesl", or "./sub/foo.wesl" */
  debugFilePath: string;
  /** original src for module */
  src: string;
}
/** a src declaration or reference to an ident */
type Ident = DeclIdent | RefIdent;
/** LATER change this to a Map, so that `toString` isn't accidentally a condition */

interface IdentBase {
  originalName: string;
  id?: number;
}
interface RefIdent extends IdentBase {
  kind: "ref";
  refersTo?: Ident;
  std?: true;
  ast: WeslAST;
  refIdentElem: RefIdentElem;
}
interface DeclIdent extends IdentBase {
  kind: "decl";
  /** name in the output code */
  mangledName?: string;
  /** link to AST so that we can traverse scopes and know what elems to emit */
  declElem?: DeclarationElem;
  /** scope in which this declaration is found */
  containingScope: Scope;
  /** scope for the references within this declaration
   * (only needed for global decls.)
   * if this decl is included in the link, dependentScope holds other refIdents that should be included too */
  dependentScope?: Scope;
  /** true if this is a global declaration (e.g. not a local variable) */
  isGlobal: boolean;
  /** To figure out which module this declaration is from. */
  srcModule: SrcModule;
}
/** tree of ident references, organized by lexical scope and partialScope . */
type Scope = LexicalScope | PartialScope;
/** A wgsl scope */
interface LexicalScope extends ScopeBase {
  kind: "scope";
  /** @if condition for conditionally translating this scope */
  ifAttribute?: IfAttribute;
  /**
   * Efficient access to declarations in this scope.
   * constructed on demand, for module root scopes only */
  _scopeDecls?: LiveDecls;
}
/** A synthetic partial scope to contain @if conditioned idents.
 * PartialScope idents are considered to be in the wgsl lexical scope of their parent.  */
interface PartialScope extends ScopeBase {
  kind: "partial";
  /** @if condition for conditionally translating this scope */
  ifAttribute?: IfAttribute;
}
/** common scope elements  */
interface ScopeBase {
  /** id for debugging */
  id: number;
  /** null for root scope in a module */
  parent: Scope | null;
  contents: (Ident | Scope)[];
  /** @if conditions for conditionally translating this scope */
  ifAttribute?: IfAttribute;
}

//#endregion
//#region ../wesl/src/AbstractElems.d.ts
/** Combine two scope siblings.
 * The first scope is mutated to append the contents of the second.  */
/**
 * Structures to describe the 'interesting' parts of a WESL source file.
 *
 * The parts of the source that need to analyze further in the linker
 * are pulled out into these structures.
 *
 * The parts that are uninteresting the the linker are recorded
 * as 'TextElem' nodes, which are generally just copied to the output WGSL
 * along with their containing element.
 */
type AbstractElem = GrammarElem | SyntheticElem;
type GrammarElem = ContainerElem | TerminalElem;
type ContainerElem = AttributeElem | AliasElem | ConstAssertElem | ConstElem | UnknownExpressionElem | SimpleMemberRef | FnElem | TypedDeclElem | GlobalVarElem | LetElem | ModuleElem | OverrideElem | FnParamElem | StructElem | StructMemberElem | StuffElem | TypeRefElem | VarElem | StatementElem | SwitchClauseElem;
/** Inspired by https://github.com/wgsl-tooling-wg/wesl-rs/blob/3b2434eac1b2ebda9eb8bfb25f43d8600d819872/crates/wgsl-parse/src/syntax.rs#L364 */
type ExpressionElem = Literal | TranslateTimeFeature | RefIdentElem | ParenthesizedExpression | ComponentExpression | ComponentMemberExpression | UnaryExpression | BinaryExpression | FunctionCallExpression;
type TerminalElem = DirectiveElem | DeclIdentElem | NameElem | RefIdentElem | TextElem | ImportElem;
type GlobalDeclarationElem = AliasElem | ConstElem | FnElem | GlobalVarElem | OverrideElem | StructElem;
type DeclarationElem = GlobalDeclarationElem | FnParamElem | VarElem;
interface AbstractElemBase {
  kind: AbstractElem["kind"];
  start: number;
  end: number;
}
interface ElemWithContentsBase extends AbstractElemBase {
  contents: AbstractElem[];
}
interface HasAttributes {
  attributes?: AttributeElem[];
}
/**
 * a raw bit of text in WESL source that's typically copied to the linked WGSL.
 * e.g. a keyword  like 'var'
 * or a phrase we needn't analyze further like '@diagnostic(off,derivative_uniformity)'
 */
interface TextElem extends AbstractElemBase {
  kind: "text";
  srcModule: SrcModule;
}
/** a name that doesn't need to be an Ident
 * e.g.
 * - a struct member name
 * - a diagnostic rule name
 * - an enable-extension name
 * - an interpolation sampling name
 */
interface NameElem extends AbstractElemBase {
  kind: "name";
  name: string;
}
/** an identifier that 'refers to' a declaration (aka a symbol reference) */
interface RefIdentElem extends AbstractElemBase {
  kind: RefIdent["kind"];
  ident: RefIdent;
  srcModule: SrcModule;
}
/** a declaration identifier (aka a symbol declaration) */
interface DeclIdentElem extends AbstractElemBase {
  kind: DeclIdent["kind"];
  ident: DeclIdent;
  srcModule: SrcModule;
}
/** Holds an import statement, and has a span */
interface ImportElem extends AbstractElemBase {
  kind: "import";
  imports: ImportStatement;
}
/**
 * An import statement, which is tree shaped.
 * `import foo::bar::{baz, cat as neko};
 */
interface ImportStatement {
  kind: "import-statement";
  segments: ImportSegment[];
  finalSegment: ImportCollection | ImportItem;
}
/**
 * A collection of import trees.
 * `{baz, cat as neko}`
 */
interface ImportSegment {
  kind: "import-segment";
  name: string;
}
/**
 * A primitive segment in an import statement.
 * `foo`
 */
interface ImportCollection {
  kind: "import-collection";
  subtrees: ImportStatement[];
}
/**
 * A renamed item at the end of an import statement.
 * `cat as neko`
 */
interface ImportItem {
  kind: "import-item";
  name: string;
  as?: string;
}
/** generated element, produced after parsing and binding */
interface SyntheticElem {
  kind: "synthetic";
  text: string;
}
/** a declaration identifer with a possible type */
interface TypedDeclElem extends ElemWithContentsBase {
  kind: "typeDecl";
  decl: DeclIdentElem;
  typeRef?: TypeRefElem;
  typeScope?: Scope;
}
/** an alias statement */
interface AliasElem extends ElemWithContentsBase, HasAttributes {
  kind: "alias";
  name: DeclIdentElem;
  typeRef: TypeRefElem;
}
/** an attribute like '@compute' or '@binding(0)' */
interface AttributeElem extends ElemWithContentsBase {
  kind: "attribute";
  attribute: Attribute;
}
type Attribute = StandardAttribute | InterpolateAttribute | BuiltinAttribute | DiagnosticAttribute | IfAttribute;
interface StandardAttribute {
  kind: "@attribute";
  name: string;
  params?: UnknownExpressionElem[];
}
interface InterpolateAttribute {
  kind: "@interpolate";
  params: NameElem[];
}
interface BuiltinAttribute {
  kind: "@builtin";
  param: NameElem;
}
interface DiagnosticAttribute {
  kind: "@diagnostic";
  severity: NameElem;
  rule: [NameElem, NameElem | null];
}
interface IfAttribute {
  kind: "@if";
  param: TranslateTimeExpressionElem;
}
/** a const_assert statement */
interface ConstAssertElem extends ElemWithContentsBase, HasAttributes {
  kind: "assert";
}
/** a const declaration */
interface ConstElem extends ElemWithContentsBase, HasAttributes {
  kind: "const";
  name: TypedDeclElem;
}
/** an expression w/o special handling, used inside attribute parameters */
interface UnknownExpressionElem extends ElemWithContentsBase {
  kind: "expression";
}
/** an expression that can be safely evaluated at compile time */
interface TranslateTimeExpressionElem {
  kind: "translate-time-expression";
  expression: ExpressionElem;
  span: Span;
}
/** A literal value in WESL source. A boolean or a number. */
interface Literal {
  kind: "literal";
  value: string;
  span: Span;
}
/** `words`s inside `@if` */
interface TranslateTimeFeature {
  kind: "translate-time-feature";
  name: string;
  span: Span;
}
/** (expr) */
interface ParenthesizedExpression {
  kind: "parenthesized-expression";
  expression: ExpressionElem;
}
/** `foo[expr]` */
interface ComponentExpression {
  kind: "component-expression";
  base: ExpressionElem;
  access: ExpressionElem;
}
/** `foo.member` */
interface ComponentMemberExpression {
  kind: "component-member-expression";
  base: ExpressionElem;
  access: NameElem;
}
/** `+foo` */
interface UnaryExpression {
  kind: "unary-expression";
  operator: UnaryOperator;
  expression: ExpressionElem;
}
/** `foo + bar` */
interface BinaryExpression {
  kind: "binary-expression";
  operator: BinaryOperator;
  left: ExpressionElem;
  right: ExpressionElem;
}
/** `foo(arg, arg)` */
interface FunctionCallExpression {
  kind: "call-expression";
  function: RefIdentElem;
  arguments: ExpressionElem[];
}
interface UnaryOperator {
  value: "!" | "&" | "*" | "-" | "~";
  span: Span;
}
interface BinaryOperator {
  value: ("||" | "&&" | "+" | "-" | "*" | "/" | "%" | "==") | ("!=" | "<" | "<=" | ">" | ">=" | "|" | "&" | "^") | ("<<" | ">>");
  span: Span;
}
type DirectiveVariant = DiagnosticDirective | EnableDirective | RequiresDirective;
interface DirectiveElem extends AbstractElemBase, HasAttributes {
  kind: "directive";
  directive: DirectiveVariant;
}
interface DiagnosticDirective {
  kind: "diagnostic";
  severity: NameElem;
  rule: [NameElem, NameElem | null];
}
interface EnableDirective {
  kind: "enable";
  extensions: NameElem[];
}
interface RequiresDirective {
  kind: "requires";
  extensions: NameElem[];
}
/** a function declaration */
interface FnElem extends ElemWithContentsBase, HasAttributes {
  kind: "fn";
  name: DeclIdentElem;
  params: FnParamElem[];
  body: StatementElem;
  returnAttributes?: AttributeElem[];
  returnType?: TypeRefElem;
}
/** a global variable declaration (at the root level) */
interface GlobalVarElem extends ElemWithContentsBase, HasAttributes {
  kind: "gvar";
  name: TypedDeclElem;
}
/** an entire file */
interface ModuleElem extends ElemWithContentsBase {
  kind: "module";
}
/** an override declaration */
interface OverrideElem extends ElemWithContentsBase, HasAttributes {
  kind: "override";
  name: TypedDeclElem;
}
/** a parameter in a function declaration */
interface FnParamElem extends ElemWithContentsBase, HasAttributes {
  kind: "param";
  name: TypedDeclElem;
}
/** simple references to structures, like myStruct.bar
 * (used for transforming refs to binding structs) */
interface SimpleMemberRef extends ElemWithContentsBase {
  kind: "memberRef";
  name: RefIdentElem;
  member: NameElem;
  extraComponents?: StuffElem;
}
/** a struct declaration */
interface StructElem extends ElemWithContentsBase, HasAttributes {
  kind: "struct";
  name: DeclIdentElem;
  members: StructMemberElem[];
  bindingStruct?: true;
}
/** generic container of other elements */
interface StuffElem extends ElemWithContentsBase {
  kind: "stuff";
}
/** a struct declaration that's been marked as a bindingStruct */

/** a member of a struct declaration */
interface StructMemberElem extends ElemWithContentsBase, HasAttributes {
  kind: "member";
  name: NameElem;
  typeRef: TypeRefElem;
  mangledVarName?: string;
}
type TypeTemplateParameter = TypeRefElem | UnknownExpressionElem;
/** a reference to a type, like 'f32', or 'MyStruct', or 'ptr<storage, array<f32>, read_only>'   */
interface TypeRefElem extends ElemWithContentsBase {
  kind: "type";
  name: RefIdent;
  templateParams?: TypeTemplateParameter[];
}
/** a variable declaration */
interface VarElem extends ElemWithContentsBase, HasAttributes {
  kind: "var";
  name: TypedDeclElem;
}
interface LetElem extends ElemWithContentsBase, HasAttributes {
  kind: "let";
  name: TypedDeclElem;
}
interface StatementElem extends ElemWithContentsBase, HasAttributes {
  kind: "statement";
}
interface SwitchClauseElem extends ElemWithContentsBase, HasAttributes {
  kind: "switch-clause";
}

//#endregion
//#region ../wesl/src/ParsedRegistry.d.ts
interface ParsedRegistry {
  modules: Record<string, WeslAST>;
}

//#endregion
//#region ../wesl/src/Linker.d.ts
type LinkerTransform = (boundAST: TransformedAST) => TransformedAST;
interface WeslJsPlugin {
  transform?: LinkerTransform;
}
interface TransformedAST extends Pick<WeslAST, "srcModule" | "moduleElem"> {
  globalNames: Set<string>;
  notableElems: Record<string, AbstractElem[]>;
}

//#endregion
//#region src/WeslPluginOptions.d.ts
interface WeslPluginOptions {
  weslToml?: string;
  extensions?: PluginExtension[];
}

//#endregion
//#region src/WeslPlugin.d.ts
/** loaded (or synthesized) info from .toml */
interface WeslToml {
  /** glob search strings to find .wesl/.wgsl files. Relative to the toml directory. */
  weslFiles: string[];
  /** base directory for wesl files. Relative to the toml directory. */
  weslRoot: string;
  /** names of directly referenced wesl shader packages (e.g. npm package names) */
  dependencies?: string[];
}
interface WeslTomlInfo {
  /** The path to the toml file, relative to the cwd, undefined if no toml file */
  tomlFile: string | undefined;
  /** The absolute path to the directory that contains the toml.
   * Paths inside the toml are relative to this. */
  tomlDir: string;
  /** The wesl root, relative to the cwd.
   * This lets us correctly do `path.resolve(resolvedWeslRoot, someShaderFile)` */
  resolvedWeslRoot: string;
  /** The underlying toml file */
  toml: WeslToml;
}

//#endregion
//#region src/PluginExtension.d.ts
/** internal cache used by the plugin to avoid reloading files
 * The assumption is that the plugin is used for a single wesl.toml and set of shaders
 * (a plugin instance supports only one shader project)
 */
/** function type required for for emit extensions */
type ExtensionEmitFn = (/** absolute path to the shader to which the extension is attached */
shaderPath: string, /** support functions available to plugin extensions */
pluginApi: PluginExtensionApi, /** static conditions specified on the js import */conditions?: Record<string, boolean>) => Promise<string>;
/** an extension that runs inside the wesl-js build plugin */
interface PluginExtension extends WeslJsPlugin {
  /** javascript imports with this suffix will trigger the plugin */
  extensionName: string;
  /** generate javascript text for js/ts importers to use.
   *   e.g. import myPluginJs from "./foo.wesl?myPlugin"; */
  emitFn: ExtensionEmitFn;
}
/** api supplied to plugin extensions */
interface PluginExtensionApi {
  weslToml: () => Promise<WeslTomlInfo>;
  weslSrc: () => Promise<Record<string, string>>;
  weslRegistry: () => Promise<ParsedRegistry>;
  weslMain: (baseId: string) => Promise<string>;
  weslDependencies: () => Promise<string[]>;
}

//#endregion
export { ExtensionEmitFn, PluginExtension, PluginExtensionApi, WeslPluginOptions };