// Layer initialization
const OCCLUSION_LAYER_NAME = 'Occlusion';

function createOcclusionLayer() {
		const occlusionLayer = new CanvasLayer(OCCLUSION_LAYER_NAME);
		occlusionLayer.interactive = false;
		return occlusionLayer;
}

// Main processing functions
function processTokenTileIntersection(token, tile) {
		const occlusionLayer = canvas.layers.find(l => l.name === OCCLUSION_LAYER_NAME);
		if (!occlusionLayer) return;

		// Remove existing graphics for this token-tile pair
		const existingGraphics = occlusionLayer.children.find(
				g => g._tokenId === token.id && g._tileId === tile.id
		);
		if (existingGraphics) existingGraphics.destroy();

		// Check for intersection
		if (!token.bounds.intersects(tile.bounds)) return;

		// Create new graphics
		const graphics = new PIXI.Graphics();
		graphics._tokenId = token.id;
		graphics._tileId = tile.id;

		// Create render texture for intersection mask
		const renderTexture = PIXI.RenderTexture.create({
				width: token.width,
				height: token.height
		});

		// Apply intersection shader
		const shader = new PIXI.Shader.from(vertexShader, fragmentShader, {
				tokenTexture: token.texture,
				tileTexture: tile.texture,
				tokenPosition: token.position,
				tilePosition: tile.position
		});

		// Render intersection to texture
		const quad = new PIXI.Mesh(
				new PIXI.Geometry(),
				shader
		);
		canvas.app.renderer.render(quad, {
				renderTexture,
				clear: true
		});

		// Apply texture to graphics
		graphics.beginTextureFill({
				texture: renderTexture
		});
		graphics.drawRect(0, 0, token.width, token.height);
		graphics.endFill();

		// Position graphics
		graphics.position.set(token.position.x, token.position.y);

		// Apply filters
		graphics.filters = [blueFilter, outlineFilter];

		// Add to occlusion layer
		occlusionLayer.addChild(graphics);
}

function updateAllIntersections() {
		const tokens = canvas.tokens.placeables;
		const tiles = canvas.tiles.placeables;
    
		tokens.forEach(token => {
				tiles.forEach(tile => {
						processTokenTileIntersection(token, tile);
				});
		});
}

// Export function with hooks
export function registerOcclusionSystem() {
		Hooks.on('canvasInit', () => {
				const occlusionLayer = createOcclusionLayer();
				canvas.layers.push(occlusionLayer);
		});

		Hooks.on('renderTile', (tile) => {
				canvas.tokens.placeables.forEach(token => {
						processTokenTileIntersection(token, tile);
				});
		});

		Hooks.on('updateToken', (token) => {
				canvas.tiles.placeables.forEach(tile => {
						processTokenTileIntersection(token, tile);
				});
		});

		Hooks.on('refreshToken', (token) => {
				canvas.tiles.placeables.forEach(tile => {
						processTokenTileIntersection(token, tile);
				});
		});
}

const vertexShader = `
		precision mediump float;
		attribute vec2 aVertexPosition;
		attribute vec2 aTextureCoord;
    
		uniform mat3 projectionMatrix;
		varying vec2 vTextureCoord;
    
		void main() {
				gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
				vTextureCoord = aTextureCoord;
		}
`;

const fragmentShader = `
		precision mediump float;
		varying vec2 vTextureCoord;
    
		uniform sampler2D tokenTexture;
		uniform sampler2D tileTexture;
		uniform vec2 tokenPosition;
		uniform vec2 tilePosition;
    
		void main() {
				// Convert texture coordinates to world space
				vec2 worldPos = vTextureCoord * tokenPosition;
        
				// Convert world space to tile texture space
				vec2 tileTexCoord = (worldPos - tilePosition) / tilePosition;
        
				// Sample both textures
				vec4 tokenColor = texture2D(tokenTexture, vTextureCoord);
				vec4 tileColor = texture2D(tileTexture, tileTexCoord);
        
				// Intersection check with alpha threshold of 0.1
				float intersection = step(0.1, tokenColor.a) * step(0.1, tileColor.a);
        
				// Output only if both textures have content at this pixel
				gl_FragColor = vec4(tokenColor.rgb, tokenColor.a * intersection);
		}
`;









// Definição do isoOutlineFilter
if (typeof PIXI !== 'undefined' && PIXI.filters) {
	const vertexShaderOutline =`
		attribute vec2 aVertexPosition;
		attribute vec2 aTextureCoord;
		uniform mat3 projectionMatrix;
		varying vec2 vTextureCoord;

		void main(void) {
			gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
			vTextureCoord = aTextureCoord;
		}
	`;
	const fragmentShader22 = `
			varying vec2 vTextureCoord;

			uniform sampler2D uSampler;
			uniform float alpha;
			uniform vec2 outlineThickness;
			uniform vec4 outlineColor;
			uniform vec4 filterArea;
			uniform vec4 filterClamp;

			void main(void) {
					vec4 ownColor = texture2D(uSampler, vTextureCoord);
					vec4 curColor;
					float maxAlpha = 0.0;
					vec2 displaced;
        
					// Outline externo com mais samples e suavização
					for (float angle = 0.0; angle < 6.28318530718; angle += 0.19634954085) {  // Dividido em 32 samples
							// Múltiplas amostras em diferentes distâncias para cada ângulo
							for (float dist = 0.5; dist <= 1.0; dist += 0.25) {
									displaced.x = vTextureCoord.x + (outlineThickness.x * dist) * cos(angle);
									displaced.y = vTextureCoord.y + (outlineThickness.y * dist) * sin(angle);
									curColor = texture2D(uSampler, displaced);
									maxAlpha = max(maxAlpha, curColor.a * (1.0 - dist * 0.5));  // Peso baseado na distância
							}
					}

					// Outline interno com suavização
					float innerAlpha = 0.0;
					for (float angle = 0.0; angle < 6.28318530718; angle += 0.19634954085) {
							for (float dist = 0.25; dist <= 0.75; dist += 0.25) {
									displaced.x = vTextureCoord.x - (outlineThickness.x * dist) * cos(angle);
									displaced.y = vTextureCoord.y - (outlineThickness.y * dist) * sin(angle);
									curColor = texture2D(uSampler, displaced);
									innerAlpha = max(innerAlpha, step(curColor.a, 0.1) * (1.0 - dist * 0.5));
							}
					}
        
					float resultAlpha = max(maxAlpha, ownColor.a);
					float innerEffect = smoothstep(0.0, 0.5, innerAlpha) * ownColor.a;

					// Suaviza a transição entre cores
					vec3 color = ownColor.rgb;
					if (ownColor.a < 0.1) {
							color = outlineColor.rgb * smoothstep(0.0, 0.3, maxAlpha);
					} else if (innerEffect > 0.0) {
							color = mix(color, outlineColor.rgb, innerEffect * 0.5);
					}

					gl_FragColor = vec4(color, resultAlpha);
			}
	`;

	// Defina a classe isoOutlineFilter
	class IsoOutlineFilter extends PIXI.Filter {
		constructor(thickness = 1, color = 0x000000, alpha = 1) {
				super(vertexShaderOutline, fragmentShader22);

				this.uniforms.outlineColor = new Float32Array(4);
				this.uniforms.outlineThickness = new Float32Array(2);
				this.uniforms.filterArea = new Float32Array(2);
				this.uniforms.alpha = alpha;

				this.color = color;
				this.thickness = thickness;
		}

		get alpha() { return this.uniforms.alpha; }	
		set alpha(value) { this.uniforms.alpha = value; }

		get color() { return PIXI.utils.rgb2hex(this.uniforms.outlineColor); }	
		set color(value) { PIXI.utils.hex2rgb(value, this.uniforms.outlineColor); }

		get thickness() { return this.uniforms.outlineThickness[0]; }	
		set thickness(value) {
				const filterAreaX = this.uniforms.filterArea[0] || 1;
				const filterAreaY = this.uniforms.filterArea[1] || 1;
			
				this.uniforms.outlineThickness[0] = value / filterAreaX;
				this.uniforms.outlineThickness[1] = value / filterAreaY;
		}
	}

	// Adicione o isoOutlineFilter ao namespace PIXI.filters
	PIXI.filters.isoOutlineFilter = IsoOutlineFilter;
} else {
	console.error('PIXI ou PIXI.filters não estão disponíveis.');
}


// Filters initialization
const blueFilter = new PIXI.filters.ColorMatrixFilter();
blueFilter.matrix = [
		1, 0, 0, 0, 0,
		0, 1, 0, 0, 0,
		0, 0, 2, 0, 0,
		0, 0, 0, 1, 0
];

const outlineFilter = new PIXI.filters.isoOutlineFilter();
outlineFilter.thickness = 0.005;
outlineFilter.color = 0x00ff59;
