export class CurveEditor {
    constructor(canvas, timelineLength = 6, _slider) {
        this.canvas = canvas;
        canvas.width = canvas.offsetWidth;
        canvas.height = canvas.offsetHeight;
        this.ctx = canvas.getContext('2d');
        this.timelineLength = timelineLength;
        this.logicalHeight = 800;

        this.scale = 1;
        this.scaleX = 1;
        this.scaleY = 1;
        this.offset = { x: 0, y: 0 };
        this.pixelsPerSecond = 48;

        this.exportRange = [0, 4095];

        this.currentTime = timelineLength * this.pixelsPerSecond / 2;

        this.durationHandle = {
            x: timelineLength, // or maxTimeCS
            y: 30,                      // top of canvas
            radius: 10,
            type: "duration"
        };


        this.motorButtons = {
            width: 30,
            height: 30,
            padding: 10,
            buttons: [] // will be populated dynamically
        };


        // COMMENT OUT AND READ FROM EXISTING TIMELINE LATER
        this.slider = _slider;//document.getElementById('timeSlider');
        this.slider.max = this.timelineLength * this.pixelsPerSecond;

        console.log(this.slider.max);
        this.slider.value = 0;
        this.slider.addEventListener('input', () => {
            this.currentTime = parseFloat(this.slider.value);
            //console.log(slider.value);
            this.draw();
        });
        this.setLength(timelineLength * this.pixelsPerSecond);


        this.curveSets = {}; // motorID → array of curves
        this.selectedMotorID = 4;
        this.curves = [];
        this.dragging = null;
        this.isPanning = false;
        this.panStart = { x: 0, y: 0 };


        this.initEvents();
        this.draw();
        console.log(this.curveSets);
    }

    encodeCurves() {
        const bufferSize = 1024; // adjust based on expected graph size
        const buffer = new ArrayBuffer(bufferSize);
        const view = new DataView(buffer);
        let offset = 0;

        const curveSegments = [];
        Object.entries(this.curveSets).forEach(([motorIDStr, segments]) => {
            const motorID = parseInt(motorIDStr, 10);
            segments.forEach(segment => {

                curveSegments.push({
                    motorID,
                    startTime: segment.startPoint.x,
                    endTime: segment.endPoint.x,
                    startPointY: segment.startPoint.y,
                    startHandleX: segment.startPointHandle.x,
                    startHandleY: segment.startPointHandle.y,
                    endHandleX: segment.endPointHandle.x,
                    endHandleY: segment.endPointHandle.y,
                    endPointY: segment.endPoint.y
                });
            });
        });
        console.log(curveSegments);
        const curveCount = curveSegments.length;
        view.setUint16(offset, curveCount, true); offset += 2;

        // 🔹 Curve segments

        curveSegments.forEach(seg => {
            console.log(offset, seg.motorID);
            view.setUint8(offset++, seg.motorID);
            view.setUint16(offset, seg.startTime, true); offset += 2;
            view.setUint16(offset, seg.endTime, true); offset += 2;
            view.setInt16(offset, this.yToExportRange(seg.startPointY), true); offset += 2;
            view.setUint16(offset, seg.startHandleX, true); offset += 2;
            view.setInt16(offset, this.yToExportRange(seg.startHandleY), true); offset += 2;
            view.setUint16(offset, seg.endHandleX, true); offset += 2;
            view.setInt16(offset, this.yToExportRange(seg.endHandleY), true); offset += 2;
            view.setInt16(offset, this.yToExportRange(seg.endPointY), true); offset += 2;
            console.log(seg.startPointY, seg.endPointY);
            console.log(this.yToExportRange(seg.startPointY), this.yToExportRange(seg.endPointY));
        });
        //console.log("🧵 Curve segments packed:", curveSegments.length);



        return new Uint8Array(buffer.slice(0, offset));
    }

    addChannel(motorID) {
        this.setCurves([
            {
                startPoint: { x: 0, y: this.canvas.height / 2 },
                startPointHandle: { x: this.timelineLength * this.pixelsPerSecond * 0.25, y: this.canvas.height / 2 },
                endPointHandle: { x: this.timelineLength * this.pixelsPerSecond * 0.75, y: this.canvas.height / 2 },
                endPoint: { x: this.timelineLength * this.pixelsPerSecond, y: this.canvas.height / 2 }
            }
        ]);
        //console.log("TL LENGTH: " + this.timelineLength);
        this.curveSets[motorID] = this.curves;
    }


    setLength(endTime) {
        this.timelineLength = endTime / this.pixelsPerSecond;
        this.slider.max = this.timelineLength * this.pixelsPerSecond;
        console.log(this.slider.max, this.slider.value);
        if (this.slider.value > this.slider.max) {
            this.slider.value = this.slider.max;
        }
        this.durationHandle.x = this.timelineLength;
        //console.log("new endtime: " + endTime);
        //this.currentTime = this.timelineLength * this.pixelsPerSecond / 2;
        this.draw();
    }

    loadCurveSets(curveSets) {
        this.curveSets = []
        this.curveSets = curveSets;
        console.log(curveSets);

        // If selectedMotorID is present in the new set, load its curves
        if (curveSets[this.selectedMotorID]) {
            this.setCurves(curveSets[this.selectedMotorID]);
        } else {
            this.setCurves([]); // fallback to empty
        }
        console.log("LOADED");
        console.log(this.curveSets);
        console.log(Object.keys(curveSets)[0]);
        setSelectedMotor(parseInt(Object.keys(curveSets)[0])); 
        // hello

        //this.selectAdjacentMotor(1);
        // Optional: update motor selector UI or redraw timeline
        //this.refreshMotorSelector?.(); // if you have a method for that
        //this.drawTimelineMarkers?.();


    }



    selectMotor(motorID) {
        this.selectedMotorID = motorID;
        this.curves = this.curveSets[motorID] || [];
        this.draw();
    }



    // valueToY(v) {
    //     return (this.canvas.height / 2 - v * (this.canvas.height / 2)) * this.scaleY + this.offset.y;
    // }

    valueToYNoOffset(v) {
        return (this.canvas.height / 2 - v * (this.canvas.height / 2));
    }

    valueToY(v) {
        return (this.logicalHeight / 2 - v * (this.logicalHeight / 2)) * this.scaleY + this.offset.y;
    }

    yToValue(y) {
        return ((this.logicalHeight / 2 - (y - this.offset.y) / this.scaleY) / (this.logicalHeight / 2));
    }


    // Maps normalised -1 to 1 value to motor range (0, 4095)
    yToExportRange(y) {
        const [minOut, maxOut] = this.exportRange;
        const clampedY = Math.max(0, Math.min(y, this.logicalHeight)); // optional safety
        const normalized = clampedY / this.logicalHeight;              // maps to [0, 1]
        return Math.round(normalized * (maxOut - minOut) + minOut);    // maps to [minOut, maxOut]
    }

    exportRangeToY(value) {
        const [minOut, maxOut] = this.exportRange;
        const normalized = 1 - (value - minOut) / (maxOut - minOut); // flipped
        return this.valueToYNoOffset(normalized * 2 - 1);
    }



    valueToX(value) {
        return value * this.pixelsPerSecond * this.scaleX + this.offset.x;
    }


    xToValue(x) {
        return (x - this.offset.x) / (this.pixelsPerSecond * this.scaleX);
    }


    transform(p) {
        return {
            x: p.x * this.scaleX + this.offset.x,
            y: p.y * this.scaleY + this.offset.y
        };
    }

    inverseTransform(p) {
        return {
            x: (p.x - this.offset.x) / this.scaleX,
            y: (p.y - this.offset.y) / this.scaleY
        };
    }



    draw() {
        const ctx = this.ctx;
        ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);

        this.drawGrid(ctx);

        // Draw inactive curves dimmed
        // Draw inactive curves dimmed only if there are curve sets
        if (this.curveSets && Object.keys(this.curveSets).length > 0) {
            for (const [motorID, curves] of Object.entries(this.curveSets)) {
                if (!curves || curves.length === 0) continue; // skip empty sets
                const isSelected = motorID == this.selectedMotorID;
                this.drawCurves(this.ctx, curves, isSelected ? 1.0 : 0.3, isSelected);
            }
        }


        this.drawCurves(ctx, this.curves);

        this.drawMotorButtons();


        this.drawDurationHandle(ctx);
    }

    drawDurationHandle() {
        const ctx = this.ctx;
        const x = this.valueToX(this.durationHandle.x); // e.g. derived from header.frameCount or maxTime
        const y = 20; // top of canvas
        ctx.save();

        // vertical guide line
        ctx.strokeStyle = "red";
        ctx.lineWidth = 1;
        ctx.beginPath();
        ctx.moveTo(x, 0);
        ctx.lineTo(x, this.canvas.height);
        ctx.stroke();

        // draggable knob at the top
        ctx.fillStyle = "red";
        ctx.beginPath();
        ctx.arc(x, this.durationHandle.y, this.durationHandle.radius, 0, Math.PI * 2); // circle 10px down from top
        ctx.fill();

        ctx.restore();
    }


    drawMotorButtons() {
        const ctx = this.ctx;
        const canvas = this.canvas;
        const { width, height, padding } = this.motorButtons;
        const y = canvas.height - height - padding;

        // Start from the right edge and move left
        const xRight = canvas.width - width - padding;
        const xMid = xRight - (width + padding);
        const xLeft = xMid - (width + padding);

        this.motorButtons.buttons = [
            { label: "<", x: xLeft, y, motorOffset: -1 },
            { label: `${this.selectedMotorID}`, x: xMid, y, motorOffset: 0 },
            { label: ">", x: xRight, y, motorOffset: 1 }
        ];


        for (let btn of this.motorButtons.buttons) {
            ctx.fillStyle = "#eee";
            ctx.fillRect(btn.x, btn.y, width, height);
            ctx.strokeStyle = "#333";
            ctx.strokeRect(btn.x, btn.y, width, height);
            ctx.fillStyle = "#000";
            ctx.font = "16px sans-serif";
            ctx.textAlign = "center";
            ctx.textBaseline = "middle";
            ctx.fillText(btn.label, btn.x + width / 2, btn.y + height / 2);
        }
    }

    cubicBezierX(t, p0, h0, h1, p1) {
        return Math.pow(1 - t, 3) * p0.x +
            3 * Math.pow(1 - t, 2) * t * h0.x +
            3 * (1 - t) * Math.pow(t, 2) * h1.x +
            Math.pow(t, 3) * p1.x;
    }

    solveTForX(targetX, P0, P1, P2, P3, epsilon = 0.5) {
        let lower = 0;
        let upper = 1;
        let t;

        for (let i = 0; i < 20; i++) {
            t = (lower + upper) / 2;

            const u = 1 - t;
            const x = u ** 3 * P0.x + 3 * u ** 2 * t * P1.x + 3 * u * t ** 2 * P2.x + t ** 3 * P3.x;

            if (Math.abs(x - targetX) < epsilon) {
                return t;
            }

            if (x < targetX) {
                lower = t;
            } else {
                upper = t;
            }
        }

        return t;
    }


    getMotorPositionAtTime(motorID, timeInFrames) {
        if (this.curveSets[motorID] === undefined || this.curveSets[motorID].length === 0) {
            console.log("THIS");
            return null;
        }
        const curves = this.curveSets[motorID];
        const timeInSeconds = timeInFrames / this.framesPerSecond;
        const currentTimeX = timeInFrames;

        for (let curve of curves) {

            const p0 = this.transform(curve.startPoint);
            const h0 = this.transform(curve.startPointHandle);
            const h1 = this.transform(curve.endPointHandle);
            const p1 = this.transform(curve.endPoint);

            const minX = Math.min(p0.x, p1.x);
            const maxX = Math.max(p0.x, p1.x);
            if (currentTimeX >= minX && currentTimeX <= maxX) {
                const t = this.solveTForX(currentTimeX, p0, h0, h1, p1);
                const pt = this.cubicBezier(t, p0, h0, h1, p1);
                return this.yToExportRange(pt.y);
            }
        }

        return null; // No curve segment matched the time
    }



    drawCurves(ctx, curves, opacity = 1.0, showControlPoints = true) {
        ctx.save();
        ctx.globalAlpha = opacity;
        if (!curves) {
            return;
        }
        for (let curve of curves) {
            const p0 = this.transform(curve.startPoint);
            const h0 = this.transform(curve.startPointHandle);
            const h1 = this.transform(curve.endPointHandle);
            const p1 = this.transform(curve.endPoint);



            // Draw curve
            ctx.beginPath();
            ctx.moveTo(p0.x, p0.y);
            ctx.bezierCurveTo(h0.x, h0.y, h1.x, h1.y, p1.x, p1.y);
            ctx.strokeStyle = "#0077cc";
            ctx.lineWidth = 2;
            ctx.stroke();

            const seconds = this.currentTime / 48; // convert to seconds
            const currentTimeX = this.valueToX(seconds);

            if (this.currentTime !== null) {
                for (let curve of curves) {
                    const p0 = this.transform(curve.startPoint);
                    const h0 = this.transform(curve.startPointHandle);
                    const h1 = this.transform(curve.endPointHandle);
                    const p1 = this.transform(curve.endPoint);

                    const minX = Math.min(p0.x, p1.x);
                    const maxX = Math.max(p0.x, p1.x);

                    if (currentTimeX >= minX && currentTimeX <= maxX) {
                        const t = this.solveTForX(currentTimeX, p0, h0, h1, p1);
                        const pt = this.cubicBezier(t, p0, h0, h1, p1);

                        ctx.beginPath();
                        ctx.arc(pt.x, pt.y, 5, 0, Math.PI * 2);
                        ctx.fillStyle = "#00cc66";
                        ctx.fill();
                        ctx.strokeStyle = "#003300";
                        ctx.stroke();
                    }
                }
            }

            // Draw control points (optional)
            if (showControlPoints) {
                // Draw handles
                ctx.beginPath();
                ctx.moveTo(p0.x, p0.y);
                ctx.lineTo(h0.x, h0.y);
                ctx.moveTo(h1.x, h1.y);
                ctx.lineTo(p1.x, p1.y);
                ctx.strokeStyle = "#aaa";
                ctx.lineWidth = 1;
                ctx.stroke();

                for (let key of ['startPoint', 'startPointHandle', 'endPointHandle', 'endPoint']) {
                    const p = this.transform(curve[key]);
                    ctx.beginPath();
                    ctx.arc(p.x, p.y, 6, 0, Math.PI * 2);
                    ctx.fillStyle = key.includes('Handle') ? "#888" : "#ff6600";
                    ctx.fill();
                    ctx.strokeStyle = "#333";
                    ctx.stroke();
                }
            }


        }

        ctx.restore();
    }



    drawGrid(ctx) {
        ctx.fillStyle = "#f0f0f0";
        ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);

        const totalTicks = this.timelineLength * 4;
        for (let s = 0; s <= totalTicks; s++) {
            const time = s / 4;
            const x = this.valueToX(time);

            if (x < 0 || x > this.canvas.width) continue;

            ctx.beginPath();
            ctx.moveTo(x, 0);
            ctx.lineTo(x, this.canvas.height);

            if (s % 4 === 0) {
                ctx.strokeStyle = "#999";
                ctx.lineWidth = 1.5;
                ctx.font = "12px sans-serif";
                ctx.fillStyle = "#666";
                ctx.fillText(`${time}s`, x + 0 / this.scaleX, 12 / this.scaleY);
            } else if (s % 2 === 0) {
                ctx.strokeStyle = "#bbb";
                ctx.lineWidth = 1;
            } else {
                ctx.strokeStyle = "#ddd";
                ctx.lineWidth = 0.5;
            }

            ctx.stroke();
        }

        const visibleYMin = Math.max(this.yToValue(this.canvas.height), -1);
        const visibleYMax = Math.min(this.yToValue(0), 1);

        for (let v = Math.ceil(visibleYMin * 4) / 4; v <= visibleYMax; v += 0.25) {
            const y = this.valueToY(v);
            ctx.beginPath();
            ctx.moveTo(0, y);
            ctx.lineTo(this.canvas.width, y);

            ctx.strokeStyle = (v === 1 || v === -1 || v === 0) ? "#999" : "#ddd";
            ctx.lineWidth = (v === 1 || v === -1 || v === 0) ? 1.5 : 0.5;
            ctx.stroke();

            ctx.font = "12px sans-serif";
            ctx.fillStyle = "#666";
            ctx.fillText(v.toFixed(2), 4 / this.scaleX + 10, y - 6 / this.scaleY);

        }
    }

    cubicBezier(t, P0, P1, P2, P3) {
        const u = 1 - t;
        return {
            x: u ** 3 * P0.x + 3 * u ** 2 * t * P1.x + 3 * u * t ** 2 * P2.x + t ** 3 * P3.x,
            y: u ** 3 * P0.y + 3 * u ** 2 * t * P1.y + 3 * u * t ** 2 * P2.y + t ** 3 * P3.y
        };
    }

    splitCurve(curve, t) {
        const lerp = (a, b, t) => ({
            x: a.x + (b.x - a.x) * t,
            y: a.y + (b.y - a.y) * t
        });

        const p01 = lerp(curve.startPoint, curve.startPointHandle, t);
        const p12 = lerp(curve.startPointHandle, curve.endPointHandle, t);
        const p23 = lerp(curve.endPointHandle, curve.endPoint, t);

        const p012 = lerp(p01, p12, t);
        const p123 = lerp(p12, p23, t);

        const split = lerp(p012, p123, t);

        return [
            {
                startPoint: curve.startPoint,
                startPointHandle: p01,
                endPointHandle: p012,
                endPoint: split
            },
            {
                startPoint: split,
                startPointHandle: p123,
                endPointHandle: p23,
                endPoint: curve.endPoint
            }
        ];
    }

    setCurves(curves) {
        this.curves = curves;
        this.draw();
    }

    getCurves() {
        return this.curves;
    }

    selectAdjacentMotor(direction) {
        const ids = Object.keys(this.curveSets)
            .map(id => parseInt(id))
            .sort((a, b) => a - b);

        const currentIndex = ids.indexOf(this.selectedMotorID);
        if (currentIndex === -1) return;

        const newIndex = (currentIndex + direction + ids.length) % ids.length;
        setSelectedMotor(ids[newIndex]); // Global defined in script.js

    }


    initEvents() {
        this.canvas.addEventListener('mousedown', e => {
            const { buttons, width, height } = this.motorButtons;
            const mx = e.offsetX;
            const my = e.offsetY;

            // Check duration handle
            const handleX = this.valueToX(this.durationHandle.x);
            const handleY = this.durationHandle.y; // same as draw
            if (Math.hypot(e.offsetX - handleX, e.offsetY - handleY) < this.durationHandle.radius) {
                this.dragging = { type: "durationHandle" };
                return;
            }


            for (let btn of buttons) {
                if (
                    mx >= btn.x &&
                    mx <= btn.x + width &&
                    my >= btn.y &&
                    my <= btn.y + height
                ) {
                    if (btn.motorOffset === -1) {
                        this.selectAdjacentMotor(-1);
                    } else if (btn.motorOffset === 1) {
                        this.selectAdjacentMotor(1);
                    }
                    console.log(this.selectedMotorID);
                    // No action needed for label button (motorOffset === 0)
                    return;
                }
            }

            const mouse = this.inverseTransform({ x: e.offsetX, y: e.offsetY });

            if (e.button === 1) {
                e.preventDefault(); // Prevent page panning
                this.isPanning = true;
                this.panStart = { x: e.offsetX, y: e.offsetY };
                return;
            }

            for (let curve of this.curves) {
                for (let key of ['startPointHandle', 'endPointHandle', 'startPoint', 'endPoint']) {
                    const p = curve[key];
                    if (Math.hypot(p.x - mouse.x, p.y - mouse.y) < 10 / this.scale) {
                        this.dragging = { curve, key };
                        return;
                    }
                }
            }

        });



        this.canvas.addEventListener('mousemove', e => {
            if (this.isPanning) {
                this.offset.x += e.offsetX - this.panStart.x;
                this.offset.y += e.offsetY - this.panStart.y;
                this.panStart = { x: e.offsetX, y: e.offsetY };
                this.draw();
                return;
            }

            if (this.dragging) {
                const mouse = this.inverseTransform({ x: e.offsetX, y: e.offsetY });
                const { curve, key } = this.dragging;
                const index = this.curves.indexOf(curve);

                if (key === 'startPoint' || key === 'endPoint') {
                    this.dragEndpoint(curve, key, mouse.x, mouse.y, index);
                } else if (this.dragging.type === "durationHandle") {
                    // Update duration in timeline units
                    const newTime = Math.max(0, mouse.x); // timeline units


                    // Optionally update timeline length
                    this.setLength(newTime);
                    this.adjustAllCurvesToDuration(newTime); // truncate/extend curves

                    //console.log(this.durationHandle.x);
                    this.draw();
                    return;

                } else {
                    this.dragControlPoint(curve, key, mouse.x, mouse.y, index);
                }
                this.curveSets[this.selectedMotorID] = this.curves;

                this.draw();
            }
        });

        this.canvas.addEventListener('mouseup', () => {
            this.dragging = null;
            this.isPanning = false;
        });

        this.canvas.addEventListener('wheel', e => {
            e.preventDefault();
            const mouse = { x: e.offsetX, y: e.offsetY };
            const zoomFactor = e.deltaY < 0 ? 1.1 : 0.9;

            const before = this.inverseTransform(mouse);

            // Apply zoom based on modifier keys
            if (e.ctrlKey && !e.altKey) {
                this.scaleX *= zoomFactor;
            } else if (e.altKey && !e.ctrlKey) {
                this.scaleY *= zoomFactor;
            } else {
                // Default: uniform zoom
                this.scaleX *= zoomFactor;
                this.scaleY *= zoomFactor;
            }

            const after = this.inverseTransform(mouse);

            this.offset.x += (after.x - before.x) * this.scaleX;
            this.offset.y += (after.y - before.y) * this.scaleY;

            this.draw();
        });


        this.canvas.addEventListener('dblclick', e => {
            const mouse = this.inverseTransform({ x: e.offsetX, y: e.offsetY });

            for (let i = 0; i < this.curves.length; i++) {
                const curve = this.curves[i];

                if (i > 0 && this.isNearPoint(mouse, curve.startPoint)) {
                    const prev = this.curves[i - 1];
                    const merged = {
                        startPoint: prev.startPoint,
                        startPointHandle: prev.startPointHandle,
                        endPointHandle: curve.endPointHandle,
                        endPoint: curve.endPoint
                    };
                    this.curves.splice(i - 1, 2, merged);
                    this.draw();
                    this.curveSets[this.selectedMotorID] = this.curves;

                    return;
                }

                if (i < this.curves.length - 1 && this.isNearPoint(mouse, curve.endPoint)) {
                    const next = this.curves[i + 1];
                    const merged = {
                        startPoint: curve.startPoint,
                        startPointHandle: curve.startPointHandle,
                        endPointHandle: next.endPointHandle,
                        endPoint: next.endPoint
                    };
                    this.curves.splice(i, 2, merged);
                    this.draw();
                    this.curveSets[this.selectedMotorID] = this.curves;

                    return;
                }

                const x0 = curve.startPoint.x;
                const x3 = curve.endPoint.x;
                if (mouse.x >= x0 && mouse.x <= x3) {
                    let t = 0.5, minT = 0, maxT = 1;
                    for (let j = 0; j < 10; j++) {
                        const pt = this.cubicBezier(t, curve.startPoint, curve.startPointHandle, curve.endPointHandle, curve.endPoint);
                        if (pt.x < mouse.x) minT = t;
                        else maxT = t;
                        t = (minT + maxT) / 2;
                    }

                    const [left, right] = this.splitCurve(curve, t);
                    this.curves.splice(i, 1, left, right);
                    this.draw();
                    this.curveSets[this.selectedMotorID] = this.curves;

                    return;
                }
            }
        });
    }

    adjustAllCurvesToDuration(newTime) {
        for (const [motorID, curves] of Object.entries(this.curveSets)) {
            if (!curves || curves.length === 0) continue;

            for (let i = 0; i < curves.length; i++) {
                const curve = curves[i];

                // If a curve starts beyond the new duration, drop it
                if (curve.startPoint.x >= newTime) {
                    curves.splice(i);
                    break;
                }

                // If a curve ends beyond the new duration, truncate it
                if (curve.endPoint.x > newTime) {
                    curve.endPoint.x = newTime;
                    curve.endPointHandle.x = newTime;
                    curves.splice(i + 1);
                    break;
                }
            }

            // If extending, push the last curve’s end point out
            const last = curves[curves.length - 1];
            if (last.endPoint.x < newTime) {
                const delta = newTime - last.endPoint.x;
                last.endPoint.x = newTime;
                last.endPointHandle.x += delta;
            }

            this.curveSets[motorID] = curves;
        }

        // Update the active reference too
        this.curves = this.curveSets[this.selectedMotorID] || [];
    }



    dragControlPoint(curve, key, mouseX, mouseY, index) {
        curve[key].y = mouseY;

        const firstX = this.curves[0].startPoint.x;
        const lastX = this.curves[this.curves.length - 1].endPoint.x;

        if (key === 'startPointHandle') {
            const minX = Math.max(curve.startPoint.x, this.curves[index - 1]?.endPoint?.x ?? firstX);
            const maxX = this.curves[index + 1]?.startPoint?.x ?? lastX;
            curve.startPointHandle.x = Math.max(minX, Math.min(mouseX, maxX - 0.01));
        } else if (key === 'endPointHandle') {
            const minX = this.curves[index - 1]?.endPoint?.x ?? firstX;
            const maxX = Math.min(curve.endPoint.x, this.curves[index + 1]?.endPoint?.x ?? lastX);
            curve.endPointHandle.x = Math.max(minX, Math.min(mouseX, maxX - 0.01));
        } else {
            curve[key].x = Math.max(firstX, Math.min(mouseX, lastX));
        }
    }

    dragEndpoint(curve, key, mouseX, mouseY, index) {
        let deltaX = 0;
        let deltaY = 0;

        if (key === 'startPoint') {
            const oldX = curve.startPoint.x;
            const oldY = curve.startPoint.y;

            if (index === 0) {
                curve.startPoint.x = 0;
            } else {
                const firstX = this.curves[0].startPoint.x;
                const nextX = this.curves[index + 1]?.startPoint?.x ?? Infinity;
                curve.startPoint.x = Math.max(firstX + 0.01, Math.min(mouseX, nextX - 0.01));
            }

            curve.startPoint.y = mouseY;

            deltaX = curve.startPoint.x - oldX;
            deltaY = curve.startPoint.y - oldY;

            curve.startPointHandle.x += deltaX;
            curve.startPointHandle.y += deltaY;

            this.dragControlPoint(curve, 'startPointHandle', curve.startPointHandle.x, curve.startPointHandle.y, index);

            //console.log(this.yToExportRange(curve.startPoint.y));
        } else if (key === 'endPoint') {
            const oldX = curve.endPoint.x;
            const oldY = curve.endPoint.y;

            if (index === this.curves.length - 1) {
                curve.endPoint.x = this.timelineLength * this.pixelsPerSecond; // logical time, not screen space
            } else {
                const firstX = this.curves[0].startPoint.x;
                const prevX = this.curves[index - 1]?.endPoint?.x ?? firstX;
                const nextX = this.curves[index + 1]?.endPoint?.x ?? Infinity;
                curve.endPoint.x = Math.max(prevX + 0.01, Math.min(mouseX, nextX - 0.01));
            }


            curve.endPoint.y = mouseY;

            deltaX = curve.endPoint.x - oldX;
            deltaY = curve.endPoint.y - oldY;

            curve.endPointHandle.x += deltaX;
            curve.endPointHandle.y += deltaY;

            this.dragControlPoint(curve, 'endPointHandle', curve.endPointHandle.x, curve.endPointHandle.y, index);

            const nextCurve = this.curves[index + 1];
            //console.log(curve.endPoint.y);
            if (nextCurve) {
                nextCurve.startPoint.x = curve.endPoint.x;
                nextCurve.startPoint.y = curve.endPoint.y;
                nextCurve.startPointHandle.x += deltaX;
                nextCurve.startPointHandle.y += deltaY;

                this.dragControlPoint(nextCurve, 'startPointHandle', nextCurve.startPointHandle.x, nextCurve.startPointHandle.y, index + 1);
            }
        }
        // After all endpoint logic is done
        for (let i = 0; i < this.curves.length; i++) {
            const curve = this.curves[i];

            this.dragControlPoint(curve, 'startPointHandle', curve.startPointHandle.x, curve.startPointHandle.y, i);
            this.dragControlPoint(curve, 'endPointHandle', curve.endPointHandle.x, curve.endPointHandle.y, i);
        }


    }



    isNearPoint(mouse, point, threshold = 5) {
        return (
            Math.abs(mouse.x - point.x) < threshold &&
            Math.abs(mouse.y - point.y) < threshold
        );
    }

}
