<?=link_tag('assets/css/potong-plano.css?v='.asset_version('assets/css/potong-plano.css')); ?>

<div class="potong-plano-page">
    <div class="potong-plano-shell">
        <div class="row">
            <div class="col-xl-5 col-lg-5">
                <div class="pp-card pp-config-card">
                    <div class="pp-section-title">
                        <span class="pp-title-icon"><i class="fa fa-clone"></i></span>
                        <span>Konfigurasi Lembar Kertas</span>
                    </div>

                    <div class="pp-divider"></div>

                    <div class="pp-form-block">
                        <div class="pp-block-head">
                            <span>Kertas Plano Utama</span>
                            <small>Bahan Induk</small>
                        </div>
                        <div class="pp-preset-grid" id="planoPresets">
                            <button type="button" class="pp-preset-btn" data-preset="A">A (65 x 90 cm)</button>
                            <button type="button" class="pp-preset-btn" data-preset="B">B (65 x 100 cm)</button>
                            <button type="button" class="pp-preset-btn is-active" data-preset="C">C (79 x 109 cm)</button>
                            <button type="button" class="pp-preset-btn" data-preset="D">D (90 x 120 cm)</button>
                            <button type="button" class="pp-preset-btn" data-preset="CUSTOM">Custom</button>
                        </div>

                        <div class="pp-input-grid">
                            <label>
                                <span>Lebar Plano (cm)</span>
                                <input type="number" min="1" step="0.1" id="planoWidth" value="79">
                            </label>
                            <label>
                                <span>Tinggi Plano (cm)</span>
                                <input type="number" min="1" step="0.1" id="planoHeight" value="109">
                            </label>
                        </div>
                    </div>

                    <div class="pp-divider"></div>

                    <div class="pp-form-block">
                        <div class="pp-block-head">
                            <span>Ukuran Hasil Potong</span>
                            <small class="pp-soft-badge">Anak Kertas</small>
                        </div>
                        <div id="cutSizeList" class="pp-cut-size-list">
                            <div class="pp-cut-size-item" data-cut-id="1">
                                <div class="pp-cut-size-head">
                                    <strong>Ukuran 1</strong>
                                </div>
                                <select class="pp-paper-select pp-cut-preset">
                                    <option value="A3P_DIGITAL" selected>A3+ Digital (32 x 48 cm)</option>
                                    <option value="A3P_STANDARD">A3+ Standard (32.5 x 48.8 cm)</option>
                                    <option value="A3">A3 (29.7 x 42 cm)</option>
                                    <option value="A4">A4 (21 x 29.7 cm)</option>
                                    <option value="A5">A5 (14.8 x 21 cm)</option>
                                    <option value="A6">A6 (10.5 x 14.8 cm)</option>
                                    <option value="F4">F4 / Folio (21.5 x 33 cm)</option>
                                    <option value="BUSINESS_CARD">Kartu Nama (9 x 5.5 cm)</option>
                                    <option value="CUSTOM">Kustom Potongan</option>
                                </select>
                                <div class="pp-input-grid">
                                    <label>
                                        <span>Lebar Potongan (cm)</span>
                                        <input type="number" min="0.1" step="0.1" class="pp-cut-width" value="32">
                                    </label>
                                    <label>
                                        <span>Tinggi Potongan (cm)</span>
                                        <input type="number" min="0.1" step="0.1" class="pp-cut-height" value="48">
                                    </label>
                                </div>
                                <label class="pp-cut-target-field">
                                    <span>Jumlah Target Ukuran 1 (pcs)</span>
                                    <input type="number" min="1" step="1" class="pp-cut-target" value="10">
                                </label>
                            </div>
                        </div>
                        <button type="button" class="pp-add-cut-btn" id="addCutSize">
                            <i class="fa fa-plus"></i>
                            Tambah Ukuran Hasil Potong
                        </button>
                        <p class="pp-cut-help">Komposisi setiap ukuran dioptimalkan sesuai target masing-masing dan dimuat minimal satu kali pada plano bila ruang memungkinkan.</p>
                    </div>

                    <div class="pp-divider"></div>

                    <div class="pp-form-block">
                        <div class="pp-block-head">
                            <span>Grip (Sisi Cetak/Jarum) & Jarak Pisau</span>
                        </div>
                        <div class="pp-input-grid">
                            <label>
                                <span>Margin Sisi Plano (cm)</span>
                                <input type="number" min="0" step="0.1" id="marginSize" value="0">
                            </label>
                            <label>
                                <span>Cutter / Jarak Potong (cm)</span>
                                <input type="number" min="0" step="0.1" id="gapSize" value="0">
                            </label>
                        </div>
                    </div>
                </div>

                <div class="pp-card pp-option-card">
                    <div class="pp-block-head">
                        <span>Mode Orientasi & Optimasi Sisa</span>
                    </div>
                    <div class="pp-mode-grid" id="modeGroup">
                        <button type="button" class="pp-mode-btn is-active" data-mode="auto">
                            <i class="fa fa-refresh"></i>
                            <span>Auto (Campur)</span>
                        </button>
                        <button type="button" class="pp-mode-btn" data-mode="portrait">
                            <span class="pp-mode-icon portrait"></span>
                            <span>Potret Saja</span>
                        </button>
                        <button type="button" class="pp-mode-btn" data-mode="landscape">
                            <span class="pp-mode-icon landscape"></span>
                            <span>Lanskap Saja</span>
                        </button>
                    </div>
                    <div class="pp-hint-line" id="modeHint">
                        <i class="fa fa-info-circle"></i>
                        <span>Menyeleksi kombinasi portrait & landscape terbaik untuk hasil cutting terbanyak.</span>
                    </div>
                </div>

                <div class="pp-card pp-need-card">
                    <div class="pp-block-head">
                        <span>Simulasi Kebutuhan Plano Utama</span>
                    </div>
                    <div class="pp-need-row">
                        <div class="pp-target-summary">
                            <span>Total Target Seluruh Ukuran</span>
                            <strong id="totalTarget">10 pcs</strong>
                        </div>
                        <div class="pp-need-result">
                            <span id="neededPlano">Butuh 3 Plano</span>
                        </div>
                    </div>
                    <p class="pp-micro-copy" id="needCopy">1 plano = 4 pcs hasil potong. Pembelian kertas plano sebaiknya dilebihkan sekitar 3% untuk sisa peminggiran produksi (insheet cetak).</p>
                </div>
            </div>

            <div class="col-xl-7 col-lg-7">
                <div class="pp-stats-row">
                    <div class="pp-stat-card">
                        <span>Total Hasil Potong</span>
                        <strong class="pp-stat-value" id="statPieces">4</strong>
                        <small>Pcs per Plano</small>
                    </div>
                    <div class="pp-stat-card">
                        <span>Pemanfaatan Area</span>
                        <strong class="pp-stat-value pp-tone-warning" id="statUsed">71.4%</strong>
                        <small>Kertas Terpakai</small>
                    </div>
                    <div class="pp-stat-card">
                        <span>Kertas Terbuang</span>
                        <strong class="pp-stat-value pp-tone-danger" id="statWaste">28.6%</strong>
                        <small id="statWasteCm">Sisa / Waste (2467 cm2)</small>
                    </div>
                </div>

                <div class="pp-card pp-map-card">
                    <div class="pp-map-head">
                        <div>
                            <span>Peta Pembagian Kertas</span>
                            <h3 id="layoutTitle">Potret Saja (Grid Tegak)</h3>
                        </div>
                        <div class="pp-map-size">
                            <span id="resultSize">Hasil: 32 x 48 cm</span>
                            <span id="planoSize">Plano: 79 x 109 cm</span>
                        </div>
                    </div>

                    <div class="pp-divider"></div>

                    <div class="pp-paper-stage">
                        <svg id="paperMap" class="pp-paper-map" role="img" aria-label="Peta pembagian kertas plano"></svg>
                    </div>

                    <div class="pp-legend-row" id="paperLegend">
                        <span><i class="pp-legend-box portrait"></i>Portrait</span>
                        <span><i class="pp-legend-box landscape"></i>Landscape</span>
                        <span><i class="pp-legend-box waste"></i>Sisa / Waste</span>
                    </div>

                    <div class="pp-info-box">
                        <i class="fa fa-info-circle"></i>
                        <div>
                            <strong>Informasi Pemotongan Kertas</strong>
                            <p id="cuttingInfo">Sisa kertas dapat disimpan sebagai potongan custom untuk pesanan nota mini, kupon, gantungan kunci, atau kartu nama. Pastikan arah serat kertas (grain direction) plano tegak lurus dengan pelipatan hasil akhir bila produk butuh ditekuk (seperti brosur lipat / cover buku).</p>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

<script>
(function () {
    var planoPresets = {
        A: { width: 65, height: 90 },
        B: { width: 65, height: 100 },
        C: { width: 79, height: 109 },
        D: { width: 90, height: 120 }
    };

    var cutPresets = {
        A3P_DIGITAL: { width: 32, height: 48 },
        A3P_STANDARD: { width: 32.5, height: 48.8 },
        A3: { width: 29.7, height: 42 },
        A4: { width: 21, height: 29.7 },
        A5: { width: 14.8, height: 21 },
        A6: { width: 10.5, height: 14.8 },
        F4: { width: 21.5, height: 33 },
        BUSINESS_CARD: { width: 9, height: 5.5 }
    };

    var state = {
        mode: 'auto',
        nextCutId: 2
    };

    var el = {
        planoPresets: document.getElementById('planoPresets'),
        cutSizeList: document.getElementById('cutSizeList'),
        addCutSize: document.getElementById('addCutSize'),
        planoWidth: document.getElementById('planoWidth'),
        planoHeight: document.getElementById('planoHeight'),
        marginSize: document.getElementById('marginSize'),
        gapSize: document.getElementById('gapSize'),
        totalTarget: document.getElementById('totalTarget'),
        modeGroup: document.getElementById('modeGroup'),
        modeHint: document.getElementById('modeHint'),
        statPieces: document.getElementById('statPieces'),
        statUsed: document.getElementById('statUsed'),
        statWaste: document.getElementById('statWaste'),
        statWasteCm: document.getElementById('statWasteCm'),
        neededPlano: document.getElementById('neededPlano'),
        needCopy: document.getElementById('needCopy'),
        layoutTitle: document.getElementById('layoutTitle'),
        resultSize: document.getElementById('resultSize'),
        planoSize: document.getElementById('planoSize'),
        paperMap: document.getElementById('paperMap'),
        paperLegend: document.getElementById('paperLegend'),
        cuttingInfo: document.getElementById('cuttingInfo')
    };

    function numberValue(input, fallback) {
        var parsed = parseFloat(String(input.value).replace(',', '.'));
        return isFinite(parsed) && parsed >= 0 ? parsed : fallback;
    }

    function cleanNumber(value) {
        return parseFloat(value.toFixed(2)).toString();
    }

    function percent(value) {
        return (Math.round(value * 10) / 10).toFixed(1) + '%';
    }

    function fitCount(areaSize, pieceSize, gap) {
        if (pieceSize <= 0 || areaSize < pieceSize) {
            return 0;
        }
        return Math.floor((areaSize + gap) / (pieceSize + gap));
    }

    function buildGrid(area, pieceWidth, pieceHeight, gap, type, startNo, forcedCols, forcedRows) {
        var cols = typeof forcedCols === 'number' ? forcedCols : fitCount(area.width, pieceWidth, gap);
        var rows = typeof forcedRows === 'number' ? forcedRows : fitCount(area.height, pieceHeight, gap);
        var pieces = [];
        var no = startNo;

        for (var row = 0; row < rows; row++) {
            for (var col = 0; col < cols; col++) {
                pieces.push({
                    x: area.x + col * (pieceWidth + gap),
                    y: area.y + row * (pieceHeight + gap),
                    width: pieceWidth,
                    height: pieceHeight,
                    type: type,
                    no: no
                });
                no++;
            }
        }

        return {
            pieces: pieces,
            cols: cols,
            rows: rows,
            count: pieces.length,
            usedWidth: cols > 0 ? cols * pieceWidth + Math.max(0, cols - 1) * gap : 0,
            usedHeight: rows > 0 ? rows * pieceHeight + Math.max(0, rows - 1) * gap : 0
        };
    }

    function uniformLayout(settings, orientation) {
        var piece = orientation === 'landscape'
            ? { width: settings.cutHeight, height: settings.cutWidth, type: 'landscape' }
            : { width: settings.cutWidth, height: settings.cutHeight, type: 'portrait' };
        var inner = {
            x: settings.margin,
            y: settings.margin,
            width: Math.max(0, settings.planoWidth - (settings.margin * 2)),
            height: Math.max(0, settings.planoHeight - (settings.margin * 2))
        };
        var grid = buildGrid(inner, piece.width, piece.height, settings.gap, piece.type, 1);

        return {
            mode: orientation,
            label: orientation === 'landscape' ? 'Lanskap Saja (Grid Mendatar)' : 'Potret Saja (Grid Tegak)',
            pieces: grid.pieces,
            count: grid.count,
            primary: orientation
        };
    }

    function mixedVertical(settings, firstType) {
        var inner = {
            x: settings.margin,
            y: settings.margin,
            width: Math.max(0, settings.planoWidth - (settings.margin * 2)),
            height: Math.max(0, settings.planoHeight - (settings.margin * 2))
        };
        var first = firstType === 'landscape'
            ? { width: settings.cutHeight, height: settings.cutWidth, type: 'landscape' }
            : { width: settings.cutWidth, height: settings.cutHeight, type: 'portrait' };
        var second = firstType === 'landscape'
            ? { width: settings.cutWidth, height: settings.cutHeight, type: 'portrait' }
            : { width: settings.cutHeight, height: settings.cutWidth, type: 'landscape' };
        var maxFirstCols = fitCount(inner.width, first.width, settings.gap);
        var best = null;

        for (var cols = 1; cols <= maxFirstCols; cols++) {
            var firstWidth = cols * first.width + Math.max(0, cols - 1) * settings.gap;
            var secondX = inner.x + firstWidth + settings.gap;
            var secondWidth = inner.width - firstWidth - settings.gap;
            var left = buildGrid(inner, first.width, first.height, settings.gap, first.type, 1, cols);
            var right = secondWidth > 0 ? buildGrid({ x: secondX, y: inner.y, width: secondWidth, height: inner.height }, second.width, second.height, settings.gap, second.type, left.count + 1) : { pieces: [], count: 0 };
            var candidate = {
                mode: 'mixed',
                label: 'Auto Campur (Kombinasi)',
                pieces: left.pieces.concat(right.pieces),
                count: left.count + right.count,
                primary: firstType
            };
            if (!best || candidate.count > best.count) {
                best = candidate;
            }
        }

        return best;
    }

    function mixedHorizontal(settings, firstType) {
        var inner = {
            x: settings.margin,
            y: settings.margin,
            width: Math.max(0, settings.planoWidth - (settings.margin * 2)),
            height: Math.max(0, settings.planoHeight - (settings.margin * 2))
        };
        var first = firstType === 'landscape'
            ? { width: settings.cutHeight, height: settings.cutWidth, type: 'landscape' }
            : { width: settings.cutWidth, height: settings.cutHeight, type: 'portrait' };
        var second = firstType === 'landscape'
            ? { width: settings.cutWidth, height: settings.cutHeight, type: 'portrait' }
            : { width: settings.cutHeight, height: settings.cutWidth, type: 'landscape' };
        var maxFirstRows = fitCount(inner.height, first.height, settings.gap);
        var best = null;

        for (var rows = 1; rows <= maxFirstRows; rows++) {
            var firstHeight = rows * first.height + Math.max(0, rows - 1) * settings.gap;
            var secondY = inner.y + firstHeight + settings.gap;
            var secondHeight = inner.height - firstHeight - settings.gap;
            var top = buildGrid(inner, first.width, first.height, settings.gap, first.type, 1, null, rows);
            var bottom = secondHeight > 0 ? buildGrid({ x: inner.x, y: secondY, width: inner.width, height: secondHeight }, second.width, second.height, settings.gap, second.type, top.count + 1) : { pieces: [], count: 0 };
            var candidate = {
                mode: 'mixed',
                label: 'Auto Campur (Kombinasi)',
                pieces: top.pieces.concat(bottom.pieces),
                count: top.count + bottom.count,
                primary: firstType
            };
            if (!best || candidate.count > best.count) {
                best = candidate;
            }
        }

        return best;
    }

    function chooseLayout(settings) {
        if (settings.cuts.length > 1) {
            return multiSizeLayout(settings);
        }

        var portrait = uniformLayout(settings, 'portrait');
        var landscape = uniformLayout(settings, 'landscape');

        if (state.mode === 'portrait') {
            return portrait;
        }
        if (state.mode === 'landscape') {
            return landscape;
        }

        var candidates = [
            portrait,
            landscape,
            mixedVertical(settings, 'portrait'),
            mixedVertical(settings, 'landscape'),
            mixedHorizontal(settings, 'portrait'),
            mixedHorizontal(settings, 'landscape')
        ].filter(Boolean);

        candidates.sort(function (a, b) {
            if (b.count !== a.count) {
                return b.count - a.count;
            }
            if (a.mode === 'mixed' && b.mode !== 'mixed') {
                return 1;
            }
            if (b.mode === 'mixed' && a.mode !== 'mixed') {
                return -1;
            }
            return a.primary === 'portrait' ? -1 : 1;
        });

        return candidates[0] || portrait;
    }

    function orientationsFor(cut) {
        var result = [];
        if (state.mode !== 'landscape') {
            result.push({ width: cut.width, height: cut.height, orientation: 'portrait' });
        }
        if (state.mode !== 'portrait' && (cut.width !== cut.height || !result.length)) {
            result.push({ width: cut.height, height: cut.width, orientation: 'landscape' });
        }
        return result;
    }

    function placeInFreeRect(freeRects, option, cut, splitMode, pieces) {
        var bestIndex = -1;
        var bestScore = Infinity;
        for (var i = 0; i < freeRects.length; i++) {
            var free = freeRects[i];
            if (option.width <= free.width + 0.0001 && option.height <= free.height + 0.0001) {
                var score = (free.width * free.height) - (option.width * option.height);
                if (score < bestScore) {
                    bestScore = score;
                    bestIndex = i;
                }
            }
        }
        if (bestIndex < 0) {
            return false;
        }

        var target = freeRects.splice(bestIndex, 1)[0];
        pieces.push({
            x: target.x,
            y: target.y,
            width: option.width,
            height: option.height,
            type: option.orientation,
            cutIndex: cut.index,
            no: 0
        });

        var rightWidth = target.width - option.width - cut.gap;
        var bottomHeight = target.height - option.height - cut.gap;
        if (splitMode === 0) {
            if (rightWidth > 0) {
                freeRects.push({ x: target.x + option.width + cut.gap, y: target.y, width: rightWidth, height: option.height });
            }
            if (bottomHeight > 0) {
                freeRects.push({ x: target.x, y: target.y + option.height + cut.gap, width: target.width, height: bottomHeight });
            }
        } else {
            if (rightWidth > 0) {
                freeRects.push({ x: target.x + option.width + cut.gap, y: target.y, width: rightWidth, height: target.height });
            }
            if (bottomHeight > 0) {
                freeRects.push({ x: target.x, y: target.y + option.height + cut.gap, width: option.width, height: bottomHeight });
            }
        }
        return true;
    }

    function buildMultiCandidate(settings, order, splitMode, fillStrategy) {
        var innerWidth = Math.max(0, settings.planoWidth - (settings.margin * 2));
        var innerHeight = Math.max(0, settings.planoHeight - (settings.margin * 2));
        var freeRects = [{ x: settings.margin, y: settings.margin, width: innerWidth, height: innerHeight }];
        var pieces = [];
        var runningCounts = {};

        for (var o = 0; o < order.length; o++) {
            var requiredCut = settings.cuts[order[o]];
            var requiredOptions = orientationsFor(requiredCut).sort(function (a, b) {
                return (b.width * b.height) - (a.width * a.height);
            });
            var placed = false;
            for (var ro = 0; ro < requiredOptions.length && !placed; ro++) {
                placed = placeInFreeRect(freeRects, requiredOptions[ro], requiredCut, splitMode, pieces);
            }
            if (!placed) {
                return null;
            }
            runningCounts[requiredCut.index] = 1;
        }

        for (var guard = 0; guard < 1000; guard++) {
            var choices = [];
            settings.cuts.forEach(function (cut) {
                orientationsFor(cut).forEach(function (option) {
                    freeRects.forEach(function (free, freeIndex) {
                        if (option.width <= free.width + 0.0001 && option.height <= free.height + 0.0001) {
                            var area = option.width * option.height;
                            var coverage = (runningCounts[cut.index] || 0) / cut.target;
                            var targetPriority = 1 / (coverage + (1 / cut.target));
                            choices.push({
                                cut: cut,
                                option: option,
                                freeIndex: freeIndex,
                                score: fillStrategy === 'target'
                                    ? targetPriority
                                    : (fillStrategy === 'count' ? targetPriority / area : targetPriority * area)
                            });
                        }
                    });
                });
            });
            if (!choices.length) {
                break;
            }
            choices.sort(function (a, b) {
                if (b.score !== a.score) {
                    return b.score - a.score;
                }
                return a.freeIndex - b.freeIndex;
            });
            if (!placeInFreeRect(freeRects, choices[0].option, choices[0].cut, splitMode, pieces)) {
                break;
            }
            runningCounts[choices[0].cut.index] = (runningCounts[choices[0].cut.index] || 0) + 1;
        }

        var counts = {};
        var usedArea = 0;
        pieces.forEach(function (piece, index) {
            piece.no = index + 1;
            counts[piece.cutIndex] = (counts[piece.cutIndex] || 0) + 1;
            usedArea += piece.width * piece.height;
        });
        var planoNeeded = 0;
        var totalProduced = 0;
        var totalTarget = 0;
        settings.cuts.forEach(function (cut) {
            var count = counts[cut.index] || 0;
            planoNeeded = count > 0 ? Math.max(planoNeeded, Math.ceil(cut.target / count)) : Infinity;
            totalProduced += count;
            totalTarget += cut.target;
        });
        var overproduction = isFinite(planoNeeded)
            ? settings.cuts.reduce(function (sum, cut) {
                return sum + Math.max(0, ((counts[cut.index] || 0) * planoNeeded) - cut.target);
            }, 0)
            : Infinity;
        return {
            mode: 'multi',
            label: 'Multi Ukuran (Optimasi Kombinasi)',
            pieces: pieces,
            count: pieces.length,
            counts: counts,
            usedArea: usedArea,
            planoNeeded: planoNeeded,
            overproduction: overproduction,
            targetBalance: totalTarget > 0 ? totalProduced / totalTarget : 0,
            primary: 'multi'
        };
    }

    function multiSizeLayout(settings) {
        var candidates = [];
        var baseOrder = settings.cuts.map(function (cut, index) { return index; });
        var orders = [];

        function addOrders(prefix, remaining) {
            if (!remaining.length) {
                orders.push(prefix);
                return;
            }
            remaining.forEach(function (value, index) {
                addOrders(prefix.concat([value]), remaining.slice(0, index).concat(remaining.slice(index + 1)));
            });
        }

        addOrders([], baseOrder);
        orders.forEach(function (order) {
            [0, 1].forEach(function (splitMode) {
                ['target', 'area', 'count'].forEach(function (strategy) {
                    var candidate = buildMultiCandidate(settings, order, splitMode, strategy);
                    if (candidate) {
                        candidates.push(candidate);
                    }
                });
            });
        });
        candidates.sort(function (a, b) {
            if (a.planoNeeded !== b.planoNeeded) {
                return a.planoNeeded - b.planoNeeded;
            }
            if (a.overproduction !== b.overproduction) {
                return a.overproduction - b.overproduction;
            }
            if (b.usedArea !== a.usedArea) {
                return b.usedArea - a.usedArea;
            }
            return b.count - a.count;
        });
        return candidates[0] || {
            mode: 'multi',
            label: 'Multi Ukuran Belum Muat',
            pieces: [],
            count: 0,
            counts: {},
            usedArea: 0,
            primary: 'multi'
        };
    }

    function readCuts() {
        return Array.prototype.map.call(el.cutSizeList.querySelectorAll('.pp-cut-size-item'), function (item, index) {
            return {
                index: index,
                id: item.getAttribute('data-cut-id'),
                width: Math.max(0.1, numberValue(item.querySelector('.pp-cut-width'), 32)),
                height: Math.max(0.1, numberValue(item.querySelector('.pp-cut-height'), 48)),
                target: Math.max(1, Math.ceil(numberValue(item.querySelector('.pp-cut-target'), 10))),
                gap: Math.max(0, numberValue(el.gapSize, 0))
            };
        });
    }

    function settings() {
        var cuts = readCuts();
        return {
            planoWidth: Math.max(1, numberValue(el.planoWidth, 79)),
            planoHeight: Math.max(1, numberValue(el.planoHeight, 109)),
            cutWidth: cuts[0].width,
            cutHeight: cuts[0].height,
            cuts: cuts,
            margin: Math.max(0, numberValue(el.marginSize, 0)),
            gap: Math.max(0, numberValue(el.gapSize, 0))
        };
    }

    function drawMap(settings, layout) {
        var svg = el.paperMap;
        var ns = 'http://www.w3.org/2000/svg';
        var paperWidth = settings.planoWidth;
        var paperHeight = settings.planoHeight;
        svg.setAttribute('viewBox', '0 0 ' + paperWidth + ' ' + paperHeight);
        svg.innerHTML = '';

        var defs = document.createElementNS(ns, 'defs');
        var pattern = document.createElementNS(ns, 'pattern');
        pattern.setAttribute('id', 'wastePattern');
        pattern.setAttribute('patternUnits', 'userSpaceOnUse');
        pattern.setAttribute('width', '6');
        pattern.setAttribute('height', '6');
        pattern.setAttribute('patternTransform', 'rotate(45)');
        var line = document.createElementNS(ns, 'line');
        line.setAttribute('x1', '0');
        line.setAttribute('y1', '0');
        line.setAttribute('x2', '0');
        line.setAttribute('y2', '6');
        line.setAttribute('stroke', '#f43f5e');
        line.setAttribute('stroke-width', '0.35');
        line.setAttribute('opacity', '0.35');
        pattern.appendChild(line);
        defs.appendChild(pattern);
        svg.appendChild(defs);

        var base = document.createElementNS(ns, 'rect');
        base.setAttribute('x', '0');
        base.setAttribute('y', '0');
        base.setAttribute('width', paperWidth);
        base.setAttribute('height', paperHeight);
        base.setAttribute('fill', 'url(#wastePattern)');
        base.setAttribute('stroke', '#f43f5e');
        base.setAttribute('stroke-width', '0.6');
        base.setAttribute('stroke-dasharray', '1.8 1.5');
        svg.appendChild(base);

        var colors = [
            { stroke: '#4f46e5', fill: 'rgba(79,70,229,0.1)', text: '#4f46e5', soft: '#818cf8' },
            { stroke: '#f43f5e', fill: 'rgba(244,63,94,0.1)', text: '#db2777', soft: '#fb7185' },
            { stroke: '#0ea5e9', fill: 'rgba(14,165,233,0.1)', text: '#0284c7', soft: '#38bdf8' },
            { stroke: '#10b981', fill: 'rgba(16,185,129,0.1)', text: '#059669', soft: '#34d399' }
        ];
        layout.pieces.forEach(function (piece) {
            var group = document.createElementNS(ns, 'g');
            var rect = document.createElementNS(ns, 'rect');
            var isLandscape = piece.type === 'landscape';
            var color = layout.mode === 'multi' ? colors[piece.cutIndex % colors.length] : (isLandscape ? colors[1] : colors[0]);
            rect.setAttribute('x', piece.x);
            rect.setAttribute('y', piece.y);
            rect.setAttribute('width', piece.width);
            rect.setAttribute('height', piece.height);
            rect.setAttribute('fill', color.fill);
            rect.setAttribute('stroke', color.stroke);
            rect.setAttribute('stroke-width', '0.45');
            group.appendChild(rect);

            var numberLabel = String(piece.no);
            var hasPieceLabel = layout.pieces.length <= 24 && Math.min(piece.width, piece.height) >= 14;
            var maxNumberWidth = Math.max(1, piece.width - 0.8);
            var maxNumberHeight = piece.height * (hasPieceLabel ? 0.38 : 0.58);
            var numberWidthFit = maxNumberWidth / Math.max(1, numberLabel.length * 0.62);
            var numberHeightFit = Math.max(1, maxNumberHeight);
            var numberSize = Math.max(1.15, Math.min(numberWidthFit, numberHeightFit, Math.min(piece.width, piece.height) * 0.36));

            var text = document.createElementNS(ns, 'text');
            text.setAttribute('x', piece.x + piece.width / 2);
            text.setAttribute('y', piece.y + piece.height / 2 - (hasPieceLabel ? Math.min(piece.width, piece.height) * 0.03 : 0));
            text.setAttribute('text-anchor', 'middle');
            text.setAttribute('dominant-baseline', 'middle');
            text.setAttribute('fill', color.text);
            text.setAttribute('font-size', numberSize.toFixed(2));
            text.setAttribute('font-weight', '800');
            text.textContent = numberLabel;
            group.appendChild(text);

            if (hasPieceLabel) {
                var label = document.createElementNS(ns, 'text');
                label.setAttribute('x', piece.x + piece.width / 2);
                label.setAttribute('y', piece.y + piece.height / 2 + Math.min(piece.width, piece.height) * 0.16);
                label.setAttribute('text-anchor', 'middle');
                label.setAttribute('dominant-baseline', 'middle');
                label.setAttribute('fill', color.soft);
                label.setAttribute('font-size', Math.max(2.7, Math.min(piece.width, piece.height) * 0.09));
                label.setAttribute('font-weight', '800');
                label.textContent = layout.mode === 'multi' ? 'U' + (piece.cutIndex + 1) : (isLandscape ? 'LS' : 'PT');
                group.appendChild(label);
            }

            svg.appendChild(group);
        });
    }

    function update() {
        var current = settings();
        var layout = chooseLayout(current);
        var planoArea = current.planoWidth * current.planoHeight;
        var usedArea = layout.mode === 'multi'
            ? layout.usedArea
            : (current.cutWidth * current.cutHeight * layout.count);
        var usedPercent = planoArea > 0 ? (usedArea / planoArea) * 100 : 0;
        var wasteArea = Math.max(0, planoArea - usedArea);
        var wastePercent = planoArea > 0 ? (wasteArea / planoArea) * 100 : 0;
        var needPlano = layout.count > 0
            ? current.cuts.reduce(function (needed, cut, index) {
                var perPlano = layout.counts ? (layout.counts[index] || 0) : layout.count;
                return perPlano > 0 ? Math.max(needed, Math.ceil(cut.target / perPlano)) : Infinity;
            }, 0)
            : 0;
        var totalTarget = current.cuts.reduce(function (sum, cut) { return sum + cut.target; }, 0);

        el.statPieces.textContent = layout.count;
        el.statUsed.textContent = percent(usedPercent);
        el.statWaste.textContent = percent(wastePercent);
        el.statWasteCm.textContent = 'Sisa / Waste (' + Math.round(wasteArea) + ' cm2)';
        el.totalTarget.textContent = totalTarget + ' pcs';
        el.neededPlano.textContent = layout.count > 0 && isFinite(needPlano) ? 'Butuh ' + needPlano + ' Plano' : 'Belum Muat';
        var countSummary = current.cuts.map(function (cut, index) {
            var perPlano = layout.counts ? (layout.counts[index] || 0) : layout.count;
            return 'Ukuran ' + (index + 1) + ': ' + perPlano + '/plano, target ' + cut.target +
                ', hasil ' + (isFinite(needPlano) ? perPlano * needPlano : 0) + ' pcs';
        }).join(', ');
        el.needCopy.textContent = layout.count > 0 && isFinite(needPlano)
            ? '1 plano = ' + layout.count + ' pcs hasil potong. ' + needPlano + ' plano menghasilkan: ' + countSummary + '. Pembelian kertas plano sebaiknya dilebihkan sekitar 3% untuk sisa peminggiran produksi (insheet cetak).'
            : 'Ukuran potongan belum muat pada plano dengan margin dan jarak pisau saat ini.';
        el.layoutTitle.textContent = layout.label;
        el.resultSize.textContent = 'Hasil: ' + current.cuts.map(function (cut) {
            return cleanNumber(cut.width) + ' x ' + cleanNumber(cut.height);
        }).join(' + ') + ' cm';
        el.planoSize.textContent = 'Plano: ' + cleanNumber(current.planoWidth) + ' x ' + cleanNumber(current.planoHeight) + ' cm';
        if (current.cuts.length > 1) {
            var legendClasses = ['portrait', 'landscape', 'size-three', 'size-four'];
            el.paperLegend.innerHTML = current.cuts.map(function (cut, index) {
                return '<span><i class="pp-legend-box ' + legendClasses[index] + '"></i>Ukuran ' + (index + 1) +
                    ' (' + cleanNumber(cut.width) + ' x ' + cleanNumber(cut.height) + ') · ' +
                    (layout.counts[index] || 0) + '/plano · target ' + cut.target + '</span>';
            }).join('') + '<span><i class="pp-legend-box waste"></i>Sisa / Waste</span>';
        } else {
            el.paperLegend.innerHTML = '<span><i class="pp-legend-box portrait"></i>Portrait · target ' + current.cuts[0].target + '</span>' +
                '<span><i class="pp-legend-box landscape"></i>Landscape · target ' + current.cuts[0].target + '</span>' +
                '<span><i class="pp-legend-box waste"></i>Sisa / Waste</span>';
        }
        el.cuttingInfo.textContent = layout.count > 0 && isFinite(needPlano)
            ? 'Layout disusun menurut perbandingan target tiap ukuran. Dengan ' + needPlano + ' plano: ' +
                current.cuts.map(function (cut, index) {
                    var perPlano = layout.counts ? (layout.counts[index] || 0) : layout.count;
                    return 'Ukuran ' + (index + 1) + ' menghasilkan ' + (perPlano * needPlano) + ' pcs dari target ' + cut.target;
                }).join('; ') + '. Kelebihan hasil merupakan konsekuensi pembulatan ke lembar plano utuh.'
            : 'Satu atau lebih ukuran belum dapat dimuat pada plano dengan margin, jarak pisau, dan orientasi saat ini.';

        drawMap(current, layout);
    }

    el.planoPresets.addEventListener('click', function (event) {
        var button = event.target.closest('[data-preset]');
        if (!button) {
            return;
        }
        Array.prototype.forEach.call(el.planoPresets.querySelectorAll('.pp-preset-btn'), function (item) {
            item.classList.remove('is-active');
        });
        button.classList.add('is-active');

        var preset = planoPresets[button.getAttribute('data-preset')];
        if (preset) {
            el.planoWidth.value = preset.width;
            el.planoHeight.value = preset.height;
        }
        update();
    });

    function cutOptions(selected) {
        var options = [
            ['A3P_DIGITAL', 'A3+ Digital (32 x 48 cm)'],
            ['A3P_STANDARD', 'A3+ Standard (32.5 x 48.8 cm)'],
            ['A3', 'A3 (29.7 x 42 cm)'],
            ['A4', 'A4 (21 x 29.7 cm)'],
            ['A5', 'A5 (14.8 x 21 cm)'],
            ['A6', 'A6 (10.5 x 14.8 cm)'],
            ['F4', 'F4 / Folio (21.5 x 33 cm)'],
            ['BUSINESS_CARD', 'Kartu Nama (9 x 5.5 cm)'],
            ['CUSTOM', 'Kustom Potongan']
        ];
        return options.map(function (option) {
            return '<option value="' + option[0] + '"' + (option[0] === selected ? ' selected' : '') + '>' + option[1] + '</option>';
        }).join('');
    }

    function renumberCutSizes() {
        var items = el.cutSizeList.querySelectorAll('.pp-cut-size-item');
        Array.prototype.forEach.call(items, function (item, index) {
            item.querySelector('.pp-cut-size-head strong').textContent = 'Ukuran ' + (index + 1);
            item.querySelector('.pp-cut-target-field span').textContent = 'Jumlah Target Ukuran ' + (index + 1) + ' (pcs)';
            var removeButton = item.querySelector('.pp-remove-cut-btn');
            if (removeButton) {
                removeButton.setAttribute('aria-label', 'Hapus ukuran ' + (index + 1));
            }
        });
    }

    el.addCutSize.addEventListener('click', function () {
        if (el.cutSizeList.querySelectorAll('.pp-cut-size-item').length >= 4) {
            return;
        }
        var item = document.createElement('div');
        item.className = 'pp-cut-size-item';
        item.setAttribute('data-cut-id', state.nextCutId++);
        item.innerHTML =
            '<div class="pp-cut-size-head"><strong>Ukuran Baru</strong>' +
            '<button type="button" class="pp-remove-cut-btn" title="Hapus ukuran"><i class="fa fa-times"></i></button></div>' +
            '<select class="pp-paper-select pp-cut-preset">' + cutOptions('A4') + '</select>' +
            '<div class="pp-input-grid">' +
            '<label><span>Lebar Potongan (cm)</span><input type="number" min="0.1" step="0.1" class="pp-cut-width" value="21"></label>' +
            '<label><span>Tinggi Potongan (cm)</span><input type="number" min="0.1" step="0.1" class="pp-cut-height" value="29.7"></label>' +
            '</div>' +
            '<label class="pp-cut-target-field"><span>Jumlah Target (pcs)</span><input type="number" min="1" step="1" class="pp-cut-target" value="10"></label>';
        el.cutSizeList.appendChild(item);
        renumberCutSizes();
        el.addCutSize.style.display = el.cutSizeList.querySelectorAll('.pp-cut-size-item').length >= 4 ? 'none' : '';
        update();
    });

    el.cutSizeList.addEventListener('click', function (event) {
        var removeButton = event.target.closest('.pp-remove-cut-btn');
        if (!removeButton) {
            return;
        }
        removeButton.closest('.pp-cut-size-item').remove();
        renumberCutSizes();
        el.addCutSize.style.display = '';
        update();
    });

    el.cutSizeList.addEventListener('change', function (event) {
        if (!event.target.classList.contains('pp-cut-preset')) {
            return;
        }
        var item = event.target.closest('.pp-cut-size-item');
        var preset = cutPresets[event.target.value];
        if (preset) {
            item.querySelector('.pp-cut-width').value = preset.width;
            item.querySelector('.pp-cut-height').value = preset.height;
        }
        update();
    });

    el.cutSizeList.addEventListener('input', function (event) {
        if (!event.target.classList.contains('pp-cut-width') &&
            !event.target.classList.contains('pp-cut-height') &&
            !event.target.classList.contains('pp-cut-target')) {
            return;
        }
        if (!event.target.classList.contains('pp-cut-target')) {
            event.target.closest('.pp-cut-size-item').querySelector('.pp-cut-preset').value = 'CUSTOM';
        }
        update();
    });

    el.modeGroup.addEventListener('click', function (event) {
        var button = event.target.closest('[data-mode]');
        if (!button) {
            return;
        }
        state.mode = button.getAttribute('data-mode');
        Array.prototype.forEach.call(el.modeGroup.querySelectorAll('.pp-mode-btn'), function (item) {
            item.classList.remove('is-active');
        });
        button.classList.add('is-active');

        var messages = {
            auto: 'Menyeleksi kombinasi portrait & landscape terbaik untuk hasil cutting terbanyak.',
            portrait: 'Memaksa kertas dipotong sejajar secara portrait.',
            landscape: 'Memaksa semua potongan ke arah landscape 90 deg.'
        };
        el.modeHint.querySelector('span').textContent = messages[state.mode];
        update();
    });

    [el.planoWidth, el.planoHeight, el.marginSize, el.gapSize].forEach(function (input) {
        input.addEventListener('input', function () {
            if (input === el.planoWidth || input === el.planoHeight) {
                Array.prototype.forEach.call(el.planoPresets.querySelectorAll('.pp-preset-btn'), function (item) {
                    item.classList.toggle('is-active', item.getAttribute('data-preset') === 'CUSTOM');
                });
            }
            update();
        });
    });

    update();
})();
</script>
