/*
    Project Name: Real-Time Whiteboard
    Founded by: Molla Samser
    Designer & Tester: Rima Khatun
    Website: https://rskworld.in
    Email: hello@rskworld.in, info@rskworld.com
    Phone: +91 93305 39277
    Address: Nutanhat, Mongolkote, Purba Burdwan, West Bengal, India, 713147
    Year: 2026
    Copyright: © 2026 RSK World. All rights reserved.
*/

export class CanvasEngine {
    constructor(canvasId) {
        this.canvas = document.getElementById(canvasId);
        this.ctx = this.canvas.getContext('2d');
        
        // Internal buffer for drawing state
        this.buffer = document.createElement('canvas');
        this.bctx = this.buffer.getContext('2d');
        
        this.isDrawing = false;
        this.tool = 'pen';
        this.color = '#00d1ff';
        this.size = 5;
        this.fillShape = false;
        this.startX = 0;
        this.startY = 0;
        this.snapshot = null;
        this.isRemoteDrawing = false;

        // Zoom and Pan
        this.scale = 1;
        this.offsetX = 0;
        this.offsetY = 0;
        this.isPanning = false;

        // History for undo/redo
        this.history = [];
        this.redoStack = [];
        this.maxHistory = 50;

        // Laser pointer
        this.laserTrails = [];

        this.init();
    }

    init() {
        this.resize();
        window.addEventListener('resize', () => this.resize());

        this.canvas.addEventListener('mousedown', (e) => this.handleMouseDown(e));
        this.canvas.addEventListener('mousemove', (e) => this.handleMouseMove(e));
        this.canvas.addEventListener('mouseup', () => this.handleMouseUp());
        this.canvas.addEventListener('mouseleave', () => this.handleMouseUp());

        // Mouse wheel for zoom
        this.canvas.addEventListener('wheel', (e) => {
            e.preventDefault();
            const zoomSpeed = 0.1;
            const delta = e.deltaY > 0 ? -zoomSpeed : zoomSpeed;
            this.zoom(delta, e.clientX, e.clientY);
        });

        // Save initial state
        this.saveHistory();

        // Laser animation loop
        this.animateLaser();
    }

    handleMouseDown(e) {
        if (e.button === 1 || (e.button === 0 && this.tool === 'pan')) {
            this.isPanning = true;
            this.lastPanX = e.clientX;
            this.lastPanY = e.clientY;
            this.canvas.style.cursor = 'grabbing';
            return;
        }
        this.startDrawing(e);
    }

    handleMouseMove(e) {
        if (this.isPanning) {
            const dx = e.clientX - this.lastPanX;
            const dy = e.clientY - this.lastPanY;
            this.offsetX += dx;
            this.offsetY += dy;
            this.lastPanX = e.clientX;
            this.lastPanY = e.clientY;
            this.render();
            return;
        }
        const pos = this.getMousePos(e);
        this.draw(e);
        this.onMouseMove?.(pos);
    }

    handleMouseUp() {
        this.isPanning = false;
        this.canvas.style.cursor = this.tool === 'pan' ? 'grab' : 'crosshair';
        this.stopDrawing();
    }

    zoom(delta, mouseX, mouseY) {
        const rect = this.canvas.getBoundingClientRect();
        const x = mouseX - rect.left;
        const y = mouseY - rect.top;

        const zoomFactor = 1 + delta;
        const newScale = Math.min(Math.max(0.1, this.scale * zoomFactor), 10);
        
        // Adjust offsets to zoom into mouse position
        this.offsetX = x - (x - this.offsetX) * (newScale / this.scale);
        this.offsetY = y - (y - this.offsetY) * (newScale / this.scale);
        
        this.scale = newScale;
        this.render();
    }

    render() {
        this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
        this.ctx.save();
        this.ctx.translate(this.offsetX, this.offsetY);
        this.ctx.scale(this.scale, this.scale);
        this.ctx.drawImage(this.buffer, 0, 0);
        this.ctx.restore();
    }

    resize() {
        const parent = this.canvas.parentElement;
        const width = parent.clientWidth;
        const height = parent.clientHeight;
        
        this.canvas.width = width;
        this.canvas.height = height;
        
        // Ensure buffer matches or exceeds current size
        if (this.buffer.width < width || this.buffer.height < height) {
            const temp = this.bctx.getImageData(0, 0, this.buffer.width, this.buffer.height);
            this.buffer.width = Math.max(this.buffer.width, width);
            this.buffer.height = Math.max(this.buffer.height, height);
            this.bctx.putImageData(temp, 0, 0);
        }
        
        this.render();
    }

    setTool(tool) {
        this.tool = tool;
        this.canvas.style.cursor = tool === 'pan' ? 'grab' : 'crosshair';
    }

    setColor(color) {
        this.color = color;
    }

    setSize(size) {
        this.size = size;
    }

    setFill(fill) {
        this.fillShape = fill;
    }

    getMousePos(e) {
        const rect = this.canvas.getBoundingClientRect();
        return {
            x: (e.clientX - rect.left - this.offsetX) / this.scale,
            y: (e.clientY - rect.top - this.offsetY) / this.scale
        };
    }

    startDrawing(e) {
        this.isDrawing = true;
        const pos = this.getMousePos(e);
        this.startX = pos.x;
        this.startY = pos.y;

        if (this.tool === 'text') {
            const text = prompt('Enter text:');
            if (text) {
                this.drawText(text, this.startX, this.startY);
                this.onDraw?.({
                    startX: this.startX,
                    startY: this.startY,
                    text,
                    tool: 'text',
                    color: this.color,
                    size: this.size
                });
            }
            this.isDrawing = false;
            return;
        }

        this.bctx.beginPath();
        this.bctx.lineWidth = this.size;

        if (this.tool === 'highlighter') {
            this.bctx.globalAlpha = 0.3;
            this.bctx.lineCap = 'square';
        } else {
            this.bctx.globalAlpha = 1.0;
            this.bctx.lineCap = 'round';
        }

        this.bctx.strokeStyle = this.tool === 'eraser' ? '#0d1117' : this.color;
        this.bctx.fillStyle = this.color;

        // Take snapshot for shapes on buffer
        this.snapshot = this.bctx.getImageData(0, 0, this.buffer.width, this.buffer.height);

        if (this.tool === 'pen' || this.tool === 'eraser' || this.tool === 'laser' || this.tool === 'highlighter') {
            this.bctx.moveTo(this.startX, this.startY);
        }

        // Emit start event for real-time
        this.onDrawStart?.({
            x: this.startX,
            y: this.startY,
            tool: this.tool,
            color: this.color,
            size: this.size
        });
    }

    draw(e) {
        if (!this.isDrawing) return;

        const pos = this.getMousePos(e);
        const currentX = pos.x;
        const currentY = pos.y;

        if (this.tool === 'laser') {
            this.addLaserPoint(currentX, currentY);
        } else if (this.tool === 'pen' || this.tool === 'eraser' || this.tool === 'highlighter') {
            this.bctx.lineTo(currentX, currentY);
            this.bctx.stroke();
        } else {
            // Restore snapshot for shapes to avoid "smearing"
            this.bctx.putImageData(this.snapshot, 0, 0);

            if (this.tool === 'line') {
                this.drawLine(this.startX, this.startY, currentX, currentY);
            } else if (this.tool === 'arrow') {
                this.drawArrow(this.startX, this.startY, currentX, currentY);
            } else if (this.tool === 'rectangle') {
                this.drawRect(this.startX, this.startY, currentX, currentY);
            } else if (this.tool === 'triangle') {
                this.drawTriangle(this.startX, this.startY, currentX, currentY);
            } else if (this.tool === 'diamond') {
                this.drawDiamond(this.startX, this.startY, currentX, currentY);
            } else if (this.tool === 'pentagon') {
                this.drawPentagon(this.startX, this.startY, currentX, currentY);
            } else if (this.tool === 'circle') {
                this.drawCircle(this.startX, this.startY, currentX, currentY);
            }
        }

        this.render();

        // Emit draw event for real-time
        this.onDraw?.({
            startX: this.startX,
            startY: this.startY,
            currentX,
            currentY,
            tool: this.tool,
            color: this.color,
            size: this.size,
            fill: this.fillShape
        });
    }

    stopDrawing() {
        if (!this.isDrawing) return;
        this.isDrawing = false;
        this.bctx.closePath();

        if (this.tool !== 'laser') {
            this.saveHistory();
        }

        // Emit end event
        this.onDrawEnd?.();
    }

    drawLine(x1, y1, x2, y2) {
        this.bctx.beginPath();
        this.bctx.moveTo(x1, y1);
        this.bctx.lineTo(x2, y2);
        this.bctx.stroke();
    }

    drawArrow(x1, y1, x2, y2) {
        const headlen = this.size * 3;
        const angle = Math.atan2(y2 - y1, x2 - x1);
        this.bctx.beginPath();
        this.bctx.moveTo(x1, y1);
        this.bctx.lineTo(x2, y2);
        this.bctx.lineTo(x2 - headlen * Math.cos(angle - Math.PI / 6), y2 - headlen * Math.sin(angle - Math.PI / 6));
        this.bctx.moveTo(x2, y2);
        this.bctx.lineTo(x2 - headlen * Math.cos(angle + Math.PI / 6), y2 - headlen * Math.sin(angle + Math.PI / 6));
        this.bctx.stroke();
    }

    drawRect(x1, y1, x2, y2) {
        if (this.fillShape) {
            this.bctx.fillRect(x1, y1, x2 - x1, y2 - y1);
        }
        this.bctx.strokeRect(x1, y1, x2 - x1, y2 - y1);
    }

    drawTriangle(x1, y1, x2, y2) {
        this.bctx.beginPath();
        this.bctx.moveTo(x1 + (x2 - x1) / 2, y1);
        this.bctx.lineTo(x2, y2);
        this.bctx.lineTo(x1, y2);
        this.bctx.closePath();
        if (this.fillShape) {
            this.bctx.fill();
        }
        this.bctx.stroke();
    }

    drawPentagon(x1, y1, x2, y2) {
        const cx = x1 + (x2 - x1) / 2;
        const cy = y1 + (y2 - y1) / 2;
        const r = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)) / 2;
        this.bctx.beginPath();
        for (let i = 0; i < 5; i++) {
            const angle = (i * 2 * Math.PI) / 5 - Math.PI / 2;
            const px = cx + r * Math.cos(angle);
            const py = cy + r * Math.sin(angle);
            if (i === 0) this.bctx.moveTo(px, py);
            else this.bctx.lineTo(px, py);
        }
        this.bctx.closePath();
        if (this.fillShape) {
            this.bctx.fill();
        }
        this.bctx.stroke();
    }

    drawDiamond(x1, y1, x2, y2) {
        const centerX = x1 + (x2 - x1) / 2;
        const centerY = y1 + (y2 - y1) / 2;
        this.bctx.beginPath();
        this.bctx.moveTo(centerX, y1);
        this.bctx.lineTo(x2, centerY);
        this.bctx.lineTo(centerX, y2);
        this.bctx.lineTo(x1, centerY);
        this.bctx.closePath();
        if (this.fillShape) {
            this.bctx.fill();
        }
        this.bctx.stroke();
    }

    drawCircle(x1, y1, x2, y2) {
        this.bctx.beginPath();
        const radius = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
        this.bctx.arc(x1, y1, radius, 0, 2 * Math.PI);
        if (this.fillShape) {
            this.bctx.fill();
        }
        this.bctx.stroke();
    }

    drawText(text, x, y) {
        this.bctx.font = `${this.size * 3}px Inter`;
        this.bctx.fillStyle = this.color;
        this.bctx.fillText(text, x, y);
        this.render();
    }

    clear() {
        this.bctx.clearRect(0, 0, this.buffer.width, this.buffer.height);
        this.saveHistory();
        this.render();
    }

    setBackground(bg) {
        const area = this.canvas.parentElement;
        area.classList.remove('dots', 'grid', 'ruled', 'none');
        area.classList.add(bg);
    }

    async downloadPDF() {
        let jsPDFModule, html2canvasModule;

        try {
            jsPDFModule = (await import('jspdf')).jsPDF;
            html2canvasModule = (await import('html2canvas')).default;
        } catch (e) {
            // Fallback to CDN globals
            jsPDFModule = window.jspdf.jsPDF;
            html2canvasModule = window.html2canvas;
        }

        const canvasArea = this.canvas.parentElement;
        const canvas = await html2canvasModule(canvasArea);
        const imgData = canvas.toDataURL('image/png');

        const pdf = new jsPDFModule({
            orientation: this.canvas.width > this.canvas.height ? 'l' : 'p',
            unit: 'px',
            format: [this.canvas.width, this.canvas.height]
        });

        pdf.addImage(imgData, 'PNG', 0, 0, this.canvas.width, this.canvas.height);
        pdf.save(`whiteboard-${Date.now()}.pdf`);
    }

    // History Management
    saveHistory() {
        if (this.history.length >= this.maxHistory) {
            this.history.shift();
        }
        this.history.push(this.bctx.getImageData(0, 0, this.buffer.width, this.buffer.height));
        this.redoStack = []; // Clear redo stack on new action
    }

    undo() {
        if (this.history.length > 1) {
            this.redoStack.push(this.history.pop());
            const previousState = this.history[this.history.length - 1];
            this.bctx.putImageData(previousState, 0, 0);
            this.render();
        }
    }

    redo() {
        if (this.redoStack.length > 0) {
            const nextState = this.redoStack.pop();
            this.history.push(nextState);
            this.bctx.putImageData(nextState, 0, 0);
            this.render();
        }
    }

    // Image Drawing
    drawImage(img) {
        const maxWidth = this.buffer.width * 0.8;
        const maxHeight = this.buffer.height * 0.8;
        let width = img.width;
        let height = img.height;

        if (width > maxWidth) {
            height *= maxWidth / width;
            width = maxWidth;
        }
        if (height > maxHeight) {
            width *= maxHeight / height;
            height = maxHeight;
        }

        const x = (this.buffer.width - width) / 2;
        const y = (this.buffer.height - height) / 2;

        this.bctx.drawImage(img, x, y, width, height);
        this.saveHistory();
        this.render();

        // Return data for sync
        return { x, y, width, height, src: img.src };
    }

    drawRemoteImage(data) {
        const img = new Image();
        img.src = data.src;
        img.onload = () => {
            this.bctx.drawImage(img, data.x, data.y, data.width, data.height);
            this.saveHistory();
            this.render();
        };
    }

    // Laser Pointer Logic (Visual only, not saved to history)
    addLaserPoint(x, y) {
        this.laserTrails.push({ x, y, life: 1.0, color: this.color });
    }

    animateLaser() {
        const animate = () => {
            if (this.laserTrails.length > 0) {
                this.render();

                this.ctx.save();
                this.ctx.translate(this.offsetX, this.offsetY);
                this.ctx.scale(this.scale, this.scale);
                
                this.laserTrails.forEach((p, i) => {
                    p.life -= 0.05;
                    if (p.life <= 0) {
                        this.laserTrails.splice(i, 1);
                        return;
                    }
                    this.ctx.save();
                    this.ctx.beginPath();
                    this.ctx.arc(p.x, p.y, this.size * p.life, 0, Math.PI * 2);
                    this.ctx.fillStyle = p.color;
                    this.ctx.globalAlpha = p.life;
                    // Glow effect
                    this.ctx.shadowBlur = 15;
                    this.ctx.shadowColor = p.color;
                    this.ctx.fill();
                    this.ctx.restore();
                });
                
                this.ctx.restore();
            }
            requestAnimationFrame(animate);
        };
        requestAnimationFrame(animate);
    }

    // Remote drawing methods for real-time sync
    remoteDraw(data) {
        this.isRemoteDrawing = true;
        const { startX, startY, currentX, currentY, tool, color, size, text, x, y, width, height, src, fill } = data;

        if (tool === 'laser') {
            this.addLaserPoint(currentX, currentY);
            this.isRemoteDrawing = false;
            return;
        }

        if (tool === 'image') {
            this.drawRemoteImage({ x, y, width, height, src });
            this.isRemoteDrawing = false;
            return;
        }

        const prevStrokeStyle = this.bctx.strokeStyle;
        const prevFillStyle = this.bctx.fillStyle;
        const prevLineWidth = this.bctx.lineWidth;
        const prevGlobalAlpha = this.bctx.globalAlpha;
        const prevLineCap = this.bctx.lineCap;
        const prevFillShape = this.fillShape;

        this.bctx.lineWidth = size;
        this.bctx.strokeStyle = tool === 'eraser' ? '#0d1117' : color;
        this.bctx.fillStyle = color;
        this.fillShape = fill;

        if (tool === 'highlighter') {
            this.bctx.globalAlpha = 0.3;
            this.bctx.lineCap = 'square';
        } else {
            this.bctx.globalAlpha = 1.0;
            this.bctx.lineCap = 'round';
        }

        this.bctx.lineJoin = 'round';

        if (tool === 'pen' || tool === 'eraser' || tool === 'highlighter') {
            this.bctx.beginPath();
            this.bctx.moveTo(startX, startY);
            this.bctx.lineTo(currentX, currentY);
            this.bctx.stroke();
            this.bctx.closePath();
        } else if (tool === 'line') {
            this.drawLine(startX, startY, currentX, currentY);
        } else if (tool === 'arrow') {
            this.drawArrow(startX, startY, currentX, currentY);
        } else if (tool === 'rectangle') {
            this.drawRect(startX, startY, currentX, currentY);
        } else if (tool === 'triangle') {
            this.drawTriangle(startX, startY, currentX, currentY);
        } else if (tool === 'diamond') {
            this.drawDiamond(startX, startY, currentX, currentY);
        } else if (tool === 'pentagon') {
            this.drawPentagon(startX, startY, currentX, currentY);
        } else if (tool === 'circle') {
            this.drawCircle(startX, startY, currentX, currentY);
        } else if (tool === 'text') {
            this.bctx.font = `${size * 3}px Inter`;
            this.bctx.fillText(text, startX, startY);
        }

        // Restore context
        this.bctx.strokeStyle = prevStrokeStyle;
        this.bctx.fillStyle = prevFillStyle;
        this.bctx.lineWidth = prevLineWidth;
        this.bctx.globalAlpha = prevGlobalAlpha;
        this.bctx.lineCap = prevLineCap;
        this.fillShape = prevFillShape;

        this.saveHistory();
        this.render();
        this.isRemoteDrawing = false;
    }
}
