// Enhanced Occlusion Layer Module for Foundry VTT

export function registerOcclusionLayerConfig() {
	// Persistent occlusion layer configuration
	const occlusionConfig = {
		container: null,
		tokensLayer: null,
		initialized: false
	};

	// Initialize or reset the occlusion layer
	function initializeOcclusionLayer() {
		// Remove existing container if it exists
		if (occlusionConfig.container) {
			canvas.stage.removeChild(occlusionConfig.container);
			occlusionConfig.container.destroy({ children: true });
		}

		// Create the main occlusion container
		occlusionConfig.container = new PIXI.Container();
		occlusionConfig.container.name = "OcclusionContainer";
		occlusionConfig.container.eventMode = 'passive';

		// Create a layer for occlusion tokens
		occlusionConfig.tokensLayer = new PIXI.Container();
		occlusionConfig.tokensLayer.name = "OcclusionTokens";
		occlusionConfig.tokensLayer.sortableChildren = true;

		// Add the layer to the container
		occlusionConfig.container.addChild(occlusionConfig.tokensLayer);

		// Add to canvas stage
		canvas.stage.addChild(occlusionConfig.container);
		canvas.stage.sortChildren();

		occlusionConfig.initialized = true;
	}


	// Comprehensive update mechanism for occlusion layer
	function updateOcclusionLayer() {
		// Ensure canvas is ready and layer is initialized
		if (!canvas.ready) return;

		// Reinitialize if not yet set up
		if (!occlusionConfig.initialized) 
			initializeOcclusionLayer();
		

		// Clear existing occlusion tokens
		occlusionConfig.tokensLayer.removeChildren();

		// Get all tokens and tiles
		const tokens = canvas.tokens.placeables;
		const tiles = canvas.tiles.placeables;

		tokens.forEach(token => {
			// Find tiles that intersect with this token
			const intersectingTiles = tiles.filter(tile => 
				checkTokenTileIntersection(token, tile)
			);

			// If there are intersecting tiles, create an occlusion sprite
			if (intersectingTiles.length > 0) {
				const occlusionSprite = createOcclusionSprite(token, intersectingTiles);
				if (occlusionSprite) {
					occlusionConfig.tokensLayer.addChild(occlusionSprite);
				}
			}
		});
	}

	// Token-Tile Intersection Check
	function checkTokenTileIntersection(token, tile) {
		// Basic intersection check using bounding boxes
		const tokenBounds = token.mesh.getBounds();
		const tileBounds = tile.mesh.getBounds();

		return (
			tokenBounds.x < tileBounds.x + tileBounds.width &&
			tokenBounds.x + tokenBounds.width > tileBounds.x &&
			tokenBounds.y < tileBounds.y + tileBounds.height &&
			tokenBounds.y + tokenBounds.height > tileBounds.y
		);
	}

	// Create Occlusion Sprite with Advanced Masking
	function createOcclusionSprite(token, intersectingTiles) {
		if (!token.mesh || !token.mesh.texture) return null;

		try {
			// Create a sprite from the token's texture
			const sprite = new PIXI.Sprite(token.mesh.texture);
			sprite.position.set(token.mesh.position.x, token.mesh.position.y);
			sprite.anchor.set(token.mesh.anchor.x, token.mesh.anchor.y);
			sprite.angle = token.mesh.angle;
			sprite.scale.set(token.mesh.scale.x, token.mesh.scale.y);

			// Create a mask for the occlusion
			const mask = createOcclusionMask(token, intersectingTiles);
			if (mask) {
				sprite.mask = mask;
			}

			// Create new outline filter
			const outlineFilter = new PIXI.filters.isoOutlineFilter();
			outlineFilter.thickness = 0.005;
			outlineFilter.color = 0x00ff59;
			
			// Create a greyscale/dimming filter
			const colorMatrix = new PIXI.ColorMatrixFilter();
			colorMatrix.alpha = 1;
			colorMatrix.matrix = [
				0.3, 0.0, 0.0, 0.0, 0.0,
				0.0, 0.3, 0.0, 0.0, 0.0,
				0.0, 0.0, 0.3, 0.0, 0.0,
				0.0, 0.0, 0.0, 1.0, 0.0
			];

			// Create new alpha filter
			//const alphaFilter = new PIXI.AlphaFilter();
			//alphaFilter.alpha = 0.5;


			sprite.filters = [colorMatrix, outlineFilter];

			//sprite.alpha = 0.75;
			sprite.eventMode = 'passive';

			return sprite;
		} catch (error) {
			console.error("Error creating occlusion sprite:", error);
			return null;
		}
	}

	// Advanced Occlusion Mask Creation
	function createOcclusionMask(token, intersectingTiles) {
		const maskGraphics = new PIXI.Graphics();
		maskGraphics.beginFill(0xffffff);

		intersectingTiles.forEach(tile => {
			const tokenBounds = token.mesh.getBounds();
			const tileBounds = tile.mesh.getBounds();

			// Calculate intersection area
			const intersectionX = Math.max(tokenBounds.x, tileBounds.x);
			const intersectionY = Math.max(tokenBounds.y, tileBounds.y);
			const intersectionWidth = Math.min(tokenBounds.x + tokenBounds.width, tileBounds.x + tileBounds.width) - intersectionX;
			const intersectionHeight = Math.min(tokenBounds.y + tokenBounds.height, tileBounds.y + tileBounds.height) - intersectionY;

			// Validate intersection
			if (intersectionWidth > 0 && intersectionHeight > 0) {
				try {
					// Safely access tile texture
					const tileTexture = tile.texture?.baseTexture?.resource?.source;
					if (!tileTexture) {
						console.warn("Invalid tile texture", tile);
						return;
					}

					// Validate texture dimensions
					if (tileTexture.width <= 0 || tileTexture.height <= 0) {
						console.warn("Invalid tile texture dimensions", {
							width: tileTexture.width, 
							height: tileTexture.height
						});
						return;
					}

					const canvas = document.createElement("canvas");
					const ctx = canvas.getContext("2d");

					// Set canvas dimensions
					canvas.width = tileTexture.width;
					canvas.height = tileTexture.height;
			
					// Safely draw the image
					ctx.drawImage(tileTexture, 0, 0);

					// Ensure safe intersection coordinates
					const safeIntersectionX = Math.max(0, intersectionX - tileBounds.x);
					const safeIntersectionY = Math.max(0, intersectionY - tileBounds.y);
					const safeWidth = Math.min(intersectionWidth, canvas.width - safeIntersectionX);
					const safeHeight = Math.min(intersectionHeight, canvas.height - safeIntersectionY);

					// Validate sample area
					if (safeWidth <= 0 || safeHeight <= 0) {
						console.warn("Invalid intersection area", {
							safeWidth, 
							safeHeight
						});
						return;
					}

					// Sample image data
					const imageData = ctx.getImageData(
						safeIntersectionX,
						safeIntersectionY,
						safeWidth, 
						safeHeight
					);
					const data = imageData.data;

					// Detect transparent areas
					let transparentArea = false;
					for (let i = 3; i < data.length; i += 4) {
						const alpha = data[i];
						if (alpha === 0) {
							transparentArea = true;
							break;
						}
					}

					// Draw masked intersection
					if (transparentArea) {
						maskGraphics.drawRect(intersectionX, intersectionY, intersectionWidth, intersectionHeight);
					}

					// Clean up
					canvas.remove();
				} catch (error) {
					console.error("Error processing tile texture for occlusion mask:", error);
				}
			}
		});

		maskGraphics.endFill();
		return maskGraphics;
	}

	// Global Hook Registration
	function registerGlobalHooks() {
		// 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;
			}
		});

		// Update triggers for occlusion layer
		const updateTriggers = [
			'canvasReady', 
			'canvasTokensRefresh', 
			'updateToken', 
			'controlToken', 
			'refreshToken',
			'canvasPan',
			'preUpdateScene',  // Added to ensure update on scene modifications
			'updateScene'      // Added to ensure update on scene modifications
		];

		updateTriggers.forEach(hookName => {
			Hooks.on(hookName, () => {
				updateOcclusionLayer();
			});
		});
	}

	// Initialize the module
	function initModule() {
		// Register global hooks
		registerGlobalHooks();

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

	// Start the module
	initModule();
}













// Definição do isoOutlineFilter
if (typeof PIXI !== 'undefined' && PIXI.filters) {
	const vertexShader =`
		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 fragmentShader = `
		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;

			for (float angle = 0.0; angle < 6.28318530718; angle += 0.78539816339) {
				displaced.x = vTextureCoord.x + outlineThickness.x * cos(angle);
				displaced.y = vTextureCoord.y + outlineThickness.y * sin(angle);
				curColor = texture2D(uSampler, displaced);
				maxAlpha = max(maxAlpha, curColor.a);
			}
			float resultAlpha = max(maxAlpha, ownColor.a);
			gl_FragColor = vec4((ownColor.rgb * ownColor.a + outlineColor.rgb * (1.0 - ownColor.a)) * resultAlpha, resultAlpha);
		 }
	`;

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

			// Inicialize os uniforms
			this.uniforms.outlineColor = new Float32Array(4); // Para armazenar RGBA
			this.uniforms.outlineThickness = new Float32Array(2); // Para armazenar X e Y
			this.uniforms.filterArea = new Float32Array(2); // Para área de filtro
			this.uniforms.alpha = alpha;

			// Configure as propriedades iniciais
			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) {
			// Certifique-se de que filterArea tenha valores válidos
			const filterAreaX = this.uniforms.filterArea[0] || 1; // Evite divisão por 0
			const filterAreaY = this.uniforms.filterArea[1] || 1; // Evite divisão por 0
			
			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.');
}