import type { GrlSourceRange, GrlToken } from "../lexer/index.js";

export type GrlAstNodeKind =
  | "Program"
  | "LanguageDeclaration"
  | "ModuleDeclaration"
  | "ImportDeclaration"
  | "DataDeclaration"
  | "TargetDeclaration"
  | "PathDeclaration"
  | "PathDefaultsBlock"
  | "PathSourceBlock"
  | "PathPoint"
  | "PathEvent"
  | "OperationDeclaration"
  | "OperationProcessBlock"
  | "OperationActionBlock"
  | "ProcedureDeclaration"
  | "FunctionDeclaration"
  | "RawTopLevelDeclaration"
  | "IdentifierExpression"
  | "NumberLiteral"
  | "StringLiteral"
  | "BooleanLiteral"
  | "ArrayExpression"
  | "CallExpression"
  | "ObjectExpression"
  | "OffsetExpression";

export interface GrlAstNode {
  kind: GrlAstNodeKind;
  range: GrlSourceRange;
}

export interface GrlLanguageDeclaration extends GrlAstNode {
  kind: "LanguageDeclaration";
  language: "grl";
  version: string;
}

export interface GrlImportDeclaration extends GrlAstNode {
  kind: "ImportDeclaration";
  moduleName: string;
}

export type GrlDeclarationStorage = "persistent" | "const" | "var";

export interface GrlDataDeclaration extends GrlAstNode {
  kind: "DataDeclaration";
  storage: GrlDeclarationStorage;
  typeName: string;
  name: string;
  initializer: GrlExpression;
}

export interface GrlTargetDeclaration extends GrlAstNode {
  kind: "TargetDeclaration";
  name: string;
  target: GrlExpression;
}

export interface GrlPathProperty {
  key: string;
  value: GrlExpression;
  range: GrlSourceRange;
}

export interface GrlPathDefaultsBlock extends GrlAstNode {
  kind: "PathDefaultsBlock";
  properties: GrlPathProperty[];
}

export interface GrlPathSourceBlock extends GrlAstNode {
  kind: "PathSourceBlock";
  properties: GrlPathProperty[];
}

export interface GrlPathPoint extends GrlAstNode {
  kind: "PathPoint";
  id: string;
  motionTokens: GrlToken[];
}

export interface GrlPathEvent extends GrlAstNode {
  kind: "PathEvent";
  timing: "before" | "after" | "at";
  pointId: string;
  distance?: GrlNumberLiteral;
  actionTokens: GrlToken[];
}

export type GrlPathItem =
  | GrlPathDefaultsBlock
  | GrlPathSourceBlock
  | GrlPathPoint
  | GrlPathEvent;

export interface GrlPathDeclaration extends GrlAstNode {
  kind: "PathDeclaration";
  name: string;
  items: GrlPathItem[];
}

export interface GrlOperationProcessBlock extends GrlAstNode {
  kind: "OperationProcessBlock";
  properties: GrlPathProperty[];
}

export interface GrlOperationActionBlock extends GrlAstNode {
  kind: "OperationActionBlock";
  actionKind: "start_action" | "end_action";
  actionTokens: GrlToken[];
}

export type GrlOperationItem = GrlOperationProcessBlock | GrlOperationActionBlock;

export interface GrlOperationDeclaration extends GrlAstNode {
  kind: "OperationDeclaration";
  name: string;
  operationKind: string;
  pathName: string;
  items: GrlOperationItem[];
}

export interface GrlProcedureDeclaration extends GrlAstNode {
  kind: "ProcedureDeclaration";
  name: string;
  params: GrlToken[];
  bodyTokens: GrlToken[];
}

export interface GrlFunctionDeclaration extends GrlAstNode {
  kind: "FunctionDeclaration";
  returnType: string;
  name: string;
  params: GrlToken[];
  bodyTokens: GrlToken[];
}

export interface GrlRawTopLevelDeclaration extends GrlAstNode {
  kind: "RawTopLevelDeclaration";
  declarationType: string;
  tokens: GrlToken[];
}

export type GrlTopLevelDeclaration =
  | GrlImportDeclaration
  | GrlDataDeclaration
  | GrlTargetDeclaration
  | GrlPathDeclaration
  | GrlOperationDeclaration
  | GrlProcedureDeclaration
  | GrlFunctionDeclaration
  | GrlRawTopLevelDeclaration;

export interface GrlModuleDeclaration extends GrlAstNode {
  kind: "ModuleDeclaration";
  name: string;
  declarations: GrlTopLevelDeclaration[];
}

export interface GrlProgram extends GrlAstNode {
  kind: "Program";
  language?: GrlLanguageDeclaration;
  module: GrlModuleDeclaration;
}

export interface GrlIdentifierExpression extends GrlAstNode {
  kind: "IdentifierExpression";
  name: string;
}

export interface GrlNumberLiteral extends GrlAstNode {
  kind: "NumberLiteral";
  value: number;
  raw: string;
  unit?: {
    raw: string;
    kind: string;
    siUnit: string;
    normalizedValue: number;
  };
}

export interface GrlStringLiteral extends GrlAstNode {
  kind: "StringLiteral";
  value: string;
}

export interface GrlBooleanLiteral extends GrlAstNode {
  kind: "BooleanLiteral";
  value: boolean;
}

export interface GrlArrayExpression extends GrlAstNode {
  kind: "ArrayExpression";
  elements: GrlExpression[];
}

export interface GrlCallExpression extends GrlAstNode {
  kind: "CallExpression";
  callee: string;
  args: GrlExpression[];
}

export interface GrlObjectProperty {
  key: string;
  value: GrlExpression;
  range: GrlSourceRange;
}

export interface GrlObjectExpression extends GrlAstNode {
  kind: "ObjectExpression";
  typeName: string;
  properties: GrlObjectProperty[];
}

export interface GrlOffsetAxis {
  axis: "x" | "y" | "z";
  value: GrlNumberLiteral;
}

export interface GrlOffsetExpression extends GrlAstNode {
  kind: "OffsetExpression";
  base: GrlExpression;
  mode: "frame" | "tool";
  frameName?: string;
  axes: GrlOffsetAxis[];
}

export type GrlExpression =
  | GrlIdentifierExpression
  | GrlNumberLiteral
  | GrlStringLiteral
  | GrlBooleanLiteral
  | GrlArrayExpression
  | GrlCallExpression
  | GrlObjectExpression
  | GrlOffsetExpression;
