import { BaseGenerator } from "./BaseGenerator.js";
import { DampCaveTextureGenerator } from "./textures/DampCaveTextureGenerator.js";
import { BioluminescentCaveWallTextureGenerator } from "./textures/BioluminescentCaveWallTextureGenerator.js";

/**
 * Generates organic, open-topped caves using Cellular Automata.
 * Designed to create "Canyons" rather than "Tunnels" for better camera visibility.
 * Integrates procedural textures for floors and walls with variation.
 */
export class CaveGenerator extends BaseGenerator {
  constructor(grid, seed) {
    super(grid, seed);

    this.floorGen = new DampCaveTextureGenerator(seed);
    this.wallGen = new BioluminescentCaveWallTextureGenerator(seed);

    // Container for assets generated by this biome logic.
    // We preload 10 variations of floors and walls.
    // The VoxelManager will need to read this 'palette' to create materials.
    this.generatedAssets = {
      palette: {}, // Maps Voxel ID -> Texture Asset Config
    };

    this.preloadTextures();
  }

  /**
   * Pre-generates texture variations so we don't regenerate them per voxel.
   * Assigns them to specific Voxel IDs.
   */
  preloadTextures() {
    const VARIATIONS = 10;
    const TEXTURE_SIZE = 128;

    // 1. Preload Wall Variations (IDs 100 - 109)
    for (let i = 0; i < VARIATIONS; i++) {
      // Vary the seed slightly for each texture to get unique noise patterns
      // but keep them consistent across re-runs of the same level seed
      const wallSeed = this.rng.next() * 5000 + i;

      // Generate the complex map (Diffuse + Emissive + Normal etc.)
      // We use a new generator instance or just rely on the internal noise
      // if the generator supports offset/variant seeding (assuming basic usage here)
      // Ideally generators support a 'variantSeed' param in generate calls.
      // Since BioluminescentCaveWallTextureGenerator creates a new Simplex(seed) in constructor,
      // we instantiate a new one for variation or update the generator to support methods.
      // For efficiency here, we assume the generators handle unique output if re-instantiated or parametrized.

      const tempWallGen = new BioluminescentCaveWallTextureGenerator(wallSeed);
      this.generatedAssets.palette[100 + i] =
        tempWallGen.generateWall(TEXTURE_SIZE);
    }

    // 2. Preload Floor Variations (IDs 200 - 209)
    for (let i = 0; i < VARIATIONS; i++) {
      const floorSeed = this.rng.next() * 10000 + i;
      // Using the variantSeed support added previously to generateFloor
      this.generatedAssets.palette[200 + i] = this.floorGen.generateFloor(
        TEXTURE_SIZE,
        floorSeed
      );
    }
  }

  generate(fillPercent = 0.45, iterations = 4) {
    // 1. Initial Noise
    for (let x = 0; x < this.width; x++) {
      for (let z = 0; z < this.depth; z++) {
        for (let y = 0; y < this.height; y++) {
          // RULE 1: Foundation is always solid.
          if (y === 0) {
            this.grid.setCell(x, y, z, 100); // Default to Wall 0
            continue;
          }

          // RULE 2: Sky is always clear.
          if (y >= this.height - 1) {
            this.grid.setCell(x, y, z, 0);
            continue;
          }

          // RULE 3: Noise for the middle layers.
          const isSolid = this.rng.chance(fillPercent);
          // We use a placeholder ID (1) for calculation, applied later
          this.grid.setCell(x, y, z, isSolid ? 1 : 0);
        }
      }
    }

    // 2. Smoothing Iterations (Calculates based on ID != 0)
    for (let i = 0; i < iterations; i++) {
      this.smooth();
    }

    // 3. Apply Texture/Material Logic
    // This replaces the placeholder IDs with our specific texture IDs (100-109, 200-209)
    this.applyTextures();

    // 4. Scatter Cover (Post-Texturing)
    // ID 10 = Destructible Cover (Mushrooms/Rocks)
    // 5% Density for open movement
    this.scatterCover(10, 0.05);
  }

  smooth() {
    const nextGrid = this.grid.clone();

    for (let x = 0; x < this.width; x++) {
      for (let z = 0; z < this.depth; z++) {
        for (let y = 1; y < this.height - 1; y++) {
          const neighbors = this.getSolidNeighbors(x, y, z);

          if (neighbors > 13) nextGrid.setCell(x, y, z, 1);
          else if (neighbors < 13) nextGrid.setCell(x, y, z, 0);
        }
      }
    }
    this.grid.cells = nextGrid.cells;
  }

  /**
   * Iterates through the grid to identify Floor and Wall voxels,
   * assigning randomized IDs from our preloaded palette.
   */
  applyTextures() {
    for (let x = 0; x < this.width; x++) {
      for (let z = 0; z < this.depth; z++) {
        for (let y = 0; y < this.height; y++) {
          const current = this.grid.getCell(x, y, z);
          const above = this.grid.getCell(x, y + 1, z);

          if (current !== 0) {
            if (above === 0) {
              // This is a Floor Surface
              // Pick random ID from 200 to 209
              const variant = this.rng.rangeInt(0, 9);
              this.grid.setCell(x, y, z, 200 + variant);
            } else {
              // This is a Wall
              // Pick random ID from 100 to 109
              const variant = this.rng.rangeInt(0, 9);
              this.grid.setCell(x, y, z, 100 + variant);
            }
          }
        }
      }
    }
  }
}
