import shaderSource from './shaders/shader.wgsl?raw';
import { QuadGeometry } from './geometry';
import { Texture } from './texture';
class Renderer{
  private device! : GPUDevice;
  private context! : GPUCanvasContext;
  private pipeline! : GPURenderPipeline;
  private postitionBuffer! : GPUBuffer;
  private colorBuffer! : GPUBuffer;
  private texCoordBuffer! : GPUBuffer;
  private textureBindGroup! : GPUBindGroup;

  private testTexture! : Texture;

  public async initialize()
  {
    const canvas = document.getElementById('canvas') as HTMLCanvasElement;
    
    this.context = canvas.getContext('webgpu')!;

    if(!this.context){
      alert('WebGPU not supported');
      return;
    }

    const adapter = await navigator.gpu.requestAdapter(
      {
        powerPreference: 'low-power'
      }
    );

    if(!adapter)
      {
        alert('No WebGPU adapter found');
        return;
      }

    this.device = await adapter.requestDevice()

    this.context.configure({
      device: this.device,
      format: navigator.gpu.getPreferredCanvasFormat()
    })



    this.testTexture = await Texture.createTextureFromUrl(this.device, 'assets/UVEditorColorGrid.png');
    this.prepareModel();



    const geometry = new QuadGeometry();
    
    this.postitionBuffer = this.CreateBuffer(new Float32Array(geometry.positions));
    this.colorBuffer = this.CreateBuffer(new Float32Array(geometry.colors));
    this.texCoordBuffer = this.CreateBuffer(new Float32Array(geometry.texCoords));
  }

  private CreateBuffer(data: Float32Array)
  {
    const buffer = this.device.createBuffer({
      size: data.byteLength,
      usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
      mappedAtCreation: true
    });

    new Float32Array(buffer.getMappedRange()).set(data);
    buffer.unmap();


    return  buffer;
  }

    
  private prepareModel() {
    const shaderModule = this.device.createShaderModule({code: shaderSource});

    const postitionBufferLayout : GPUVertexBufferLayout =
    {
      arrayStride: 2 * Float32Array.BYTES_PER_ELEMENT,
      attributes: [
        {
          shaderLocation: 0,
          offset: 0,
          format: 'float32x2'
        }  
      ],
      stepMode: 'vertex'
    }


    const colorBufferLayout : GPUVertexBufferLayout =
    {
      arrayStride: 3 * Float32Array.BYTES_PER_ELEMENT,
      attributes: [
        {
          shaderLocation: 1,
          offset: 0,
          format: 'float32x3'
        }]
    }

    const textureCoordBufferLayout : GPUVertexBufferLayout =
    {
      arrayStride: 2 * Float32Array.BYTES_PER_ELEMENT,
      attributes: [
        {
          shaderLocation: 2,
          offset: 0,
          format: 'float32x2'
        }],
        stepMode: 'vertex'
    }

    const vertexState: GPUVertexState = {
      module : shaderModule,
      entryPoint: "VertexMain",
      buffers:
      [
        postitionBufferLayout, 
        colorBufferLayout,
        textureCoordBufferLayout
      ]  
    }

    const fragmentState: GPUFragmentState = {
      module : shaderModule,
      entryPoint: "FragmentMain",
      targets: [
        {
          format: navigator.gpu.getPreferredCanvasFormat(),
          blend: {
            color: {
              srcFactor: 'one',
              dstFactor: 'zero',
              operation: 'add'
            },
            alpha: {
              srcFactor: 'one',
              dstFactor: 'zero',
              operation: 'add'
            }
          }
        }
      ]
    }

    const textureBindGroupLayout = this.device.createBindGroupLayout({
      entries: [
        {
          binding: 0,
          visibility: GPUShaderStage.FRAGMENT,
          sampler: {}
        },
        {
          binding: 1,
          visibility: GPUShaderStage.FRAGMENT,
          texture: {}
        }
      ]
    });

    const pipelineLayout = this.device.createPipelineLayout({
      bindGroupLayouts: [
        textureBindGroupLayout
      ]
    });

    this.textureBindGroup = this.device.createBindGroup({
      layout: textureBindGroupLayout,
      entries: [
        {
          binding: 0,
          resource: this.testTexture.sampler
        },
        {
          binding: 1,
          resource: this.testTexture.texture.createView()
        }
      ]
    });

    this.pipeline = this.device.createRenderPipeline({
      vertex: vertexState,
      fragment: fragmentState,
      primitive:{
        topology: 'triangle-list',
      },
      layout: pipelineLayout
    });


  }


  public draw()
  {
    const commandEncoder = this.device.createCommandEncoder();  
    const textureView = this.context.getCurrentTexture().createView();  

    const renderPassDescriptor:GPURenderPassDescriptor = {
      colorAttachments:[{
        view: textureView,
        clearValue: {r: 0, g: 0, b: 0, a: 1},
        loadOp: 'clear',
        storeOp:'store'
      }]
      }  

    const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor); 


    //Draw here
    passEncoder.setPipeline(this.pipeline );
    passEncoder.setVertexBuffer(0, this.postitionBuffer);
    passEncoder.setVertexBuffer(1, this.colorBuffer);
    passEncoder.setVertexBuffer(2, this.texCoordBuffer);
    passEncoder.setBindGroup(0, this.textureBindGroup)
    passEncoder.draw(6);

    passEncoder.end();

    this.device.queue.submit([commandEncoder.finish()]);
  }
  



}
  

const renderer = new Renderer();
renderer.initialize()
  .then(()=> renderer.draw());