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





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

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

	sprite.filters = [colorMatrix];
	sprite.eventMode = 'passive';

	return sprite;
}














// Advanced Occlusion Mask Creation
function createOcclusionMask2(token, intersectingTiles) {
	let maskGraphics;
	
	intersectingTiles.forEach(tile => {
		const tokenBounds = token.mesh.getBounds();
		const tileBounds = tile.mesh.getBounds();

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

		if (width <= 0 || height <= 0) return;

		//código para a criação da mascara
	});

	return maskGraphics;
}
function createOcclusionMask(token, intersectingTiles) {
	const renderer = canvas.app.renderer;
	const maskGraphics = new PIXI.Graphics();

	// Create a render texture for the mask
	const maskTexture = PIXI.RenderTexture.create({
			width: canvas.dimensions.width,
			height: canvas.dimensions.height
	});

	// Create a framebuffer for rendering the mask
	const framebuffer = new PIXI.RenderTexture.create({
			width: canvas.dimensions.width,
			height: canvas.dimensions.height
	});

	// Create a shader program for the mask
	const vertexSrc = `
			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 fragmentSrc = `
			varying vec2 vTextureCoord;
			uniform sampler2D uSampler;
			void main(void) {
					vec4 tokenColor = texture2D(uSampler, vTextureCoord);
					if (tokenColor.a > 0.0) {
							gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
					} else {
							gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
					}
			}
	`;

	const shader = PIXI.Shader.from(vertexSrc, fragmentSrc);

	// Create a sprite for the token texture
	const tokenSprite = new PIXI.Sprite(token.mesh.texture);
	tokenSprite.position.set(token.mesh.position.x, token.mesh.position.y);
	tokenSprite.anchor.set(token.mesh.anchor.x, token.mesh.anchor.y);
	tokenSprite.angle = token.mesh.angle;
	tokenSprite.scale.set(token.mesh.scale.x, token.mesh.scale.y);

	// Render the token texture to the framebuffer
	renderer.render(tokenSprite, framebuffer, false, null, false);

	// Create a sprite for the mask texture
	const maskSprite = new PIXI.Sprite(maskTexture);

	// Render the mask using the shader
	renderer.render(maskSprite, {
			renderTexture: maskTexture,
			clear: true,
			transform: null,
			skipUpdateTransform: false
	}, shader, false, null, false);

	// Draw the mask graphics
	maskGraphics.beginFill(0xFFFFFF);
	maskGraphics.drawRect(0, 0, canvas.dimensions.width, canvas.dimensions.height);
	maskGraphics.endFill();

	// Apply the mask texture to the mask graphics
	maskGraphics.texture = maskTexture;

	return maskGraphics;
}


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