'use strict';

/**
 * @file ShaderGraph.ts
 * @description Visual Shader Graph Editor System
 * PRO-1.4: Professional Editor Features
 *
 * Features:
 * - Node-based shader editing
 * - GLSL/WGSL/HLSL generation
 * - Real-time preview
 * - Material templates
 * - Custom node creation
 * - Subgraph support
 * - PBR and unlit workflows
 */
// ============================================================
// Built-in Node Library
// ============================================================
const SHADER_NODE_LIBRARY = [
    // ─── Input Nodes ─────────────────────────────────────────
    {
        id: 'position',
        name: 'Position',
        category: 'input',
        description: 'World/Object/View position',
        inputs: [],
        outputs: [
            { name: 'World', type: 'vec3' },
            { name: 'Object', type: 'vec3' },
            { name: 'View', type: 'vec3' },
            { name: 'Tangent', type: 'vec3' },
        ],
        glsl: `
      vec3 {{output.World}} = v_worldPosition;
      vec3 {{output.Object}} = v_objectPosition;
      vec3 {{output.View}} = v_viewPosition;
      vec3 {{output.Tangent}} = v_tangent;
    `,
    },
    {
        id: 'normal',
        name: 'Normal',
        category: 'input',
        description: 'Surface normal vectors',
        inputs: [],
        outputs: [
            { name: 'World', type: 'vec3' },
            { name: 'Object', type: 'vec3' },
            { name: 'View', type: 'vec3' },
            { name: 'Tangent', type: 'vec3' },
        ],
        glsl: `
      vec3 {{output.World}} = normalize(v_worldNormal);
      vec3 {{output.Object}} = normalize(v_objectNormal);
      vec3 {{output.View}} = normalize(v_viewNormal);
      vec3 {{output.Tangent}} = normalize(v_tangentNormal);
    `,
    },
    {
        id: 'uv',
        name: 'UV',
        category: 'input',
        description: 'Texture coordinates',
        inputs: [],
        outputs: [
            { name: 'UV0', type: 'vec2' },
            { name: 'UV1', type: 'vec2' },
            { name: 'UV2', type: 'vec2' },
            { name: 'UV3', type: 'vec2' },
        ],
        glsl: `
      vec2 {{output.UV0}} = v_uv0;
      vec2 {{output.UV1}} = v_uv1;
      vec2 {{output.UV2}} = v_uv2;
      vec2 {{output.UV3}} = v_uv3;
    `,
    },
    {
        id: 'vertexColor',
        name: 'Vertex Color',
        category: 'input',
        description: 'Per-vertex color',
        inputs: [],
        outputs: [
            { name: 'Color', type: 'vec4' },
            { name: 'R', type: 'float' },
            { name: 'G', type: 'float' },
            { name: 'B', type: 'float' },
            { name: 'A', type: 'float' },
        ],
        glsl: `
      vec4 {{output.Color}} = v_color;
      float {{output.R}} = v_color.r;
      float {{output.G}} = v_color.g;
      float {{output.B}} = v_color.b;
      float {{output.A}} = v_color.a;
    `,
    },
    {
        id: 'time',
        name: 'Time',
        category: 'input',
        description: 'Time values for animation',
        inputs: [],
        outputs: [
            { name: 'Time', type: 'float' },
            { name: 'SinTime', type: 'float' },
            { name: 'CosTime', type: 'float' },
            { name: 'DeltaTime', type: 'float' },
            { name: 'SmoothDelta', type: 'float' },
        ],
        glsl: `
      float {{output.Time}} = u_time;
      float {{output.SinTime}} = sin(u_time);
      float {{output.CosTime}} = cos(u_time);
      float {{output.DeltaTime}} = u_deltaTime;
      float {{output.SmoothDelta}} = u_smoothDeltaTime;
    `,
    },
    {
        id: 'viewDirection',
        name: 'View Direction',
        category: 'input',
        description: 'Camera view direction',
        inputs: [],
        outputs: [
            { name: 'World', type: 'vec3' },
            { name: 'Object', type: 'vec3' },
            { name: 'View', type: 'vec3' },
        ],
        glsl: `
      vec3 {{output.World}} = normalize(u_cameraPosition - v_worldPosition);
      vec3 {{output.Object}} = (u_worldToObject * vec4({{output.World}}, 0.0)).xyz;
      vec3 {{output.View}} = (u_viewMatrix * vec4({{output.World}}, 0.0)).xyz;
    `,
    },
    {
        id: 'cameraPosition',
        name: 'Camera Position',
        category: 'input',
        inputs: [],
        outputs: [{ name: 'Position', type: 'vec3' }],
        glsl: `vec3 {{output.Position}} = u_cameraPosition;`,
    },
    {
        id: 'screenPosition',
        name: 'Screen Position',
        category: 'input',
        inputs: [],
        outputs: [
            { name: 'Default', type: 'vec4' },
            { name: 'Raw', type: 'vec4' },
            { name: 'Center', type: 'vec2' },
            { name: 'Tiled', type: 'vec2' },
        ],
        glsl: `
      vec4 {{output.Default}} = gl_FragCoord;
      vec4 {{output.Raw}} = gl_FragCoord;
      vec2 {{output.Center}} = gl_FragCoord.xy / u_resolution - 0.5;
      vec2 {{output.Tiled}} = fract(gl_FragCoord.xy / 256.0);
    `,
    },
    {
        id: 'constant',
        name: 'Constant',
        category: 'input',
        description: 'Constant value',
        inputs: [],
        outputs: [{ name: 'Out', type: 'float' }],
        properties: [{ name: 'Value', type: 'float', default: 0 }],
        glsl: `float {{output.Out}} = {{prop.Value}};`,
    },
    {
        id: 'color',
        name: 'Color',
        category: 'input',
        description: 'Color constant',
        inputs: [],
        outputs: [
            { name: 'Out', type: 'vec4' },
            { name: 'RGB', type: 'vec3' },
            { name: 'R', type: 'float' },
            { name: 'G', type: 'float' },
            { name: 'B', type: 'float' },
            { name: 'A', type: 'float' },
        ],
        properties: [{ name: 'Color', type: 'color', default: [1, 1, 1, 1] }],
        glsl: `
      vec4 {{output.Out}} = vec4({{prop.Color}});
      vec3 {{output.RGB}} = {{output.Out}}.rgb;
      float {{output.R}} = {{output.Out}}.r;
      float {{output.G}} = {{output.Out}}.g;
      float {{output.B}} = {{output.Out}}.b;
      float {{output.A}} = {{output.Out}}.a;
    `,
    },
    {
        id: 'vector2',
        name: 'Vector 2',
        category: 'input',
        inputs: [],
        outputs: [{ name: 'Out', type: 'vec2' }],
        properties: [{ name: 'Value', type: 'vec2', default: [0, 0] }],
        glsl: `vec2 {{output.Out}} = vec2({{prop.Value}});`,
    },
    {
        id: 'vector3',
        name: 'Vector 3',
        category: 'input',
        inputs: [],
        outputs: [{ name: 'Out', type: 'vec3' }],
        properties: [{ name: 'Value', type: 'vec3', default: [0, 0, 0] }],
        glsl: `vec3 {{output.Out}} = vec3({{prop.Value}});`,
    },
    {
        id: 'vector4',
        name: 'Vector 4',
        category: 'input',
        inputs: [],
        outputs: [{ name: 'Out', type: 'vec4' }],
        properties: [{ name: 'Value', type: 'vec4', default: [0, 0, 0, 0] }],
        glsl: `vec4 {{output.Out}} = vec4({{prop.Value}});`,
    },
    // ─── Math Nodes ──────────────────────────────────────────
    {
        id: 'add',
        name: 'Add',
        category: 'math',
        inputs: [
            { name: 'A', type: 'float', default: 0 },
            { name: 'B', type: 'float', default: 0 },
        ],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `float {{output.Out}} = {{input.A}} + {{input.B}};`,
    },
    {
        id: 'subtract',
        name: 'Subtract',
        category: 'math',
        inputs: [
            { name: 'A', type: 'float', default: 0 },
            { name: 'B', type: 'float', default: 0 },
        ],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `float {{output.Out}} = {{input.A}} - {{input.B}};`,
    },
    {
        id: 'multiply',
        name: 'Multiply',
        category: 'math',
        inputs: [
            { name: 'A', type: 'float', default: 1 },
            { name: 'B', type: 'float', default: 1 },
        ],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `float {{output.Out}} = {{input.A}} * {{input.B}};`,
    },
    {
        id: 'divide',
        name: 'Divide',
        category: 'math',
        inputs: [
            { name: 'A', type: 'float', default: 1 },
            { name: 'B', type: 'float', default: 1 },
        ],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `float {{output.Out}} = {{input.A}} / max({{input.B}}, 0.0001);`,
    },
    {
        id: 'power',
        name: 'Power',
        category: 'math',
        inputs: [
            { name: 'Base', type: 'float', default: 2 },
            { name: 'Exp', type: 'float', default: 2 },
        ],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `float {{output.Out}} = pow({{input.Base}}, {{input.Exp}});`,
    },
    {
        id: 'sqrt',
        name: 'Square Root',
        category: 'math',
        inputs: [{ name: 'In', type: 'float', default: 1 }],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `float {{output.Out}} = sqrt(max({{input.In}}, 0.0));`,
    },
    {
        id: 'abs',
        name: 'Absolute',
        category: 'math',
        inputs: [{ name: 'In', type: 'float', default: 0 }],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `float {{output.Out}} = abs({{input.In}});`,
    },
    {
        id: 'negate',
        name: 'Negate',
        category: 'math',
        inputs: [{ name: 'In', type: 'float', default: 0 }],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `float {{output.Out}} = -{{input.In}};`,
    },
    {
        id: 'min',
        name: 'Minimum',
        category: 'math',
        inputs: [
            { name: 'A', type: 'float', default: 0 },
            { name: 'B', type: 'float', default: 0 },
        ],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `float {{output.Out}} = min({{input.A}}, {{input.B}});`,
    },
    {
        id: 'max',
        name: 'Maximum',
        category: 'math',
        inputs: [
            { name: 'A', type: 'float', default: 0 },
            { name: 'B', type: 'float', default: 0 },
        ],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `float {{output.Out}} = max({{input.A}}, {{input.B}});`,
    },
    {
        id: 'clamp',
        name: 'Clamp',
        category: 'math',
        inputs: [
            { name: 'In', type: 'float', default: 0 },
            { name: 'Min', type: 'float', default: 0 },
            { name: 'Max', type: 'float', default: 1 },
        ],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `float {{output.Out}} = clamp({{input.In}}, {{input.Min}}, {{input.Max}});`,
    },
    {
        id: 'saturate',
        name: 'Saturate',
        category: 'math',
        description: 'Clamp to 0-1 range',
        inputs: [{ name: 'In', type: 'float', default: 0 }],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `float {{output.Out}} = clamp({{input.In}}, 0.0, 1.0);`,
    },
    {
        id: 'lerp',
        name: 'Lerp',
        category: 'math',
        description: 'Linear interpolation',
        inputs: [
            { name: 'A', type: 'float', default: 0 },
            { name: 'B', type: 'float', default: 1 },
            { name: 'T', type: 'float', default: 0.5 },
        ],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `float {{output.Out}} = mix({{input.A}}, {{input.B}}, {{input.T}});`,
    },
    {
        id: 'smoothstep',
        name: 'Smoothstep',
        category: 'math',
        inputs: [
            { name: 'Edge0', type: 'float', default: 0 },
            { name: 'Edge1', type: 'float', default: 1 },
            { name: 'In', type: 'float', default: 0.5 },
        ],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `float {{output.Out}} = smoothstep({{input.Edge0}}, {{input.Edge1}}, {{input.In}});`,
    },
    {
        id: 'step',
        name: 'Step',
        category: 'math',
        inputs: [
            { name: 'Edge', type: 'float', default: 0.5 },
            { name: 'In', type: 'float', default: 0 },
        ],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `float {{output.Out}} = step({{input.Edge}}, {{input.In}});`,
    },
    {
        id: 'floor',
        name: 'Floor',
        category: 'math',
        inputs: [{ name: 'In', type: 'float', default: 0 }],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `float {{output.Out}} = floor({{input.In}});`,
    },
    {
        id: 'ceil',
        name: 'Ceiling',
        category: 'math',
        inputs: [{ name: 'In', type: 'float', default: 0 }],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `float {{output.Out}} = ceil({{input.In}});`,
    },
    {
        id: 'round',
        name: 'Round',
        category: 'math',
        inputs: [{ name: 'In', type: 'float', default: 0 }],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `float {{output.Out}} = floor({{input.In}} + 0.5);`,
    },
    {
        id: 'frac',
        name: 'Fraction',
        category: 'math',
        inputs: [{ name: 'In', type: 'float', default: 0 }],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `float {{output.Out}} = fract({{input.In}});`,
    },
    {
        id: 'mod',
        name: 'Modulo',
        category: 'math',
        inputs: [
            { name: 'A', type: 'float', default: 0 },
            { name: 'B', type: 'float', default: 1 },
        ],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `float {{output.Out}} = mod({{input.A}}, {{input.B}});`,
    },
    {
        id: 'sin',
        name: 'Sine',
        category: 'math',
        inputs: [{ name: 'In', type: 'float', default: 0 }],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `float {{output.Out}} = sin({{input.In}});`,
    },
    {
        id: 'cos',
        name: 'Cosine',
        category: 'math',
        inputs: [{ name: 'In', type: 'float', default: 0 }],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `float {{output.Out}} = cos({{input.In}});`,
    },
    {
        id: 'tan',
        name: 'Tangent',
        category: 'math',
        inputs: [{ name: 'In', type: 'float', default: 0 }],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `float {{output.Out}} = tan({{input.In}});`,
    },
    {
        id: 'asin',
        name: 'Arcsine',
        category: 'math',
        inputs: [{ name: 'In', type: 'float', default: 0 }],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `float {{output.Out}} = asin(clamp({{input.In}}, -1.0, 1.0));`,
    },
    {
        id: 'acos',
        name: 'Arccosine',
        category: 'math',
        inputs: [{ name: 'In', type: 'float', default: 0 }],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `float {{output.Out}} = acos(clamp({{input.In}}, -1.0, 1.0));`,
    },
    {
        id: 'atan',
        name: 'Arctangent',
        category: 'math',
        inputs: [{ name: 'In', type: 'float', default: 0 }],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `float {{output.Out}} = atan({{input.In}});`,
    },
    {
        id: 'atan2',
        name: 'Arctangent2',
        category: 'math',
        inputs: [
            { name: 'Y', type: 'float', default: 0 },
            { name: 'X', type: 'float', default: 1 },
        ],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `float {{output.Out}} = atan({{input.Y}}, {{input.X}});`,
    },
    {
        id: 'exp',
        name: 'Exponential',
        category: 'math',
        inputs: [{ name: 'In', type: 'float', default: 0 }],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `float {{output.Out}} = exp({{input.In}});`,
    },
    {
        id: 'exp2',
        name: 'Exponential 2',
        category: 'math',
        inputs: [{ name: 'In', type: 'float', default: 0 }],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `float {{output.Out}} = exp2({{input.In}});`,
    },
    {
        id: 'log',
        name: 'Logarithm',
        category: 'math',
        inputs: [{ name: 'In', type: 'float', default: 1 }],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `float {{output.Out}} = log(max({{input.In}}, 0.0001));`,
    },
    {
        id: 'log2',
        name: 'Logarithm 2',
        category: 'math',
        inputs: [{ name: 'In', type: 'float', default: 1 }],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `float {{output.Out}} = log2(max({{input.In}}, 0.0001));`,
    },
    {
        id: 'oneMinus',
        name: 'One Minus',
        category: 'math',
        inputs: [{ name: 'In', type: 'float', default: 0 }],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `float {{output.Out}} = 1.0 - {{input.In}};`,
    },
    {
        id: 'remap',
        name: 'Remap',
        category: 'math',
        description: 'Remap value from one range to another',
        inputs: [
            { name: 'In', type: 'float', default: 0 },
            { name: 'InMin', type: 'float', default: 0 },
            { name: 'InMax', type: 'float', default: 1 },
            { name: 'OutMin', type: 'float', default: 0 },
            { name: 'OutMax', type: 'float', default: 1 },
        ],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `
      float {{output.Out}} = {{input.OutMin}} + ({{input.In}} - {{input.InMin}}) * ({{input.OutMax}} - {{input.OutMin}}) / ({{input.InMax}} - {{input.InMin}});
    `,
    },
    // ─── Vector Nodes ────────────────────────────────────────
    {
        id: 'vectorSplit',
        name: 'Split',
        category: 'vector',
        description: 'Split vector into components',
        inputs: [{ name: 'In', type: 'vec4', default: [0, 0, 0, 0] }],
        outputs: [
            { name: 'X', type: 'float' },
            { name: 'Y', type: 'float' },
            { name: 'Z', type: 'float' },
            { name: 'W', type: 'float' },
        ],
        glsl: `
      float {{output.X}} = {{input.In}}.x;
      float {{output.Y}} = {{input.In}}.y;
      float {{output.Z}} = {{input.In}}.z;
      float {{output.W}} = {{input.In}}.w;
    `,
    },
    {
        id: 'vectorCombine',
        name: 'Combine',
        category: 'vector',
        description: 'Combine components into vector',
        inputs: [
            { name: 'X', type: 'float', default: 0 },
            { name: 'Y', type: 'float', default: 0 },
            { name: 'Z', type: 'float', default: 0 },
            { name: 'W', type: 'float', default: 0 },
        ],
        outputs: [
            { name: 'XYZW', type: 'vec4' },
            { name: 'XYZ', type: 'vec3' },
            { name: 'XY', type: 'vec2' },
        ],
        glsl: `
      vec4 {{output.XYZW}} = vec4({{input.X}}, {{input.Y}}, {{input.Z}}, {{input.W}});
      vec3 {{output.XYZ}} = vec3({{input.X}}, {{input.Y}}, {{input.Z}});
      vec2 {{output.XY}} = vec2({{input.X}}, {{input.Y}});
    `,
    },
    {
        id: 'dot',
        name: 'Dot Product',
        category: 'vector',
        inputs: [
            { name: 'A', type: 'vec3', default: [1, 0, 0] },
            { name: 'B', type: 'vec3', default: [0, 1, 0] },
        ],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `float {{output.Out}} = dot({{input.A}}, {{input.B}});`,
    },
    {
        id: 'cross',
        name: 'Cross Product',
        category: 'vector',
        inputs: [
            { name: 'A', type: 'vec3', default: [1, 0, 0] },
            { name: 'B', type: 'vec3', default: [0, 1, 0] },
        ],
        outputs: [{ name: 'Out', type: 'vec3' }],
        glsl: `vec3 {{output.Out}} = cross({{input.A}}, {{input.B}});`,
    },
    {
        id: 'normalize',
        name: 'Normalize',
        category: 'vector',
        inputs: [{ name: 'In', type: 'vec3', default: [1, 0, 0] }],
        outputs: [{ name: 'Out', type: 'vec3' }],
        glsl: `vec3 {{output.Out}} = normalize({{input.In}});`,
    },
    {
        id: 'length',
        name: 'Length',
        category: 'vector',
        inputs: [{ name: 'In', type: 'vec3', default: [1, 0, 0] }],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `float {{output.Out}} = length({{input.In}});`,
    },
    {
        id: 'distance',
        name: 'Distance',
        category: 'vector',
        inputs: [
            { name: 'A', type: 'vec3', default: [0, 0, 0] },
            { name: 'B', type: 'vec3', default: [1, 0, 0] },
        ],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `float {{output.Out}} = distance({{input.A}}, {{input.B}});`,
    },
    {
        id: 'reflect',
        name: 'Reflect',
        category: 'vector',
        inputs: [
            { name: 'In', type: 'vec3', default: [1, 0, 0] },
            { name: 'Normal', type: 'vec3', default: [0, 1, 0] },
        ],
        outputs: [{ name: 'Out', type: 'vec3' }],
        glsl: `vec3 {{output.Out}} = reflect({{input.In}}, {{input.Normal}});`,
    },
    {
        id: 'refract',
        name: 'Refract',
        category: 'vector',
        inputs: [
            { name: 'In', type: 'vec3', default: [1, 0, 0] },
            { name: 'Normal', type: 'vec3', default: [0, 1, 0] },
            { name: 'IOR', type: 'float', default: 1.5 },
        ],
        outputs: [{ name: 'Out', type: 'vec3' }],
        glsl: `vec3 {{output.Out}} = refract({{input.In}}, {{input.Normal}}, 1.0 / {{input.IOR}});`,
    },
    // ─── Texture Nodes ───────────────────────────────────────
    {
        id: 'sampleTexture2D',
        name: 'Sample Texture 2D',
        category: 'texture',
        inputs: [{ name: 'UV', type: 'vec2', default: [0, 0] }],
        outputs: [
            { name: 'RGBA', type: 'vec4' },
            { name: 'RGB', type: 'vec3' },
            { name: 'R', type: 'float' },
            { name: 'G', type: 'float' },
            { name: 'B', type: 'float' },
            { name: 'A', type: 'float' },
        ],
        properties: [{ name: 'Texture', type: 'texture', default: null }],
        glsl: `
      vec4 {{output.RGBA}} = texture({{prop.Texture}}, {{input.UV}});
      vec3 {{output.RGB}} = {{output.RGBA}}.rgb;
      float {{output.R}} = {{output.RGBA}}.r;
      float {{output.G}} = {{output.RGBA}}.g;
      float {{output.B}} = {{output.RGBA}}.b;
      float {{output.A}} = {{output.RGBA}}.a;
    `,
    },
    {
        id: 'sampleNormalMap',
        name: 'Sample Normal Map',
        category: 'texture',
        inputs: [
            { name: 'UV', type: 'vec2', default: [0, 0] },
            { name: 'Strength', type: 'float', default: 1 },
        ],
        outputs: [{ name: 'Normal', type: 'vec3' }],
        properties: [{ name: 'Texture', type: 'texture', default: null }],
        glsl: `
      vec3 normalSample = texture({{prop.Texture}}, {{input.UV}}).rgb * 2.0 - 1.0;
      normalSample.xy *= {{input.Strength}};
      vec3 {{output.Normal}} = normalize(normalSample);
    `,
    },
    {
        id: 'sampleCubemap',
        name: 'Sample Cubemap',
        category: 'texture',
        inputs: [{ name: 'Direction', type: 'vec3', default: [0, 1, 0] }],
        outputs: [{ name: 'RGBA', type: 'vec4' }],
        properties: [{ name: 'Cubemap', type: 'texture', default: null }],
        glsl: `vec4 {{output.RGBA}} = textureCube({{prop.Cubemap}}, {{input.Direction}});`,
    },
    {
        id: 'triplanar',
        name: 'Triplanar Mapping',
        category: 'texture',
        description: 'Sample texture using world position and normal',
        inputs: [
            { name: 'Position', type: 'vec3' },
            { name: 'Normal', type: 'vec3' },
            { name: 'Tile', type: 'float', default: 1 },
            { name: 'Blend', type: 'float', default: 1 },
        ],
        outputs: [{ name: 'RGBA', type: 'vec4' }],
        properties: [{ name: 'Texture', type: 'texture', default: null }],
        glsl: `
      vec3 blending = pow(abs({{input.Normal}}), vec3({{input.Blend}}));
      blending /= dot(blending, vec3(1.0));
      
      vec4 xProject = texture({{prop.Texture}}, {{input.Position}}.yz * {{input.Tile}});
      vec4 yProject = texture({{prop.Texture}}, {{input.Position}}.xz * {{input.Tile}});
      vec4 zProject = texture({{prop.Texture}}, {{input.Position}}.xy * {{input.Tile}});
      
      vec4 {{output.RGBA}} = xProject * blending.x + yProject * blending.y + zProject * blending.z;
    `,
    },
    // ─── UV Nodes ────────────────────────────────────────────
    {
        id: 'tiling',
        name: 'Tiling and Offset',
        category: 'uv',
        inputs: [
            { name: 'UV', type: 'vec2', default: [0, 0] },
            { name: 'Tiling', type: 'vec2', default: [1, 1] },
            { name: 'Offset', type: 'vec2', default: [0, 0] },
        ],
        outputs: [{ name: 'Out', type: 'vec2' }],
        glsl: `vec2 {{output.Out}} = {{input.UV}} * {{input.Tiling}} + {{input.Offset}};`,
    },
    {
        id: 'rotate',
        name: 'Rotate',
        category: 'uv',
        inputs: [
            { name: 'UV', type: 'vec2', default: [0, 0] },
            { name: 'Center', type: 'vec2', default: [0.5, 0.5] },
            { name: 'Rotation', type: 'float', default: 0 },
        ],
        outputs: [{ name: 'Out', type: 'vec2' }],
        glsl: `
      vec2 uvCentered = {{input.UV}} - {{input.Center}};
      float c = cos({{input.Rotation}});
      float s = sin({{input.Rotation}});
      vec2 {{output.Out}} = vec2(uvCentered.x * c - uvCentered.y * s, uvCentered.x * s + uvCentered.y * c) + {{input.Center}};
    `,
    },
    {
        id: 'flipbook',
        name: 'Flipbook',
        category: 'uv',
        description: 'Animated sprite sheet UV',
        inputs: [
            { name: 'UV', type: 'vec2', default: [0, 0] },
            { name: 'Width', type: 'float', default: 4 },
            { name: 'Height', type: 'float', default: 4 },
            { name: 'Tile', type: 'float', default: 0 },
        ],
        outputs: [{ name: 'Out', type: 'vec2' }],
        glsl: `
      float tile = floor({{input.Tile}});
      float invWidth = 1.0 / {{input.Width}};
      float invHeight = 1.0 / {{input.Height}};
      float tileX = mod(tile, {{input.Width}});
      float tileY = floor(tile * invWidth);
      vec2 {{output.Out}} = ({{input.UV}} + vec2(tileX, tileY)) * vec2(invWidth, invHeight);
    `,
    },
    {
        id: 'polar',
        name: 'Polar Coordinates',
        category: 'uv',
        inputs: [
            { name: 'UV', type: 'vec2', default: [0, 0] },
            { name: 'Center', type: 'vec2', default: [0.5, 0.5] },
            { name: 'RadialScale', type: 'float', default: 1 },
            { name: 'LengthScale', type: 'float', default: 1 },
        ],
        outputs: [{ name: 'Out', type: 'vec2' }],
        glsl: `
      vec2 delta = {{input.UV}} - {{input.Center}};
      float radius = length(delta) * 2.0 * {{input.LengthScale}};
      float angle = atan(delta.y, delta.x) / 6.283185 + 0.5;
      vec2 {{output.Out}} = vec2(radius, angle * {{input.RadialScale}});
    `,
    },
    {
        id: 'spherize',
        name: 'Spherize',
        category: 'uv',
        inputs: [
            { name: 'UV', type: 'vec2', default: [0, 0] },
            { name: 'Center', type: 'vec2', default: [0.5, 0.5] },
            { name: 'Strength', type: 'float', default: 0.5 },
            { name: 'Offset', type: 'vec2', default: [0, 0] },
        ],
        outputs: [{ name: 'Out', type: 'vec2' }],
        glsl: `
      vec2 delta = {{input.UV}} - {{input.Center}};
      float dist = length(delta);
      float factor = 1.0 + dist * dist * {{input.Strength}};
      vec2 {{output.Out}} = {{input.Center}} + delta / factor + {{input.Offset}};
    `,
    },
    {
        id: 'twirl',
        name: 'Twirl',
        category: 'uv',
        inputs: [
            { name: 'UV', type: 'vec2', default: [0, 0] },
            { name: 'Center', type: 'vec2', default: [0.5, 0.5] },
            { name: 'Strength', type: 'float', default: 1 },
            { name: 'Offset', type: 'vec2', default: [0, 0] },
        ],
        outputs: [{ name: 'Out', type: 'vec2' }],
        glsl: `
      vec2 delta = {{input.UV}} - {{input.Center}};
      float dist = length(delta);
      float angle = {{input.Strength}} * dist;
      float c = cos(angle);
      float s = sin(angle);
      vec2 {{output.Out}} = vec2(delta.x * c - delta.y * s, delta.x * s + delta.y * c) + {{input.Center}} + {{input.Offset}};
    `,
    },
    {
        id: 'parallax',
        name: 'Parallax Mapping',
        category: 'uv',
        inputs: [
            { name: 'UV', type: 'vec2', default: [0, 0] },
            { name: 'Height', type: 'float', default: 0.5 },
            { name: 'ViewDir', type: 'vec3' },
            { name: 'Amplitude', type: 'float', default: 0.05 },
        ],
        outputs: [{ name: 'Out', type: 'vec2' }],
        glsl: `
      float h = {{input.Height}};
      vec2 {{output.Out}} = {{input.UV}} - {{input.ViewDir}}.xy * (h * {{input.Amplitude}});
    `,
    },
    // ─── Color Nodes ─────────────────────────────────────────
    {
        id: 'blend',
        name: 'Blend',
        category: 'color',
        description: 'Blend two colors',
        inputs: [
            { name: 'Base', type: 'vec4', default: [0, 0, 0, 1] },
            { name: 'Blend', type: 'vec4', default: [1, 1, 1, 1] },
            { name: 'Opacity', type: 'float', default: 1 },
        ],
        outputs: [{ name: 'Out', type: 'vec4' }],
        properties: [
            {
                name: 'Mode',
                type: 'dropdown',
                default: 'normal',
                options: [
                    'normal',
                    'multiply',
                    'screen',
                    'overlay',
                    'add',
                    'subtract',
                    'difference',
                    'darken',
                    'lighten',
                ],
            },
        ],
        glsl: `
      vec4 {{output.Out}};
      // Blend mode logic here based on prop.Mode
      {{output.Out}} = mix({{input.Base}}, {{input.Blend}}, {{input.Opacity}});
    `,
    },
    {
        id: 'contrast',
        name: 'Contrast',
        category: 'color',
        inputs: [
            { name: 'In', type: 'vec3', default: [0.5, 0.5, 0.5] },
            { name: 'Contrast', type: 'float', default: 1 },
        ],
        outputs: [{ name: 'Out', type: 'vec3' }],
        glsl: `vec3 {{output.Out}} = ({{input.In}} - 0.5) * {{input.Contrast}} + 0.5;`,
    },
    {
        id: 'saturation',
        name: 'Saturation',
        category: 'color',
        inputs: [
            { name: 'In', type: 'vec3', default: [0.5, 0.5, 0.5] },
            { name: 'Saturation', type: 'float', default: 1 },
        ],
        outputs: [{ name: 'Out', type: 'vec3' }],
        glsl: `
      float luma = dot({{input.In}}, vec3(0.2126, 0.7152, 0.0722));
      vec3 {{output.Out}} = mix(vec3(luma), {{input.In}}, {{input.Saturation}});
    `,
    },
    {
        id: 'hue',
        name: 'Hue',
        category: 'color',
        inputs: [
            { name: 'In', type: 'vec3', default: [1, 0, 0] },
            { name: 'Offset', type: 'float', default: 0 },
        ],
        outputs: [{ name: 'Out', type: 'vec3' }],
        glsl: `
      // RGB to HSV
      vec3 hsvIn = {{input.In}};
      float maxC = max(max(hsvIn.r, hsvIn.g), hsvIn.b);
      float minC = min(min(hsvIn.r, hsvIn.g), hsvIn.b);
      float delta = maxC - minC;
      float h = 0.0;
      if (delta > 0.0) {
        if (maxC == hsvIn.r) h = mod((hsvIn.g - hsvIn.b) / delta, 6.0);
        else if (maxC == hsvIn.g) h = (hsvIn.b - hsvIn.r) / delta + 2.0;
        else h = (hsvIn.r - hsvIn.g) / delta + 4.0;
        h /= 6.0;
      }
      float s = maxC > 0.0 ? delta / maxC : 0.0;
      float v = maxC;
      
      // Shift hue
      h = fract(h + {{input.Offset}});
      
      // HSV to RGB
      float i = floor(h * 6.0);
      float f = h * 6.0 - i;
      float p = v * (1.0 - s);
      float q = v * (1.0 - f * s);
      float t = v * (1.0 - (1.0 - f) * s);
      
      vec3 {{output.Out}};
      int im = int(mod(i, 6.0));
      if (im == 0) {{output.Out}} = vec3(v, t, p);
      else if (im == 1) {{output.Out}} = vec3(q, v, p);
      else if (im == 2) {{output.Out}} = vec3(p, v, t);
      else if (im == 3) {{output.Out}} = vec3(p, q, v);
      else if (im == 4) {{output.Out}} = vec3(t, p, v);
      else {{output.Out}} = vec3(v, p, q);
    `,
    },
    {
        id: 'brightness',
        name: 'Brightness',
        category: 'color',
        inputs: [
            { name: 'In', type: 'vec3', default: [0.5, 0.5, 0.5] },
            { name: 'Brightness', type: 'float', default: 0 },
        ],
        outputs: [{ name: 'Out', type: 'vec3' }],
        glsl: `vec3 {{output.Out}} = {{input.In}} + {{input.Brightness}};`,
    },
    {
        id: 'whiteBalance',
        name: 'White Balance',
        category: 'color',
        inputs: [
            { name: 'In', type: 'vec3', default: [1, 1, 1] },
            { name: 'Temperature', type: 'float', default: 0 },
            { name: 'Tint', type: 'float', default: 0 },
        ],
        outputs: [{ name: 'Out', type: 'vec3' }],
        glsl: `
      float t = {{input.Temperature}};
      float ti = {{input.Tint}};
      vec3 {{output.Out}} = {{input.In}} + vec3(t, ti, -t);
    `,
    },
    {
        id: 'channelMixer',
        name: 'Channel Mixer',
        category: 'color',
        inputs: [{ name: 'In', type: 'vec3', default: [1, 1, 1] }],
        outputs: [{ name: 'Out', type: 'vec3' }],
        properties: [
            { name: 'Red', type: 'vec3', default: [1, 0, 0] },
            { name: 'Green', type: 'vec3', default: [0, 1, 0] },
            { name: 'Blue', type: 'vec3', default: [0, 0, 1] },
        ],
        glsl: `
      vec3 {{output.Out}} = vec3(
        dot({{input.In}}, vec3({{prop.Red}})),
        dot({{input.In}}, vec3({{prop.Green}})),
        dot({{input.In}}, vec3({{prop.Blue}}))
      );
    `,
    },
    {
        id: 'colorGrade',
        name: 'Color Grade',
        category: 'color',
        description: 'Lift-Gamma-Gain color grading',
        inputs: [{ name: 'In', type: 'vec3', default: [0.5, 0.5, 0.5] }],
        outputs: [{ name: 'Out', type: 'vec3' }],
        properties: [
            { name: 'Lift', type: 'color', default: [0, 0, 0, 1] },
            { name: 'Gamma', type: 'color', default: [0.5, 0.5, 0.5, 1] },
            { name: 'Gain', type: 'color', default: [1, 1, 1, 1] },
        ],
        glsl: `
      vec3 lift = vec3({{prop.Lift}}.rgb);
      vec3 gamma = vec3({{prop.Gamma}}.rgb);
      vec3 gain = vec3({{prop.Gain}}.rgb);
      vec3 col = {{input.In}} * gain + lift;
      vec3 {{output.Out}} = pow(col, 1.0 / (gamma * 2.0));
    `,
    },
    // ─── Lighting Nodes ──────────────────────────────────────
    {
        id: 'lambertDiffuse',
        name: 'Lambert Diffuse',
        category: 'lighting',
        inputs: [
            { name: 'Normal', type: 'vec3' },
            { name: 'LightDir', type: 'vec3' },
            { name: 'LightColor', type: 'vec3', default: [1, 1, 1] },
        ],
        outputs: [{ name: 'Out', type: 'vec3' }],
        glsl: `
      float ndotl = max(dot({{input.Normal}}, {{input.LightDir}}), 0.0);
      vec3 {{output.Out}} = {{input.LightColor}} * ndotl;
    `,
    },
    {
        id: 'blinnPhongSpecular',
        name: 'Blinn-Phong Specular',
        category: 'lighting',
        inputs: [
            { name: 'Normal', type: 'vec3' },
            { name: 'ViewDir', type: 'vec3' },
            { name: 'LightDir', type: 'vec3' },
            { name: 'Smoothness', type: 'float', default: 0.5 },
        ],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `
      vec3 halfDir = normalize({{input.LightDir}} + {{input.ViewDir}});
      float ndoth = max(dot({{input.Normal}}, halfDir), 0.0);
      float spec = pow(ndoth, exp2(10.0 * {{input.Smoothness}} + 1.0));
      float {{output.Out}} = spec;
    `,
    },
    {
        id: 'fresnel',
        name: 'Fresnel',
        category: 'lighting',
        inputs: [
            { name: 'Normal', type: 'vec3' },
            { name: 'ViewDir', type: 'vec3' },
            { name: 'Power', type: 'float', default: 5 },
        ],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `
      float ndotv = max(dot({{input.Normal}}, {{input.ViewDir}}), 0.0);
      float {{output.Out}} = pow(1.0 - ndotv, {{input.Power}});
    `,
    },
    {
        id: 'ambientOcclusion',
        name: 'Ambient Occlusion',
        category: 'lighting',
        inputs: [
            { name: 'Color', type: 'vec3' },
            { name: 'AO', type: 'float', default: 1 },
            { name: 'Intensity', type: 'float', default: 1 },
        ],
        outputs: [{ name: 'Out', type: 'vec3' }],
        glsl: `vec3 {{output.Out}} = {{input.Color}} * mix(1.0, {{input.AO}}, {{input.Intensity}});`,
    },
    // ─── Procedural Nodes ────────────────────────────────────
    {
        id: 'noise',
        name: 'Simple Noise',
        category: 'procedural',
        inputs: [
            { name: 'UV', type: 'vec2', default: [0, 0] },
            { name: 'Scale', type: 'float', default: 10 },
        ],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `
      vec2 p = {{input.UV}} * {{input.Scale}};
      float {{output.Out}} = fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);
    `,
    },
    {
        id: 'gradient',
        name: 'Gradient Noise',
        category: 'procedural',
        inputs: [
            { name: 'UV', type: 'vec2', default: [0, 0] },
            { name: 'Scale', type: 'float', default: 5 },
        ],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `
      // Simple gradient noise approximation
      vec2 p = {{input.UV}} * {{input.Scale}};
      vec2 i = floor(p);
      vec2 f = fract(p);
      f = f * f * (3.0 - 2.0 * f);
      
      float a = fract(sin(dot(i, vec2(12.9898, 78.233))) * 43758.5453);
      float b = fract(sin(dot(i + vec2(1.0, 0.0), vec2(12.9898, 78.233))) * 43758.5453);
      float c = fract(sin(dot(i + vec2(0.0, 1.0), vec2(12.9898, 78.233))) * 43758.5453);
      float d = fract(sin(dot(i + vec2(1.0, 1.0), vec2(12.9898, 78.233))) * 43758.5453);
      
      float {{output.Out}} = mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
    `,
    },
    {
        id: 'voronoi',
        name: 'Voronoi',
        category: 'procedural',
        inputs: [
            { name: 'UV', type: 'vec2', default: [0, 0] },
            { name: 'Scale', type: 'float', default: 5 },
            { name: 'Angle', type: 'float', default: 0 },
        ],
        outputs: [
            { name: 'Out', type: 'float' },
            { name: 'Cells', type: 'float' },
        ],
        glsl: `
      vec2 p = {{input.UV}} * {{input.Scale}};
      vec2 n = floor(p);
      vec2 f = fract(p);
      
      float minDist = 1.0;
      vec2 minCell = vec2(0.0);
      
      for (int j = -1; j <= 1; j++) {
        for (int i = -1; i <= 1; i++) {
          vec2 g = vec2(float(i), float(j));
          vec2 o = fract(sin(vec2(dot(n + g, vec2(12.9898, 78.233)), dot(n + g, vec2(23.4321, 14.5678)))) * 43758.5453);
          vec2 r = g + o - f;
          float d = dot(r, r);
          if (d < minDist) {
            minDist = d;
            minCell = n + g + o;
          }
        }
      }
      
      float {{output.Out}} = minDist;
      float {{output.Cells}} = fract(sin(dot(minCell, vec2(12.9898, 78.233))) * 43758.5453);
    `,
    },
    {
        id: 'checkerboard',
        name: 'Checkerboard',
        category: 'procedural',
        inputs: [
            { name: 'UV', type: 'vec2', default: [0, 0] },
            { name: 'Frequency', type: 'vec2', default: [4, 4] },
        ],
        outputs: [{ name: 'Out', type: 'float' }],
        properties: [
            { name: 'ColorA', type: 'color', default: [1, 1, 1, 1] },
            { name: 'ColorB', type: 'color', default: [0, 0, 0, 1] },
        ],
        glsl: `
      vec2 uv = {{input.UV}} * {{input.Frequency}};
      float c = mod(floor(uv.x) + floor(uv.y), 2.0);
      float {{output.Out}} = c;
    `,
    },
    {
        id: 'ellipse',
        name: 'Ellipse',
        category: 'procedural',
        inputs: [
            { name: 'UV', type: 'vec2', default: [0, 0] },
            { name: 'Width', type: 'float', default: 0.5 },
            { name: 'Height', type: 'float', default: 0.5 },
        ],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `
      vec2 d = {{input.UV}} - 0.5;
      d.x /= {{input.Width}};
      d.y /= {{input.Height}};
      float {{output.Out}} = 1.0 - smoothstep(0.5, 0.51, length(d));
    `,
    },
    {
        id: 'rectangle',
        name: 'Rectangle',
        category: 'procedural',
        inputs: [
            { name: 'UV', type: 'vec2', default: [0, 0] },
            { name: 'Width', type: 'float', default: 0.5 },
            { name: 'Height', type: 'float', default: 0.5 },
        ],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `
      vec2 d = abs({{input.UV}} - 0.5);
      float {{output.Out}} = 1.0 - smoothstep(0.0, 0.01, max(d.x - {{input.Width}} * 0.5, d.y - {{input.Height}} * 0.5));
    `,
    },
    {
        id: 'roundedRectangle',
        name: 'Rounded Rectangle',
        category: 'procedural',
        inputs: [
            { name: 'UV', type: 'vec2', default: [0, 0] },
            { name: 'Width', type: 'float', default: 0.5 },
            { name: 'Height', type: 'float', default: 0.5 },
            { name: 'Radius', type: 'float', default: 0.1 },
        ],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `
      vec2 d = abs({{input.UV}} - 0.5) - vec2({{input.Width}}, {{input.Height}}) * 0.5 + {{input.Radius}};
      float {{output.Out}} = 1.0 - smoothstep(0.0, 0.01, length(max(d, 0.0)) - {{input.Radius}});
    `,
    },
    {
        id: 'polygon',
        name: 'Polygon',
        category: 'procedural',
        inputs: [
            { name: 'UV', type: 'vec2', default: [0, 0] },
            { name: 'Sides', type: 'float', default: 6 },
            { name: 'Width', type: 'float', default: 0.5 },
            { name: 'Height', type: 'float', default: 0.5 },
        ],
        outputs: [{ name: 'Out', type: 'float' }],
        glsl: `
      vec2 d = {{input.UV}} - 0.5;
      float angle = atan(d.y, d.x);
      float slice = 6.283185 / {{input.Sides}};
      float dist = cos(floor(0.5 + angle / slice) * slice - angle) * length(d * 2.0 / vec2({{input.Width}}, {{input.Height}}));
      float {{output.Out}} = 1.0 - smoothstep(1.0, 1.01, dist);
    `,
    },
    // ─── Output Nodes ────────────────────────────────────────
    {
        id: 'pbrMasterNode',
        name: 'PBR Master',
        category: 'output',
        description: 'Physically Based Rendering output',
        isMaster: true,
        inputs: [
            { name: 'Albedo', type: 'vec3', default: [1, 1, 1] },
            { name: 'Normal', type: 'vec3', default: [0, 0, 1] },
            { name: 'Metallic', type: 'float', default: 0 },
            { name: 'Smoothness', type: 'float', default: 0.5 },
            { name: 'AO', type: 'float', default: 1 },
            { name: 'Emission', type: 'vec3', default: [0, 0, 0] },
            { name: 'Alpha', type: 'float', default: 1 },
            { name: 'AlphaClip', type: 'float', default: 0 },
        ],
        outputs: [],
        glsl: `
      // PBR output
      gl_FragColor = vec4({{input.Albedo}}, {{input.Alpha}});
      
      // Normal output
      normalOutput = {{input.Normal}};
      
      // Material properties
      metallicOutput = {{input.Metallic}};
      smoothnessOutput = {{input.Smoothness}};
      aoOutput = {{input.AO}};
      emissionOutput = {{input.Emission}};
      
      // Alpha clip
      if ({{input.Alpha}} < {{input.AlphaClip}}) discard;
    `,
    },
    {
        id: 'unlitMasterNode',
        name: 'Unlit Master',
        category: 'output',
        description: 'Unlit/emissive output',
        isMaster: true,
        inputs: [
            { name: 'Color', type: 'vec3', default: [1, 1, 1] },
            { name: 'Alpha', type: 'float', default: 1 },
            { name: 'AlphaClip', type: 'float', default: 0 },
        ],
        outputs: [],
        glsl: `
      gl_FragColor = vec4({{input.Color}}, {{input.Alpha}});
      if ({{input.Alpha}} < {{input.AlphaClip}}) discard;
    `,
    },
    {
        id: 'vertexMasterNode',
        name: 'Vertex Master',
        category: 'output',
        description: 'Vertex shader output',
        isMaster: true,
        stage: 'vertex',
        inputs: [
            { name: 'Position', type: 'vec3' },
            { name: 'Normal', type: 'vec3' },
            { name: 'Tangent', type: 'vec3' },
        ],
        outputs: [],
        glsl: `
      gl_Position = u_projectionMatrix * u_viewMatrix * u_modelMatrix * vec4({{input.Position}}, 1.0);
    `,
    },
];
class ShaderCodeGenerator {
    constructor(graph) {
        this.nodeLibrary = new Map();
        this.generatedCode = new Map();
        this.uniforms = [];
        this.defines = [];
        this.nodeCounter = 0;
        this.graph = graph;
        // Build node library lookup
        for (const nodeDef of SHADER_NODE_LIBRARY) {
            this.nodeLibrary.set(nodeDef.id, nodeDef);
        }
    }
    generate() {
        this.generatedCode.clear();
        this.uniforms = [];
        this.defines = [];
        this.nodeCounter = 0;
        // Find master node
        const masterNode = this.graph.nodes.find((n) => {
            const def = this.nodeLibrary.get(n.type);
            return def === null || def === void 0 ? void 0 : def.isMaster;
        });
        if (!masterNode) {
            throw new Error('No master node found in shader graph');
        }
        // Generate from master node backwards
        const fragmentCode = this.generateNode(masterNode.id);
        // Build complete shaders
        const vertex = this.buildVertexShader();
        const fragment = this.buildFragmentShader(fragmentCode);
        return {
            vertex,
            fragment,
            uniforms: this.uniforms,
            defines: this.defines,
        };
    }
    generateNode(nodeId) {
        var _a;
        // Check if already generated
        if (this.generatedCode.has(nodeId)) {
            return this.generatedCode.get(nodeId);
        }
        const node = this.graph.nodes.find((n) => n.id === nodeId);
        if (!node)
            return '';
        const nodeDef = this.nodeLibrary.get(node.type);
        if (!nodeDef)
            return '';
        // Generate dependencies first
        const inputConnections = this.graph.connections.filter((c) => c.to.nodeId === nodeId);
        const inputCode = [];
        const inputVars = {};
        for (const conn of inputConnections) {
            const sourceCode = this.generateNode(conn.from.nodeId);
            inputCode.push(sourceCode);
            inputVars[conn.to.portName] = `node_${conn.from.nodeId}_${conn.from.portName}`;
        }
        // Generate this node's code
        let code = nodeDef.glsl || '';
        const varPrefix = `node_${nodeId}`;
        // Replace input placeholders
        for (const input of nodeDef.inputs) {
            const varName = inputVars[input.name] || this.defaultValue(input.type, input.default);
            code = code.replace(new RegExp(`\\{\\{input\\.${input.name}\\}\\}`, 'g'), varName);
        }
        // Replace output placeholders
        for (const output of nodeDef.outputs) {
            code = code.replace(new RegExp(`\\{\\{output\\.${output.name}\\}\\}`, 'g'), `${varPrefix}_${output.name}`);
        }
        // Replace property placeholders
        for (const prop of nodeDef.properties || []) {
            const value = (_a = node.properties[prop.name]) !== null && _a !== void 0 ? _a : prop.default;
            code = code.replace(new RegExp(`\\{\\{prop\\.${prop.name}\\}\\}`, 'g'), this.formatValue(value, prop.type));
            // Add uniform if it's a texture
            if (prop.type === 'texture') {
                this.uniforms.push({
                    name: `${varPrefix}_${prop.name}`,
                    type: 'sampler2D',
                });
            }
        }
        const fullCode = inputCode.join('\n') + '\n' + code;
        this.generatedCode.set(nodeId, fullCode);
        return fullCode;
    }
    defaultValue(type, defaultVal) {
        if (defaultVal !== undefined) {
            return this.formatValue(defaultVal, type);
        }
        switch (type) {
            case 'float':
                return '0.0';
            case 'vec2':
                return 'vec2(0.0)';
            case 'vec3':
                return 'vec3(0.0)';
            case 'vec4':
                return 'vec4(0.0)';
            case 'int':
                return '0';
            case 'bool':
                return 'false';
            default:
                return '0.0';
        }
    }
    formatValue(value, type) {
        if (Array.isArray(value)) {
            return `vec${value.length}(${value.map((v) => v.toFixed(6)).join(', ')})`;
        }
        if (typeof value === 'number') {
            return value.toFixed(6);
        }
        if (typeof value === 'boolean') {
            return value ? 'true' : 'false';
        }
        return String(value);
    }
    buildVertexShader() {
        const precision = this.graph.settings.precision;
        return `#version 300 es
precision ${precision} float;

// Attributes
in vec3 a_position;
in vec3 a_normal;
in vec2 a_uv0;
in vec2 a_uv1;
in vec4 a_color;
in vec3 a_tangent;

// Uniforms
uniform mat4 u_modelMatrix;
uniform mat4 u_viewMatrix;
uniform mat4 u_projectionMatrix;
uniform mat4 u_normalMatrix;
uniform mat4 u_worldToObject;

// Varyings
out vec3 v_worldPosition;
out vec3 v_objectPosition;
out vec3 v_viewPosition;
out vec3 v_worldNormal;
out vec3 v_objectNormal;
out vec3 v_viewNormal;
out vec3 v_tangent;
out vec3 v_tangentNormal;
out vec2 v_uv0;
out vec2 v_uv1;
out vec2 v_uv2;
out vec2 v_uv3;
out vec4 v_color;

void main() {
  vec4 worldPos = u_modelMatrix * vec4(a_position, 1.0);
  vec4 viewPos = u_viewMatrix * worldPos;
  
  v_worldPosition = worldPos.xyz;
  v_objectPosition = a_position;
  v_viewPosition = viewPos.xyz;
  
  v_worldNormal = mat3(u_normalMatrix) * a_normal;
  v_objectNormal = a_normal;
  v_viewNormal = mat3(u_viewMatrix) * v_worldNormal;
  
  v_tangent = mat3(u_normalMatrix) * a_tangent;
  v_tangentNormal = vec3(0.0, 0.0, 1.0);
  
  v_uv0 = a_uv0;
  v_uv1 = a_uv1;
  v_uv2 = a_uv0;
  v_uv3 = a_uv0;
  v_color = a_color;
  
  gl_Position = u_projectionMatrix * viewPos;
}
`;
    }
    buildFragmentShader(mainCode) {
        const precision = this.graph.settings.precision;
        const uniformDecls = this.uniforms.map((u) => `uniform ${u.type} ${u.name};`).join('\n');
        return `#version 300 es
precision ${precision} float;

// Varyings
in vec3 v_worldPosition;
in vec3 v_objectPosition;
in vec3 v_viewPosition;
in vec3 v_worldNormal;
in vec3 v_objectNormal;
in vec3 v_viewNormal;
in vec3 v_tangent;
in vec3 v_tangentNormal;
in vec2 v_uv0;
in vec2 v_uv1;
in vec2 v_uv2;
in vec2 v_uv3;
in vec4 v_color;

// Global uniforms
uniform float u_time;
uniform float u_deltaTime;
uniform float u_smoothDeltaTime;
uniform vec3 u_cameraPosition;
uniform vec2 u_resolution;

// Material uniforms
${uniformDecls}

// Outputs
out vec4 fragColor;

void main() {
${mainCode}
  fragColor = gl_FragColor;
}
`;
    }
}
function createShaderGraphEditorState(graph) {
    return {
        graph: graph !== null && graph !== void 0 ? graph : createEmptyShaderGraph(),
        selectedNodes: [],
        hoveredNode: null,
        hoveredPort: null,
        draggingConnection: null,
        viewOffset: { x: 0, y: 0 },
        zoom: 1,
        previewEnabled: true,
        generatedShader: null,
        compileErrors: [],
    };
}
function createEmptyShaderGraph() {
    return {
        id: `shader_${Date.now()}`,
        name: 'New Shader',
        target: 'glsl',
        stage: 'fragment',
        nodes: [
            {
                id: 'master',
                type: 'pbrMasterNode',
                position: { x: 600, y: 200 },
                properties: {},
            },
        ],
        connections: [],
        properties: [],
        settings: {
            precision: 'highp',
            blendMode: 'opaque',
            cullMode: 'back',
            depthWrite: true,
            depthTest: 'lequal',
            castShadows: true,
            receiveShadows: true,
            doubleSided: false,
            surfaceType: 'opaque',
            renderQueue: 2000,
        },
    };
}
// ============================================================
// Factory Functions
// ============================================================
function createShaderGraph(name, target = 'glsl') {
    const graph = createEmptyShaderGraph();
    graph.name = name;
    graph.target = target;
    return graph;
}
function generateShader(graph) {
    const generator = new ShaderCodeGenerator(graph);
    return generator.generate();
}
function getNodeLibrary() {
    return SHADER_NODE_LIBRARY;
}
function getNodesByCategory(category) {
    return SHADER_NODE_LIBRARY.filter((n) => n.category === category);
}
function findNodeDef(nodeType) {
    return SHADER_NODE_LIBRARY.find((n) => n.id === nodeType);
}

exports.SHADER_NODE_LIBRARY = SHADER_NODE_LIBRARY;
exports.ShaderCodeGenerator = ShaderCodeGenerator;
exports.createEmptyShaderGraph = createEmptyShaderGraph;
exports.createShaderGraph = createShaderGraph;
exports.createShaderGraphEditorState = createShaderGraphEditorState;
exports.findNodeDef = findNodeDef;
exports.generateShader = generateShader;
exports.getNodeLibrary = getNodeLibrary;
exports.getNodesByCategory = getNodesByCategory;
//# sourceMappingURL=ShaderGraph.js.map
