import { Button, Panel, Textbox, Label, Checkbox, RadioButton, RadioGroup } from './ui/canvasui.js';

export class ViewerOverlay {
    constructor(renderer, robot, jointAngles, findObjectByName, saveURDF, loadURDF) {
        this.renderer = renderer;
        this.robot = robot;
        this.jointAngles = jointAngles;
        this.findObjectByName = findObjectByName;
        this.saveURDF = saveURDF;   // 🔑 bound to editor
        this.loadURDF = loadURDF;   // 🔑 bound to editor


        this.overlayCanvas = document.getElementById('overlay-canvas');
        this.overlayCanvas.addEventListener("wheel", (e) => {
            e.preventDefault();   // stops page scroll/zoom
            // your zoom logic here
        }, { passive: false }); // passive:false is required to call preventDefault
        // Disable browser context menu on canvas
        this.overlayCanvas.addEventListener("contextmenu", (e) => {
            e.preventDefault();   // stops the default right‑click menu
            e.stopPropagation();  // keeps it from bubbling up

            // Your custom right‑click logic here
            // e.clientX / e.clientY give you the mouse position
            //this.handleRightClick(e);
        });

        this.overlayCtx = this.overlayCanvas.getContext('2d');

        this.uiElements = [];   // for motor list items if you wrap them in UIElements
        this.panels = [];       // for modals or floating panels


        // this.motorListPanel = this.createMotorListPanel();
        // this.panels.push(this.motorListPanel);
        // console.log(this.panels);

        this.showMotorList = true;
        this.showConfigModal = false;
        this.configMotor = null;
        this.configMotorName = null;

        // Handle resizing
        const resizeOverlay = () => {
            const { clientWidth, clientHeight } = this.renderer.domElement;
            this.overlayCanvas.width = clientWidth;
            this.overlayCanvas.height = clientHeight;
        };
        window.addEventListener('resize', resizeOverlay);
        resizeOverlay();

        this.panels.push(this.createAnimationControlPanel());
        this.panels.push(this.createSystemPanel());
        const handlePointerEvent = (event) => {
            const rect = this.overlayCanvas.getBoundingClientRect();
            const x = event.clientX - rect.left;
            const y = event.clientY - rect.top;

            let consumed = false;

            // Panels get first chance
            for (const panel of this.panels) {
                if (panel.contains(x, y)) {
                    panel.handlePointerEvent(event, x, y);
                    consumed = true;
                    break;
                }
            }



            if (!consumed) {
                const evt = new event.constructor(event.type, event);
                this.renderer.domElement.dispatchEvent(evt);
            }

            if (['pointermove', 'pointerdown', 'pointerup'].includes(event.type)) {
                this.draw();
            }
        };
        window.addEventListener('keydown', (event) => {
            this.panels.forEach(panel => panel.handleKeyEvent(event));
            this.draw();
        });




        // Attach handlers
        ['click', 'pointerdown', 'pointerup', 'pointermove', 'wheel'].forEach(type => {
            this.overlayCanvas.addEventListener(type, handlePointerEvent);
        });
    }

    init(robot) {
        this.robot = robot;
        this.motorListPanel = this.createMotorListPanel(this.overlayCtx);
        if (this.motorListPanel) {
            this.panels.push(this.motorListPanel);
        }
        this.draw();
    }

    draw() {
        const ctx = this.overlayCtx;
        ctx.clearRect(0, 0, this.overlayCanvas.width, this.overlayCanvas.height);
        this.updateMotorLabels();
        this.panels.forEach(panel => panel.draw(ctx));

        //console.log(this.panels);
    }

    createAnimationControlPanel() {
        const w = this.overlayCanvas.width;
        const h = this.overlayCanvas.height / 8;
        const x = 0;
        const y = this.overlayCanvas.height - this.overlayCanvas.height / 8;
        const panel = new Panel(x, y, w, h, "Animation Controls");

        const group = new RadioGroup();

        const rb1 = new RadioButton(panel.x + 20, panel.y + 40, 16, "None", group, true, () => {
            this.setSyncMode("none");
        }, "");
        const rb2 = new RadioButton(panel.x + 20, panel.y + 70, 16, "Sim -> Real", group, false, () => {
            this.setSyncMode("sim_to_real");
        }, "Real robot will mirror simulated movements");
        const rb3 = new RadioButton(panel.x + 20, panel.y + 100, 16, "Real -> Sim", group, false, () => {
            this.setSyncMode("real_to_sim");
        }, "Deactivate all motor torque and simulated robot will mirror real robot motion");

        group.addButton(rb1);
        group.addButton(rb2);
        group.addButton(rb3);

        panel.addElement(rb1);
        panel.addElement(rb2);
        panel.addElement(rb3);


        return panel;
    }

    setSyncMode(mode) {

        console.log(mode);
    }

    createSystemPanel() {
        const w = this.overlayCanvas.width * 0.1;
        const h = this.overlayCanvas.height * 0.05;
        const x = this.overlayCanvas.width * 0.9;   // bottom right
        const y = this.overlayCanvas.height * 0; console.log(this.overlayCanvas.width, this.overlayCanvas.height);

        const panel = new Panel(x, y, w, h, "System");

        // Save button
        panel.addElement(new Button(x, h / 2, 50, 24, "Save", () => {
            // delegate to editor’s save
            this.saveURDF(this.robot);
        }));

        // Load button
        panel.addElement(new Button(x + w / 2, h / 2, 50, 24, "Load", () => {
            // delegate to editor’s load
            this.loadURDF();
        }));

        return panel;
    }


    createMotorListPanel() {
        if (!this.robot?.joints) return null;
        this.panels = this.panels.filter(p => !p.title?.startsWith("Config:"));
        const panel = new Panel(0, 0, 300, this.overlayCanvas.height / 8 * 7, "Motors");

        let y = 10;
        for (const jointName in this.robot.joints) {
            const jointData = this.robot.joints[jointName];
            const jointObject = this.findObjectByName(this.robot, jointName);
            if (!jointData || !jointObject) continue;

            const angle = this.jointAngles?.[jointName] ?? 0;
            const degrees = (angle * 180 / Math.PI).toFixed(1);

            const motorName = jointData.transmission?.actuatorName || '(no motor)';
            if (motorName === "(no motor)") continue;

            // Build label string with angle/ticks
            const t = jointData.transmission;
            let label = `${motorName}  ${degrees}°`;
            if (t) {
                const ticks = radiansToTicks(angle, t).toFixed(0);
                label += `  ${ticks}`;
            }


            // Create button
            const btn = new Button(10, y + 20, 280, 18, label, () => {
                this.openMotorConfig(jointName, motorName);
            });

            btn.jointName = jointName;
            btn.motorName = motorName;


            panel.addElement(btn);
            y += 22;
        }

        return panel;
    }

    updateMotorLabels() {
        const motorPanel = this.panels.find(p => p.title === "Motors");
        if (!motorPanel || !this.robot?.joints) return;

        for (const element of motorPanel.elements) {
            if (!(element instanceof Button) || !element.jointName) continue;

            const jointName = element.jointName;
            const jointData = this.robot.joints[jointName];
            if (!jointData) continue;

            const angle = this.jointAngles?.[jointName] ?? 0;
            const degrees = (angle * 180 / Math.PI).toFixed(1);

            const motorName = jointData.transmission?.actuatorName || '(no motor)';
            if (motorName === "(no motor)") continue;

            let label = `${motorName}  ${degrees}°`;
            const t = jointData.transmission;
            if (t) {
                const ticks = radiansToTicks(angle, t).toFixed(0);
                label += `  ${ticks}`;
            }

            element.label = label; // 🔄 update text
        }
    }


    openMotorConfig(jointName, motorName) {

        const motor = this.robot.joints[jointName];

        // Remove any existing config panel
        this.panels = this.panels.filter(p => !p.title?.startsWith("Config:"));

        const panel = new Panel(400, 50, 400, 250, `Config: ${motorName}`);

        const motorIDLabel = new Label(panel.x + 20, panel.y + 50, 'Motor ID:');
        panel.addElement(motorIDLabel);

        const motorIdBox = new Textbox(panel.x + 100, panel.y + 35, 80, 24, motor.transmission.motorID || '');
        panel.addElement(motorIdBox);

        panel.addElement(new Button(panel.x + 20, panel.y + 150, 80, 24, 'Save', () => {
            const idValue = parseInt(motorIdBox.value, 10) || 0;
            motor.motorId = idValue;
            motor.transmission.motorID = idValue;
            this.addOrUpdateActuatorElement(motorName, "motorID", idValue);
            this.panels = this.panels.filter(p => p !== panel); // close panel
            this.draw();
        }));

        panel.addElement(new Button(panel.x + 120, panel.y + 150, 80, 24, 'Close', () => {
            this.panels = this.panels.filter(p => p !== panel);
            this.draw();
        }));

        this.panels.push(panel);
        this.draw();
    }


    /**
    * Ensure an element exists under a given actuator and set its text.
    * @param {string} transmissionName - The transmission name
    * @param {string} actuatorName - The actuator name
    * @param {string} elementName - The child element to add/update (e.g. "motorID")
    * @param {string|number} value - The value to set
    */
    addOrUpdateActuatorElement(actuatorName, elementName, value) {
        console.log("Updating URDF:", actuatorName, elementName, value);
        const urdfNode = this.robot?.urdfRobotNode;
        if (!urdfNode) return;

        // find actuator anywhere in the URDF
        const actuatorNode = urdfNode.querySelector(`actuator[name="${actuatorName}"]`);
        if (!actuatorNode) return;

        // find or create the child element
        let childNode = actuatorNode.querySelector(elementName);
        if (!childNode) {
            childNode = urdfNode.ownerDocument.createElement(elementName);
            actuatorNode.appendChild(childNode);
        }
        childNode.textContent = value.toString();


    }






    onClick(event) {
        console.log("CLICK");
        const rect = this.overlayCanvas.getBoundingClientRect();
        const x = event.clientX - rect.left;
        const y = event.clientY - rect.top;

        for (const hb of this.motorHitboxes) {
            if (x >= hb.x && x <= hb.x + hb.w &&
                y >= hb.y && y <= hb.y + hb.h) {
                this.openMotorConfig(hb.jointName, hb.motorName);
                break;
            }
        }
    }

    // openMotorConfig(jointName, motorName) {
    //     this.showConfigModal = true;
    //     this.configMotorName = motorName;
    //     this.configMotor = this.robot.joints[jointName];
    //     this.draw();
    // }
}


function ticksToRadians(ticks, actuator) {
    const ticksPerDeg = actuator.encoderTicks / actuator.encoderRange; // ≈ 22.75
    const centered = ticks - actuator.encoderTicks / 2; // shift so mid = 0
    const actuatorDeg = centered / ticksPerDeg;
    const jointDeg = actuatorDeg / actuator.mechanicalReduction;
    return jointDeg * (Math.PI / 180); // convert degrees → radians
}

function radiansToTicks(radians, actuator) {
    const ticksPerDeg = actuator.encoderTicks / actuator.encoderRange; // ≈ 22.75
    const jointDeg = radians * (180 / Math.PI); // radians → degrees
    const actuatorDeg = jointDeg * actuator.mechanicalReduction; // apply reduction
    const centered = actuatorDeg * ticksPerDeg; // convert to ticks offset from mid
    const ticks = centered + actuator.encoderTicks / 2; // shift back to full range
    return ticks;
}