// Enhanced Occlusion Layer Module for Foundry VTT

export function registerOcclusionConfig() {
	// Global Hook Registration
	function registerGlobalHooks() {
		const updateTriggers = ['canvasReady', 'canvasPan','canvasTokensRefresh', 'updateToken', 'controlToken', 'refreshToken', 'preUpdateScene', 'updateScene'];
		updateTriggers.forEach(hookName => {
			Hooks.on(hookName, () => { updateOcclusionLayer() });
		});
	}

	// Initial layer setup
	Hooks.once('ready', () => {
		if (canvas.ready)
			initializeOcclusionLayer();
	});

	// Initialize on canvas setup
	Hooks.on('canvasInit', () => {
		initializeOcclusionLayer();
	});

	// Reset on scene change
	Hooks.on('changeScene', () => {
		if (occlusionConfig.container) {
			canvas.stage.removeChild(occlusionConfig.container);
			occlusionConfig.container.destroy({ children: true });
			occlusionConfig.container = null;
			occlusionConfig.tokensLayer = null;
			occlusionConfig.initialized = false;
		}
	});

	// Start the module
	registerGlobalHooks();

  Hooks.once('ready', () => {
    TokenOcclusionManager.initialize();
  });
}












// Função principal para gerenciar a oclusão
class TokenOcclusionManager {
  static initialize() {
    // Registra o hook para atualizar a oclusão quando tokens são atualizados
    Hooks.on("refreshToken", (token) => {
      this.updateTokenOcclusion(token);
    });
  }

  static updateTokenOcclusion(token) {
    // Remove filtros antigos de oclusão
    token.mesh.filters = token.mesh.filters?.filter(f => !(f instanceof TokenOcclusionFilter)) || [];
        
    // Obtém todos os tiles da cena atual
    const tiles = canvas.tiles.placeables;
        
    // Verifica sobreposição com cada tile
    for (const tile of tiles) {
      if (this.checkTextureOverlap(token, tile)) {
        const occlusionFilter = new TokenOcclusionFilter(token, tile);
        token.mesh.filters.push(occlusionFilter);
      }
    }
  }

  static checkTextureOverlap(token, tile) {
    // Obtém os bounds das texturas
    const tokenTextureBounds = token.mesh.getBounds();
    const tileTextureBounds = tile.mesh.getBounds();
      
    // Verifica sobreposição usando os bounds das texturas
    const hasOverlap = !(
        tokenTextureBounds.right < tileTextureBounds.left ||
        tokenTextureBounds.left > tileTextureBounds.right ||
        tokenTextureBounds.bottom < tileTextureBounds.top ||
        tokenTextureBounds.top > tileTextureBounds.bottom
    );

    // Verifica se ambas as texturas têm conteúdo visível
    const tokenHasContent = token.texture && token.texture.valid;
    const tileHasContent = tile.texture && tile.texture.valid;

    return hasOverlap && tokenHasContent && tileHasContent;
  }
}















// Shader personalizado para detectar sobreposição de pixels
const occlusionShader2 = `
  precision mediump float;

  varying vec2 vTextureCoord;
  uniform sampler2D uSampler;
  uniform sampler2D tileTexture;
  uniform vec4 uColorMatrix;      // Color in RGBA normalized floats
  uniform vec4 tileArea;          // x, y, width, height em coordenadas UV

  void main(void) {
    vec4 tokenColor = texture2D(uSampler, vTextureCoord);
    vec4 tokenMixColor = tokenColor * uColorMatrix;


      
    // //Verifica se o pixel atual está dentro da área do tile
    // if (vTextureCoord.x >= tileArea.x && 
    //   vTextureCoord.x <= tileArea.x + tileArea.z &&
    //   vTextureCoord.y >= tileArea.y && 
    //   vTextureCoord.y <= tileArea.y + tileArea.w) {
          
    //   // Amostra o pixel do tile na mesma posição
    //   vec4 tileColor = texture2D(tileTexture, vTextureCoord);
          
    //   // Se o tile tiver alpha > 0, aplica o efeito de oclusão
    //   if (tokenColor.a > 0.1) {
    //     gl_FragColor = tokenMixColor; // Cor azul mantendo o alpha original
    //   } else {
    //     discard;
    //   }
    // }


    
    // Amostra o pixel do tile na mesma posição
    vec4 tileColor = texture2D(tileTexture, vTextureCoord);
          
    // Se o tile tiver alpha > 0, aplica o efeito de oclusão
    if (tokenColor.a > 0.1) {
      gl_FragColor = tokenMixColor; // Cor azul mantendo o alpha original
    } else {
      discard;
    }
  }
`;

const vertexShader = `
attribute vec2 aVertexPosition;
attribute vec2 aTextureCoord;

uniform mat3 projectionMatrix;
uniform mat3 otherMatrix;

varying vec2 vMaskCoord;
varying vec2 vTextureCoord;

void main(void) {
    gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
    vTextureCoord = aTextureCoord;
    vMaskCoord = (otherMatrix * vec3(aTextureCoord, 1.0)).xy;
    // vMaskCoord = aTextureCoord;
}
`

const occlusionShader = `
  varying vec2 vTextureCoord;
  uniform sampler2D uSampler;
  uniform sampler2D tileTexture;
  uniform vec4 tileArea;          // x, y, width, height em coordenadas UV
  
  void main(void) {
    vec4 tokenColor = texture2D(uSampler, vTextureCoord);
    vec4 tileColor = texture2D(tileTexture, vTextureCoord);
    vec4 finalColor = tokenColor * tileColor;
    float alpha = tokenColor.a * tileColor.a;

    if (tileColor.a > 0.1) {
      gl_FragColor = vec4(tokenColor.rgb, alpha);
    } else {
      gl_FragColor = vec4(0.0);
    }
  }
`;




class TokenOcclusionFilter extends PIXI.Filter {
	constructor(token, tile) {
		super(vertexShader, occlusionShader);
		this.tile = tile;
		this.uniforms.tileTexture = tile.texture;
    this.uniforms.uColorMatrix = new Float32Array([0.2, 0.3, 0.9, 1.0]);
    // this.uniforms.uOutlineColor = new Float32Array([0.2, 0.3, 0.9, 1.0]);
    // this.uniforms.uOutlineThickness = 2.0; // Largura do outline em pixels
		this.updateTileArea();
	}

  updateTileArea() {
    // Obtém os bounds da textura do tile
    const tileBounds = this.tile.mesh.getBounds();
    const tileTexture = this.tile.texture;
    
    // Calcula as coordenadas UV baseadas nos bounds da textura
    const textureFrame = tileTexture.frame;
    const textureWidth = textureFrame.width;
    const textureHeight = textureFrame.height;
    // console.log('textureFrame :>> ', textureFrame);
    // console.log('textureWidth :>> ', textureWidth);
    // console.log('textureHeight :>> ', textureHeight);
    
    // this.uniforms.tileArea = new Float32Array([
    //     tileBounds.x / textureWidth,
    //     tileBounds.y / textureHeight,
    //     tileBounds.width / textureWidth,
    //     tileBounds.height / textureHeight
    // ]);

    this.uniforms.tileArea = {
      x: tileBounds.x / textureWidth,
      y: tileBounds.y / textureHeight,
      w: tileBounds.width / textureWidth,
      z: tileBounds.height / textureHeight
    };

    console.log('x :>> ', this.uniforms.tileArea.x);
    console.log('y :>> ', this.uniforms.tileArea.y);
    console.log('w :>> ', this.uniforms.tileArea.w);
    console.log('z :>> ', this.uniforms.tileArea.z);
  }

  calculatePosition(token, tile) {
    let {x, y} = token.center;

    // if (game.settings.get('grape_juice-isometrics', 'enable_token_occluder_elevation')){
    // 		x -= (token.document.elevation / canvas.scene.grid.distance) * canvas.grid.size;
    // 		y += (token.document.elevation / canvas.scene.grid.distance) * canvas.grid.size;
    // }

    // Passo 1: Ajustar para os limites do canvas
    if (x < 0) x = 0;
    if (x > canvas.dimensions.width) x = canvas.dimensions.width;
    if (y < 0) y = 0;
    if (y > canvas.dimensions.height) y = canvas.dimensions.height;

    // Passo 2: Normalizar para o intervalo [0, 1]
    x = x / canvas.dimensions.width;
    y = y / canvas.dimensions.height;

    return {x, y};
  }
}








