import {
  BufferAttribute,
  BufferGeometry,
  Color,
  GLSL1,
  GLSL3,
  Mesh,
  Object3D,
  RawShaderMaterial,
  Vector2,
  type WebGLRenderer,
} from "three";

import AssetsProvider from "@/assets/utils/AssetsProvider";

import { fs, fs_es2, vs, vs_es2 } from "../shaders/core/ShaderGPUGeometry";
import type ISimulationEntity from "../simulation/interface/ISimulationEntity";

class TextureGeometry extends Object3D {
  private mesh: Mesh;
  public material: RawShaderMaterial;

  constructor(
    private simulator: ISimulationEntity,
    private renderer: WebGLRenderer,
  ) {
    super();

    const i2uvVector = new Vector2(0, 0);
    const i2UVBuffer = new BufferAttribute(
      new Float32Array(simulator.simulationCount * 2),
      2,
    );
    for (let i = 0; i < simulator.simulationCount; i++) {
      i2uvVector.set(
        (i % simulator.fboDimensions[0]) / simulator.fboDimensions[0],
        Math.floor(i / simulator.fboDimensions[0]) / simulator.fboDimensions[1],
      );
      i2UVBuffer.setXY(i, i2uvVector.x, i2uvVector.y);
    }

    const geometry = new BufferGeometry();
    geometry.setAttribute(
      "position",
      new BufferAttribute(new Float32Array(simulator.simulationCount * 3), 3),
    );
    geometry.setAttribute("i2uv", i2UVBuffer);

    this.material = new RawShaderMaterial({
      vertexShader: this.renderer.capabilities.isWebGL2 ? vs : vs_es2,
      fragmentShader: this.renderer.capabilities.isWebGL2 ? fs : fs_es2,
      uniforms: {
        uPositionsTexture: { value: simulator.getPositionsTexture() },
        uNormalsTexture: { value: simulator.getNormalsTexture() },
        matcap: { value: AssetsProvider.getAsset("heartMatcap").asset },
        diffuseColor: { value: new Color("#ffffff") },
      },
      depthWrite: false,
      glslVersion: this.renderer.capabilities.isWebGL2 ? GLSL3 : GLSL1,
    });
    this.mesh = new Mesh(geometry, this.material);
    this.mesh.renderOrder = -1;
    // this.mesh.rotation.y = Math.PI;
    this.add(this.mesh);
  }

  public update(dt: number) {
    this.material.uniforms.uPositionsTexture.value =
      this.simulator.getPositionsTexture();
  }
}

export default TextureGeometry;
