import { FederatedMouseEvent, FederatedWheelEvent } from "pixi.js";
import { Component, ReactNode, useEffect, useState } from "react";
import { useTransform } from "./TransformContext";
import { useMapData } from "./MapDataContext";
import { SCALE_FACTOR } from "./Constants";
import { useApplication } from "@pixi/react";

class ErrorBoundary extends Component<{ children: ReactNode }, { hasError: boolean }> {
	state = { hasError: false };
  
	static getDerivedStateFromError() {
	  return { hasError: true };
	}
  
	componentDidCatch(error: Error, info: React.ErrorInfo) {
	  console.error("Error caught:", error, info);
	}
  
	render() {
	  return this.state.hasError ? <p>Whoopsie Daisy!</p> : this.props.children;
	}
  }


export function InfiniteCanvas({children} : Readonly<{children?: ReactNode}>) {
	const { position, setPosition, scale, setScale, rotation, setRotation, setScreenCenter, screenCenter } = useTransform();
	const { routeData, originalRouteData } = useMapData();

	const applicationRef = useApplication();
	
	const [isDragging, setIsDragging] = useState(false);
	const [startMousePosition, setStartMousePosition] = useState({ x: 0, y: 0 });
	const [startRotation, setStartRotation] = useState(0);
	const [startPosition, setStartPosition] = useState({ x: 0, y: 0 });

	useEffect(() => {
		const canvas = applicationRef?.app.canvas;
		if (!canvas) return;
		const canvasRect = canvas.getBoundingClientRect();
		const canvasLeft = canvasRect?.left ?? 0; 
		const canvasTop = canvasRect?.top ?? 0;
		const centerX = window.innerWidth / 2 - canvasLeft;
		const centerY = window.innerHeight / 2 - canvasTop;
		setScreenCenter({x: centerX, y: centerY});
	}, [applicationRef?.app.canvas, window.innerWidth, window.innerHeight]);

	const handlePointerDown = (e: FederatedMouseEvent) => {
		setIsDragging(true);
		setStartPosition({
			x: position.x,
			y: position.y
		});
		setStartMousePosition({
			x: e.globalX,
			y: e.globalY
		});
		setStartRotation(rotation);
		e.stopPropagation();
	};


	useEffect(() => {
		setRotation((originalRouteData?.rotate ?? 0) * Math.PI / 180);
	}, [originalRouteData?.rotate]);
	// Get canvas element and its dimensions/position
	const handlePointerMove = (e: FederatedMouseEvent) => {
		if (!isDragging) return;

		if (e.shiftKey) {
			const center = screenCenter ?? {x: 0, y: 0};
			const startAngle = Math.atan2(startMousePosition.y - center.y, startMousePosition.x - center.x);
			const currentAngle = Math.atan2(e.globalY - center.y, e.globalX - center.x);

			// Calculate rotation difference in radians
			const rotationDiff = currentAngle - startAngle;

			// Update rotation
			setRotation(startRotation + rotationDiff);

			const cosDelta = Math.cos(rotationDiff);
			const sinDelta = Math.sin(rotationDiff);
			
			setPosition({
				x: center.x * (1 - cosDelta) + startPosition.x * cosDelta + (center.y - startPosition.y) * sinDelta,
				y: center.y * (1 - cosDelta) + startPosition.y * cosDelta + (startPosition.x - center.x) * sinDelta
			});
			
		} else {
			setRotation(startRotation);
			setPosition({
				x: startPosition.x - startMousePosition.x + e.globalX,
				y: startPosition.y - startMousePosition.y + e.globalY
			});
		}
		e.stopPropagation();
	};

	// Handle mouse up
	const handlePointerUp = (e: FederatedMouseEvent) => {
		setIsDragging(false);
		e.stopPropagation();
	};
	// Handle mouse wheel for zooming
	const handleWheel = (e: FederatedWheelEvent) => {
		e.stopPropagation();
		
		// Get mouse position relative to canvas
		const mouseX = e.globalX - position.x;
		const mouseY = e.globalY - position.y;

		// Calculate new scale
		const scaleMin = (routeData?.scale_min ?? 10)/SCALE_FACTOR;
		const scaleMax = (routeData?.scale_max ?? 20)/SCALE_FACTOR;

		let zoomFactor = e.deltaY > 0 ? 0.9 : 1.1; // Zoom out/in
		//const newScale = scale * zoomFactor;
		const newScale = Math.max(scaleMin, Math.min(scaleMax, scale * zoomFactor));
		zoomFactor = newScale / scale;

		if (scale === newScale) {
			return;
		}

		// Update position to zoom towards mouse cursor
		setPosition({
			x: position.x + mouseX * (1 - zoomFactor),
			y: position.y + mouseY * (1 - zoomFactor)
		});

		setScale(newScale);
	};

	useEffect(() => {
		applicationRef?.app.render();
		console.log(position, scale, rotation);
	}, [position, scale, rotation]);

	
	return (
		<ErrorBoundary>
			{applicationRef?.app && (
				<pixiGraphics
					draw={(g) => {
						const canvas = applicationRef.app.canvas;
						g.clear();
						g.rect(0, 0, canvas?.width ?? 0, canvas?.height ?? 0);
						g.fill("#111");
					}}
					eventMode={'static'}
					interactive
					onPointerDown={handlePointerDown}
					onGlobalPointerMove={handlePointerMove}
					onPointerUp={handlePointerUp}
					onPointerUpOutside={handlePointerUp}
					onWheel={handleWheel}
				/>
			)}
			<pixiContainer
				x={position.x}
				y={position.y}
				scale={scale}
				rotation={rotation}
			>
				{children}
			</pixiContainer>
			{/* Show center of the screen.
			<pixiGraphics
				eventMode="none"

				draw={(g) => {
					g.clear();
					const center = screenCenter ?? {x: 0, y: 0};
					g.circle(center.x, center.y, 1);
					g.fill("#fff");
				}}
			/> */}
		</ErrorBoundary>
	);
}