'use strict';

var React = require('react');
var vectorEditorTypes = require('./vectorEditorTypes.js');
var vectorEditorExport = require('./vectorEditorExport.js');
var vectorEditorImport = require('./vectorEditorImport.js');
var vectorBooleanOps = require('./vectorBooleanOps.js');

/**
 * useVectorEditor — extracted logic + state for VectorEditor.
 * Keeps every useState / useRef / useCallback / useEffect in a single hook
 * so the component file is pure layout.
 */
function useVectorEditor({ artW, artH, onSaveToLibrary }) {
    /* ── state ── */
    const [shapes, setShapes] = React.useState([]);
    const [selId, setSelId] = React.useState(null);
    const [tool, setTool] = React.useState("pointer");
    const [fillCol, setFillCol] = React.useState("#5566cc");
    const [strokeCol, setStrokeCol] = React.useState("#ffffff");
    const [strokeW, setStrokeW] = React.useState(2);
    const [fontSize, setFontSize] = React.useState(24);
    const [fontFamily, setFontFamily] = React.useState("sans-serif");
    const [shapeOpacity, setShapeOpacity] = React.useState(1);
    const [cornerRadius, setCornerRadius] = React.useState(12);
    const [polygonSides, setPolygonSides] = React.useState(6);
    const [starPoints2, setStarPoints2] = React.useState(5);
    const [starInnerRatio, setStarInnerRatio] = React.useState(0.4);
    const [zoom, setZoom] = React.useState(1);
    const [px, setPx] = React.useState(0);
    const [py, setPy] = React.useState(0);
    const [dlgSvg, setDlgSvg] = React.useState(false);
    const [svgText, setSvgText] = React.useState("");
    const [snapGrid, setSnapGrid] = React.useState(false);
    const [gridSize, setGridSize] = React.useState(16);
    const [hasClipboard, setHasClipboard] = React.useState(false);
    /* ── refs ── */
    const boxRef = React.useRef(null);
    const drawing = React.useRef(false);
    const startPt = React.useRef({ x: 0, y: 0 });
    const pendingShape = React.useRef(null);
    const penPts = React.useRef([]);
    const panning = React.useRef(false);
    const panStart = React.useRef({ mx: 0, my: 0, px: 0, py: 0 });
    const hist = React.useRef([]);
    const hIdx = React.useRef(-1);
    const fInp = React.useRef(null);
    const clipboard = React.useRef(null);
    const shapesRef = React.useRef([]);
    shapesRef.current = shapes;
    /* ── derived ── */
    const selShape = React.useMemo(() => shapes.find((s) => s.id === selId) || null, [shapes, selId]);
    /* ── history ── */
    const snap = React.useCallback((s) => {
        hist.current = hist.current.slice(0, hIdx.current + 1);
        hist.current.push({ shapes: vectorEditorTypes.cloneShapes(s) });
        if (hist.current.length > 50) {
            hist.current.shift();
            hIdx.current--;
        }
        hIdx.current = hist.current.length - 1;
    }, []);
    React.useEffect(() => {
        if (hist.current.length === 0)
            snap([]);
    }, [snap]);
    const undo = React.useCallback(() => {
        if (hIdx.current <= 0)
            return;
        hIdx.current--;
        setShapes(vectorEditorTypes.cloneShapes(hist.current[hIdx.current].shapes));
    }, []);
    const redo = React.useCallback(() => {
        if (hIdx.current >= hist.current.length - 1)
            return;
        hIdx.current++;
        setShapes(vectorEditorTypes.cloneShapes(hist.current[hIdx.current].shapes));
    }, []);
    /* ── coordinate transform ── */
    const scr2art = React.useCallback((cx, cy) => {
        var _a;
        const r = (_a = boxRef.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
        if (!r)
            return { x: 0, y: 0 };
        let x = (cx - r.left - r.width / 2 - px) / zoom + artW / 2;
        let y = (cy - r.top - r.height / 2 - py) / zoom + artH / 2;
        if (snapGrid) {
            x = vectorEditorTypes.snapToGrid(x, gridSize);
            y = vectorEditorTypes.snapToGrid(y, gridSize);
        }
        return { x, y };
    }, [px, py, zoom, artW, artH, snapGrid, gridSize]);
    /* ── mouse handlers ── */
    const onDown = React.useCallback((e) => {
        e.preventDefault();
        if (e.button === 1) {
            panning.current = true;
            panStart.current = { mx: e.clientX, my: e.clientY, px, py };
            return;
        }
        if (e.button !== 0)
            return;
        const p = scr2art(e.clientX, e.clientY);
        const defaultBase = {
            fill: fillCol,
            stroke: strokeCol,
            strokeWidth: strokeW,
            opacity: shapeOpacity,
            rotation: 0,
        };
        if (tool === "pointer") {
            let hit = null;
            for (let i = shapes.length - 1; i >= 0; i--) {
                const s = shapes[i];
                if (p.x >= s.x && p.y >= s.y && p.x <= s.x + s.w && p.y <= s.y + s.h) {
                    hit = s.id;
                    break;
                }
            }
            setSelId(hit);
            if (hit) {
                drawing.current = true;
                startPt.current = p;
            }
            return;
        }
        if (tool === "pen") {
            penPts.current.push(p);
            if (penPts.current.length === 1) {
                const ns = {
                    id: vectorEditorTypes.sid(), type: "polyline", x: p.x, y: p.y, w: 0, h: 0,
                    points: [{ x: p.x, y: p.y }], fill: "none", stroke: strokeCol,
                    strokeWidth: strokeW, opacity: shapeOpacity, rotation: 0,
                };
                pendingShape.current = ns;
                setShapes((prev) => [...prev, ns]);
            }
            else {
                const pts = [...penPts.current];
                const xs = pts.map((pp) => pp.x);
                const ys = pts.map((pp) => pp.y);
                setShapes((prev) => prev.map((s) => {
                    var _a;
                    return s.id === ((_a = pendingShape.current) === null || _a === void 0 ? void 0 : _a.id)
                        ? { ...s, points: pts, x: Math.min(...xs), y: Math.min(...ys), w: Math.max(...xs) - Math.min(...xs), h: Math.max(...ys) - Math.min(...ys) }
                        : s;
                }));
            }
            return;
        }
        if (tool === "text") {
            const txt = prompt("Enter text:", "Hello");
            if (!txt)
                return;
            const ns = {
                id: vectorEditorTypes.sid(), type: "text", x: p.x, y: p.y,
                w: txt.length * fontSize * 0.6, h: fontSize,
                ...defaultBase, stroke: "none", strokeWidth: 0,
                text: txt, fontSize, fontFamily,
            };
            const next = [...shapes, ns];
            snap(next);
            setShapes(next);
            setSelId(ns.id);
            return;
        }
        if (tool === "polygon") {
            const r2 = 40;
            const ns = {
                id: vectorEditorTypes.sid(), type: "polygon", x: p.x - r2, y: p.y - r2,
                w: r2 * 2, h: r2 * 2, ...defaultBase, sides: polygonSides,
            };
            drawing.current = true;
            startPt.current = p;
            pendingShape.current = ns;
            setShapes((prev) => [...prev, ns]);
            return;
        }
        if (tool === "star") {
            const r2 = 40;
            const ns = {
                id: vectorEditorTypes.sid(), type: "star", x: p.x - r2, y: p.y - r2,
                w: r2 * 2, h: r2 * 2, ...defaultBase, sides: starPoints2, innerRadius: starInnerRatio,
            };
            drawing.current = true;
            startPt.current = p;
            pendingShape.current = ns;
            setShapes((prev) => [...prev, ns]);
            return;
        }
        // rect / roundrect / ellipse / line / arrow — start drag
        drawing.current = true;
        startPt.current = p;
        const typeMap = { line: "line", arrow: "arrow", ellipse: "ellipse", roundrect: "roundrect" };
        const shapeType = typeMap[tool] || "rect";
        const isLineLike = shapeType === "line" || shapeType === "arrow";
        const ns = {
            id: vectorEditorTypes.sid(), type: shapeType, x: p.x, y: p.y, w: 0, h: 0,
            x2: p.x, y2: p.y,
            fill: isLineLike ? "none" : fillCol, stroke: strokeCol,
            strokeWidth: strokeW, opacity: shapeOpacity, rotation: 0,
            rx: shapeType === "roundrect" ? cornerRadius : undefined,
        };
        pendingShape.current = ns;
        setShapes((prev) => [...prev, ns]);
    }, [tool, shapes, scr2art, px, py, fillCol, strokeCol, strokeW, fontSize, fontFamily, snap, shapeOpacity, polygonSides, starPoints2, starInnerRatio, cornerRadius]);
    const onMove = React.useCallback((e) => {
        if (panning.current) {
            setPx(panStart.current.px + (e.clientX - panStart.current.mx));
            setPy(panStart.current.py + (e.clientY - panStart.current.my));
            return;
        }
        if (!drawing.current)
            return;
        const p = scr2art(e.clientX, e.clientY);
        if (tool === "pointer" && selId) {
            const dx = p.x - startPt.current.x;
            const dy = p.y - startPt.current.y;
            startPt.current = p;
            setShapes((prev) => prev.map((s) => {
                var _a;
                return s.id === selId
                    ? {
                        ...s, x: s.x + dx, y: s.y + dy,
                        x2: s.x2 != null ? s.x2 + dx : undefined,
                        y2: s.y2 != null ? s.y2 + dy : undefined,
                        points: (_a = s.points) === null || _a === void 0 ? void 0 : _a.map((pt) => ({ x: pt.x + dx, y: pt.y + dy })),
                    }
                    : s;
            }));
            return;
        }
        if (!pendingShape.current)
            return;
        const t = pendingShape.current.type;
        if (t === "polygon" || t === "star") {
            const cx = startPt.current.x;
            const cy = startPt.current.y;
            const dx = p.x - cx;
            const dy = p.y - cy;
            const r2 = Math.sqrt(dx * dx + dy * dy);
            setShapes((prev) => prev.map((s) => s.id === pendingShape.current.id
                ? { ...s, x: cx - r2, y: cy - r2, w: r2 * 2, h: r2 * 2 }
                : s));
            return;
        }
        if (t === "rect" || t === "roundrect" || t === "ellipse") {
            const x1 = Math.min(startPt.current.x, p.x);
            const y1 = Math.min(startPt.current.y, p.y);
            const w2 = Math.abs(p.x - startPt.current.x);
            const h2 = Math.abs(p.y - startPt.current.y);
            setShapes((prev) => prev.map((s) => s.id === pendingShape.current.id ? { ...s, x: x1, y: y1, w: w2, h: h2 } : s));
        }
        else if (t === "line" || t === "arrow") {
            setShapes((prev) => prev.map((s) => s.id === pendingShape.current.id
                ? { ...s, w: Math.abs(p.x - s.x), h: Math.abs(p.y - s.y), x2: p.x, y2: p.y }
                : s));
        }
    }, [tool, scr2art, selId]);
    const onUp = React.useCallback(() => {
        var _a, _b;
        if (panning.current) {
            panning.current = false;
            return;
        }
        if (drawing.current) {
            drawing.current = false;
            if (pendingShape.current) {
                const ps = pendingShape.current;
                if (ps.type !== "polyline" && ps.type !== "text") {
                    const minD = 3;
                    const isTiny = ps.type === "line" || ps.type === "arrow"
                        ? Math.abs(((_a = ps.x2) !== null && _a !== void 0 ? _a : ps.x) - ps.x) < minD && Math.abs(((_b = ps.y2) !== null && _b !== void 0 ? _b : ps.y) - ps.y) < minD
                        : Math.abs(ps.w) < minD && Math.abs(ps.h) < minD;
                    if (isTiny) {
                        setShapes((prev) => prev.filter((s) => s.id !== ps.id));
                        pendingShape.current = null;
                        return;
                    }
                }
                snap(shapesRef.current);
                pendingShape.current = null;
            }
        }
    }, [snap]);
    const onWheel = React.useCallback((e) => {
        e.preventDefault();
        setZoom((z) => {
            const n = e.deltaY < 0 ? z * 1.15 : z / 1.15;
            return Math.max(0.1, Math.min(10, n));
        });
    }, []);
    const onDblClick = React.useCallback(() => {
        if (tool === "pen" && pendingShape.current) {
            snap(shapesRef.current);
            setSelId(pendingShape.current.id);
            pendingShape.current = null;
            penPts.current = [];
        }
    }, [tool, snap]);
    /* ── shape operations ── */
    const deleteShape = React.useCallback(() => {
        if (!selId)
            return;
        const next = shapes.filter((s) => s.id !== selId);
        snap(next);
        setShapes(next);
        setSelId(null);
    }, [selId, shapes, snap]);
    const bringToFront = React.useCallback(() => {
        if (!selId)
            return;
        const next = [...shapes.filter((s) => s.id !== selId), shapes.find((s) => s.id === selId)];
        snap(next);
        setShapes(next);
    }, [selId, shapes, snap]);
    const sendToBack = React.useCallback(() => {
        if (!selId)
            return;
        const next = [shapes.find((s) => s.id === selId), ...shapes.filter((s) => s.id !== selId)];
        snap(next);
        setShapes(next);
    }, [selId, shapes, snap]);
    const updateSel = React.useCallback((patch) => {
        if (!selId)
            return;
        setShapes((prev) => prev.map((s) => (s.id === selId ? { ...s, ...patch } : s)));
    }, [selId]);
    /* ── clipboard ── */
    const copyShape = React.useCallback(() => {
        if (!selShape)
            return;
        clipboard.current = JSON.parse(JSON.stringify(selShape));
        setHasClipboard(true);
    }, [selShape]);
    const pasteShape = React.useCallback(() => {
        if (!clipboard.current)
            return;
        const ns = { ...JSON.parse(JSON.stringify(clipboard.current)), id: vectorEditorTypes.sid(), x: clipboard.current.x + 20, y: clipboard.current.y + 20 };
        const next = [...shapes, ns];
        snap(next);
        setShapes(next);
        setSelId(ns.id);
    }, [shapes, snap]);
    const duplicateShape = React.useCallback(() => {
        if (!selShape)
            return;
        const ns = { ...JSON.parse(JSON.stringify(selShape)), id: vectorEditorTypes.sid(), x: selShape.x + 20, y: selShape.y + 20 };
        const next = [...shapes, ns];
        snap(next);
        setShapes(next);
        setSelId(ns.id);
    }, [selShape, shapes, snap]);
    /* ── flip ── */
    const flipH = React.useCallback(() => {
        if (!selId || !selShape)
            return;
        if (selShape.points) {
            const cx = selShape.x + selShape.w / 2;
            const next = shapes.map((s) => { var _a; return s.id === selId ? { ...s, points: (_a = s.points) === null || _a === void 0 ? void 0 : _a.map((pt) => ({ x: cx * 2 - pt.x, y: pt.y })) } : s; });
            snap(next);
            setShapes(next);
        }
        else if (selShape.type === "line" || selShape.type === "arrow") {
            const next = shapes.map((s) => { var _a; return s.id === selId ? { ...s, x: (_a = s.x2) !== null && _a !== void 0 ? _a : s.x, x2: s.x } : s; });
            snap(next);
            setShapes(next);
        }
        else {
            const newRot = selShape.rotation ? -selShape.rotation : 0;
            const next = shapes.map((s) => s.id === selId ? { ...s, rotation: newRot } : s);
            snap(next);
            setShapes(next);
        }
    }, [selId, selShape, shapes, snap]);
    const flipV = React.useCallback(() => {
        if (!selId || !selShape)
            return;
        if (selShape.points) {
            const cy = selShape.y + selShape.h / 2;
            const next = shapes.map((s) => { var _a; return s.id === selId ? { ...s, points: (_a = s.points) === null || _a === void 0 ? void 0 : _a.map((pt) => ({ x: pt.x, y: cy * 2 - pt.y })) } : s; });
            snap(next);
            setShapes(next);
        }
        else if (selShape.type === "line" || selShape.type === "arrow") {
            const next = shapes.map((s) => { var _a; return s.id === selId ? { ...s, y: (_a = s.y2) !== null && _a !== void 0 ? _a : s.y, y2: s.y } : s; });
            snap(next);
            setShapes(next);
        }
        else {
            const newRot = selShape.rotation ? (180 - selShape.rotation) % 360 : 180;
            const next = shapes.map((s) => s.id === selId ? { ...s, rotation: newRot } : s);
            snap(next);
            setShapes(next);
        }
    }, [selId, selShape, shapes, snap]);
    /* ── alignment ── */
    const alignShapes = React.useCallback((mode) => {
        if (!selId || !selShape)
            return;
        let patch = {};
        switch (mode) {
            case "left":
                patch = { x: 0 };
                break;
            case "right":
                patch = { x: artW - selShape.w };
                break;
            case "center":
                patch = { x: (artW - selShape.w) / 2 };
                break;
            case "top":
                patch = { y: 0 };
                break;
            case "bottom":
                patch = { y: artH - selShape.h };
                break;
            case "middle":
                patch = { y: (artH - selShape.h) / 2 };
                break;
        }
        const next = shapes.map((s) => (s.id === selId ? { ...s, ...patch } : s));
        snap(next);
        setShapes(next);
    }, [selId, selShape, shapes, snap, artW, artH]);
    /* ── boolean operations ── */
    const [boolSecondShape, setBoolSecondShape] = React.useState(null);
    const applyBoolean = React.useCallback((operation) => {
        var _a;
        if (!selId || !boolSecondShape)
            return;
        const shapeA = shapes.find((s) => s.id === selId);
        const shapeB = shapes.find((s) => s.id === boolSecondShape);
        if (!shapeA || !shapeB)
            return;
        const result = vectorBooleanOps.booleanOperation(shapeA, shapeB, operation);
        if (!result) {
            alert("Boolean operation resulted in empty shape");
            return;
        }
        // Remove original shapes, add result(s)
        let next = shapes.filter((s) => s.id !== selId && s.id !== boolSecondShape);
        if (Array.isArray(result)) {
            next = [...next, ...result];
            setSelId(((_a = result[0]) === null || _a === void 0 ? void 0 : _a.id) || null);
        }
        else {
            next = [...next, result];
            setSelId(result.id);
        }
        snap(next);
        setShapes(next);
        setBoolSecondShape(null);
    }, [selId, boolSecondShape, shapes, snap]);
    const unionShapes = React.useCallback(() => applyBoolean("union"), [applyBoolean]);
    const subtractShapes = React.useCallback(() => applyBoolean("difference"), [applyBoolean]);
    const intersectShapes = React.useCallback(() => applyBoolean("intersection"), [applyBoolean]);
    const excludeShapes = React.useCallback(() => applyBoolean("exclusion"), [applyBoolean]);
    const outlineStroke = React.useCallback(() => {
        if (!selId || !selShape)
            return;
        const result = vectorBooleanOps.flattenToOutline(selShape);
        const next = shapes.map((s) => (s.id === selId ? result : s));
        snap(next);
        setShapes(next);
        setSelId(result.id);
    }, [selId, selShape, shapes, snap]);
    /* ── SVG import ── */
    const importSVGString = React.useCallback((raw) => {
        try {
            const imported = vectorEditorImport.parseSVGToShapes(raw);
            if (imported.length === 0) {
                alert("No supported shapes found in SVG.");
                return;
            }
            const next = [...shapes, ...imported];
            snap(next);
            setShapes(next);
            setDlgSvg(false);
        }
        catch (e) {
            alert(e instanceof Error ? e.message : "Failed to parse SVG.");
        }
    }, [shapes, snap]);
    const openFile = React.useCallback(async (file) => { importSVGString(await file.text()); }, [importSVGString]);
    /* ── new / reset ── */
    const newCanvas = React.useCallback(() => {
        snap([]);
        setShapes([]);
        setSelId(null);
        setZoom(1);
        setPx(0);
        setPy(0);
    }, [snap]);
    /* ── save to library ── */
    const saveToLibrary = React.useCallback(() => {
        if (!onSaveToLibrary)
            return;
        const svg = vectorEditorExport.buildSVGString(shapes, artW, artH);
        const blob = new Blob([svg], { type: "image/svg+xml" });
        const reader = new FileReader();
        reader.onload = () => {
            const defaultName = "drawing.svg";
            const name = window.prompt("Save to library as:", defaultName);
            if (!name || !name.trim())
                return;
            onSaveToLibrary(reader.result, name.trim(), "image/svg+xml");
        };
        reader.readAsDataURL(blob);
    }, [shapes, artW, artH, onSaveToLibrary]);
    /* ── export shortcuts ── */
    const exportSVG = React.useCallback(() => vectorEditorExport.exportSVGFile(shapes, artW, artH), [shapes, artW, artH]);
    const exportPNG = React.useCallback(() => vectorEditorExport.exportPNGFile(shapes, artW, artH), [shapes, artW, artH]);
    const exportJPG = React.useCallback(() => vectorEditorExport.exportJPGFile(shapes, artW, artH), [shapes, artW, artH]);
    const exportWebP = React.useCallback(() => vectorEditorExport.exportWebPFile(shapes, artW, artH), [shapes, artW, artH]);
    const exportBMP = React.useCallback(() => vectorEditorExport.exportBMPFile(shapes, artW, artH), [shapes, artW, artH]);
    /* ── tool switch helper (finishes pen) ── */
    const switchTool = React.useCallback((newTool) => {
        if (tool === "pen" && newTool !== "pen" && pendingShape.current) {
            snap(shapes);
            pendingShape.current = null;
            penPts.current = [];
        }
        setTool(newTool);
    }, [tool, shapes, snap]);
    /* ── keyboard ── */
    React.useEffect(() => {
        const h = (e) => {
            const t = e.target;
            if (t.tagName === "INPUT" || t.tagName === "TEXTAREA")
                return;
            if (e.ctrlKey && e.key === "z") {
                e.preventDefault();
                undo();
                return;
            }
            if (e.ctrlKey && (e.key === "y" || (e.shiftKey && e.key === "Z"))) {
                e.preventDefault();
                redo();
                return;
            }
            if (e.ctrlKey && e.key === "c") {
                e.preventDefault();
                copyShape();
                return;
            }
            if (e.ctrlKey && e.key === "v") {
                e.preventDefault();
                pasteShape();
                return;
            }
            if (e.ctrlKey && e.key === "d") {
                e.preventDefault();
                duplicateShape();
                return;
            }
            if (e.key === "Escape") {
                if (pendingShape.current) {
                    setShapes((prev) => prev.filter((s) => s.id !== pendingShape.current.id));
                    pendingShape.current = null;
                    drawing.current = false;
                    penPts.current = [];
                }
                else {
                    setSelId(null);
                }
                return;
            }
            const km = {
                v: "pointer", r: "rect", u: "roundrect", c: "ellipse", l: "line",
                a: "arrow", g: "polygon", s: "star", p: "pen", t: "text",
            };
            if (km[e.key.toLowerCase()] && !e.ctrlKey && !e.altKey) {
                setTool(km[e.key.toLowerCase()]);
                return;
            }
            if (e.key === "Delete" || e.key === "Backspace")
                deleteShape();
        };
        window.addEventListener("keydown", h);
        return () => window.removeEventListener("keydown", h);
    }, [undo, redo, deleteShape, copyShape, pasteShape, duplicateShape]);
    /* ── return ── */
    return {
        // state
        shapes, selId, tool, fillCol, strokeCol, strokeW, fontSize, fontFamily,
        shapeOpacity, cornerRadius, polygonSides, starPoints2, starInnerRatio,
        zoom, px, py, dlgSvg, svgText, snapGrid, gridSize, hasClipboard,
        boolSecondShape,
        // derived
        selShape,
        // setters
        setTool, setFillCol, setStrokeCol, setStrokeW, setFontSize, setFontFamily,
        setShapeOpacity, setCornerRadius, setPolygonSides, setStarPoints2, setStarInnerRatio,
        setZoom, setDlgSvg, setSvgText, setSnapGrid, setGridSize, setSelId,
        setBoolSecondShape,
        // refs
        boxRef, fInp,
        // mouse handlers
        onDown, onMove, onUp, onWheel, onDblClick,
        // actions
        undo, redo, deleteShape, bringToFront, sendToBack, updateSel,
        copyShape, pasteShape, duplicateShape,
        flipH, flipV, alignShapes,
        unionShapes, subtractShapes, intersectShapes, excludeShapes, outlineStroke,
        importSVGString, openFile, newCanvas,
        exportSVG, exportPNG, exportJPG, exportWebP, exportBMP,
        switchTool, saveToLibrary,
    };
}

exports.useVectorEditor = useVectorEditor;
//# sourceMappingURL=useVectorEditor.js.map
