// Enhanced Occlusion Layer Module for Foundry VTT

export function registerOcclusionConfig() {
  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 occlusionShader = `
  varying vec2 vTextureCoord;
  uniform sampler2D uSampler;
  
  void main(void) {
    vec4 tokenColor = texture2D(uSampler, vTextureCoord);

    if (tokenColor.a > 0.1) {
      gl_FragColor = vec4(1.0);
    } else {
      gl_FragColor = vec4(0.4);
    }
  }
`;




class TokenOcclusionFilter extends PIXI.Filter {
  constructor(token, tile) {
    super(null, occlusionShader);
    this.tile = tile;
    this.uniforms.tileTexture = tile.texture;
    this.uniforms.uColorMatrix = new Float32Array([0.2, 0.3, 0.9, 1.0]);
    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;
    
    this.uniforms.tileArea = new Float32Array([
        tileBounds.x / textureWidth,
        tileBounds.y / textureHeight,
        tileBounds.width / textureWidth,
        tileBounds.height / textureHeight
    ]);
  }

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

    // 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};
  }

  calculateSpriteMatrix(outputMatrix, texture, filterManager) {
    const {sourceFrame, destinationFrame} = filterManager.activeState;
    const {orig} = texture;
    const mappedMatrix = outputMatrix.set(destinationFrame.width, 0, 0, destinationFrame.height, sourceFrame.x, sourceFrame.y);
    const worldTransform = canvas.stage.worldTransform.copyTo(PIXI.Matrix.TEMP_MATRIX);

    worldTransform.invert();
    mappedMatrix.prepend(worldTransform);
    mappedMatrix.scale(1.0 / orig.width, 1.0 / orig.height);

    return mappedMatrix;
}
}








