<?=link_tag('assets/css/nomorator.css?v='.asset_version('assets/css/nomorator.css')); ?>

<div class="nomorator-page">
    <div class="nomorator-shell">
        <div class="row">
            <div class="col-xl-5 col-lg-5">
                <div class="nm-card nm-config-card">
                    <div class="nm-section-title">
                        <span class="nm-title-mark">#</span>
                        <span>Konfigurasi Urutan Nomor</span>
                    </div>

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

                    <div class="nm-input-grid nm-input-grid-two">
                        <label class="nm-field">
                            <span>Nomor Mulai (Start)</span>
                            <input type="number" min="1" step="1" id="nmStart" value="1">
                        </label>
                        <label class="nm-field">
                            <span>Jumlah Nomor (Qty)</span>
                            <input type="number" min="1" step="1" id="nmQty" value="1000">
                        </label>
                    </div>

                    <div class="nm-range-preview">
                        <span>Urutan : <strong id="nmFirstNumber">INV-0001</strong></span>
                        <i class="fa fa-long-arrow-right" aria-hidden="true"></i>
                        <strong id="nmLastNumber">INV-1000</strong>
                    </div>

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

                    <div class="nm-block-head">
                        <span>Layout Up per Lembar</span>
                        <button type="button" class="nm-link-button" id="nmBookFill">Isi Buku</button>
                    </div>

                    <select id="nmLayoutPreset" class="nm-select">
                        <option value="2x1">1/2 Folio (2 up - 2 kolom x 1 baris)</option>
                        <option value="3x1">1/3 Folio (3 up - 3 kolom x 1 baris)</option>
                        <option value="2x2" selected>1/4 Folio (4 up - 2 kolom x 2 baris)</option>
                        <option value="3x2">1/6 Folio (6 up - 3 kolom x 2 baris)</option>
                        <option value="2x4">1/8 Folio (8 up - 2 kolom x 4 baris)</option>
                        <option value="2x5">Voucher 10-up (2 kolom x 5 baris)</option>
                        <option value="custom">Kustom Grid</option>
                    </select>

                    <div class="nm-stepper-grid">
                        <div class="nm-stepper">
                            <span>Jumlah Kolom (Columns)</span>
                            <div>
                                <button type="button" data-step-target="nmCols" data-delta="-1" aria-label="Kurangi kolom">-</button>
                                <input type="number" min="1" max="12" step="1" id="nmCols" value="2">
                                <button type="button" data-step-target="nmCols" data-delta="1" aria-label="Tambah kolom">+</button>
                            </div>
                        </div>
                        <div class="nm-stepper">
                            <span>Jumlah Baris (Rows)</span>
                            <div>
                                <button type="button" data-step-target="nmRows" data-delta="-1" aria-label="Kurangi baris">-</button>
                                <input type="number" min="1" max="12" step="1" id="nmRows" value="2">
                                <button type="button" data-step-target="nmRows" data-delta="1" aria-label="Tambah baris">+</button>
                            </div>
                        </div>
                    </div>

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

                    <div class="nm-block-head">
                        <span>Format Teks & Digit</span>
                    </div>

                    <div class="nm-input-grid nm-input-grid-three">
                        <label class="nm-field">
                            <span>Digit (Padding)</span>
                            <select id="nmDigits" class="nm-select">
                                <option value="0">Tanpa Nol</option>
                                <option value="2">2 Digit (01)</option>
                                <option value="3">3 Digit (001)</option>
                                <option value="4" selected>4 Digit (0001)</option>
                                <option value="5">5 Digit (00001)</option>
                                <option value="6">6 Digit (000001)</option>
                            </select>
                        </label>
                        <label class="nm-field">
                            <span>Awalan (Prefix)</span>
                            <input type="text" id="nmPrefix" value="INV-">
                        </label>
                        <label class="nm-field">
                            <span>Akhiran (Suffix)</span>
                            <input type="text" id="nmSuffix" placeholder="e.g. /A">
                        </label>
                    </div>

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

                    <div class="nm-block-head">
                        <span>Metode Pengurutan</span>
                    </div>

                    <div class="nm-method-grid" id="nmMethodGroup">
                        <button type="button" class="nm-method-btn is-active" data-method="collated">
                            <i class="fa fa-database" aria-hidden="true"></i>
                            <span>Susun Tumpuk (Blok)</span>
                        </button>
                        <button type="button" class="nm-method-btn" data-method="straight">
                            <i class="fa fa-th-large" aria-hidden="true"></i>
                            <span>Sejajar (Berbaris)</span>
                        </button>
                    </div>

                    <div class="nm-hint-line">
                        <i class="fa fa-info-circle" aria-hidden="true"></i>
                        <span id="nmMethodHint">Urutan nomor meluncur ke bawah menembus tumpukan kertas. Setelah dipotong, tumpukan bukti langsung tersusun runut tanpa perlu sortir manual.</span>
                    </div>
                </div>

                <div class="nm-card nm-export-card">
                    <div class="nm-block-head">
                        <span>Ekspor Data VDP (Variable Merge)</span>
                    </div>
                    <p>Gunakan data ekspor ini untuk di-import langsung sebagai database Print Merge di <strong>CorelDRAW</strong> atau Data Merge di <strong>Adobe InDesign</strong>. Tiap baris data merupakan satu lembar kertas kerja cetak Anda.</p>

                    <div class="nm-export-actions">
                        <button type="button" id="nmCopyTsv" class="nm-export-btn">
                            <i class="fa fa-copy" aria-hidden="true"></i>
                            <span>Salin Excel TSV</span>
                        </button>
                        <button type="button" id="nmDownloadCsv" class="nm-export-btn">
                            <i class="fa fa-download" aria-hidden="true"></i>
                            <span>Unduh File CSV</span>
                        </button>
                    </div>
                </div>
            </div>

            <div class="col-xl-7 col-lg-7">
                <div class="nm-stats-row">
                    <div class="nm-stat-card">
                        <span>Total Lembar Cetak</span>
                        <strong id="nmTotalSheets">250</strong>
                        <small>Lembar kerja (cucian)</small>
                    </div>
                    <div class="nm-stat-card">
                        <span>Total Slot Urutan</span>
                        <strong class="nm-green" id="nmTotalSlots">1000</strong>
                        <small id="nmSlotCapacity">Kapasitas slot (4 up)</small>
                    </div>
                    <div class="nm-stat-card">
                        <span>Slot Lebih (Overage)</span>
                        <strong class="nm-muted-value" id="nmOverage">0</strong>
                        <small>Slot kosong / blanko</small>
                    </div>
                </div>

                <div class="nm-card nm-output-card">
                    <div class="nm-output-head">
                        <div>
                            <span>Output Susunan Operator</span>
                            <h3 id="nmOutputTitle">Metode Susun Tumpuk (Collated)</h3>
                        </div>
                        <div class="nm-tabs" id="nmTabGroup">
                            <button type="button" class="is-active" data-tab="visual">Peta Visual</button>
                            <button type="button" data-tab="table">Tabel Lembar Kerja</button>
                        </div>
                    </div>

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

                    <div class="nm-visual-panel" id="nmVisualPanel">
                        <div class="nm-sheet-toolbar">
                            <button type="button" class="nm-icon-btn" id="nmPrevSheet" aria-label="Lembar sebelumnya">
                                <i class="fa fa-angle-left" aria-hidden="true"></i>
                            </button>
                            <strong id="nmSheetLabel">Lembar 250 / 250</strong>
                            <button type="button" class="nm-icon-btn" id="nmNextSheet" aria-label="Lembar berikutnya">
                                <i class="fa fa-angle-right" aria-hidden="true"></i>
                            </button>
                            <input type="range" min="1" max="250" value="250" id="nmSheetRange">
                            <button type="button" class="nm-small-btn" id="nmFirstSheet">Pertama</button>
                            <button type="button" class="nm-small-btn" id="nmLastSheetBtn">Terakhir</button>
                        </div>

                        <div class="nm-sheet-stage">
                            <div class="nm-stage-label" id="nmStageLabel">Lembar ke-250</div>
                            <div class="nm-paper">
                                <div class="nm-grip-label">Margin grip sisi lipat / mesin cetak</div>
                                <div class="nm-slot-grid" id="nmSlotGrid"></div>
                            </div>
                        </div>
                    </div>

                    <div class="nm-table-panel d-none" id="nmTablePanel">
                        <div class="nm-table-wrap">
                            <table class="nm-data-table">
                                <thead id="nmTableHead"></thead>
                                <tbody id="nmTableBody"></tbody>
                            </table>
                        </div>
                        <div class="nm-table-foot" id="nmTableFoot">Menampilkan 50 lembar pertama.</div>
                    </div>

                    <div class="nm-production-note">
                        <i class="fa fa-exclamation-circle" aria-hidden="true"></i>
                        <div>
                            <strong>Informasi Produksi Nomorator</strong>
                            <p>Sisa slot blanko (kosong) biasanya diletakkan pada lembar-lembar terakhir. Saat menyusun plat digital, pastikan slot layout placeholder sesuai dengan slot database excel yang Anda import guna menghindari slip nomor pada lajur potong.</p>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

<script>
(function () {
    var state = {
        method: 'collated',
        tab: 'visual',
        sheet: 1
    };

    var el = {
        start: document.getElementById('nmStart'),
        qty: document.getElementById('nmQty'),
        firstNumber: document.getElementById('nmFirstNumber'),
        lastNumber: document.getElementById('nmLastNumber'),
        layoutPreset: document.getElementById('nmLayoutPreset'),
        cols: document.getElementById('nmCols'),
        rows: document.getElementById('nmRows'),
        digits: document.getElementById('nmDigits'),
        prefix: document.getElementById('nmPrefix'),
        suffix: document.getElementById('nmSuffix'),
        methodGroup: document.getElementById('nmMethodGroup'),
        methodHint: document.getElementById('nmMethodHint'),
        totalSheets: document.getElementById('nmTotalSheets'),
        totalSlots: document.getElementById('nmTotalSlots'),
        slotCapacity: document.getElementById('nmSlotCapacity'),
        overage: document.getElementById('nmOverage'),
        outputTitle: document.getElementById('nmOutputTitle'),
        tabGroup: document.getElementById('nmTabGroup'),
        visualPanel: document.getElementById('nmVisualPanel'),
        tablePanel: document.getElementById('nmTablePanel'),
        prevSheet: document.getElementById('nmPrevSheet'),
        nextSheet: document.getElementById('nmNextSheet'),
        firstSheet: document.getElementById('nmFirstSheet'),
        lastSheetBtn: document.getElementById('nmLastSheetBtn'),
        sheetRange: document.getElementById('nmSheetRange'),
        sheetLabel: document.getElementById('nmSheetLabel'),
        stageLabel: document.getElementById('nmStageLabel'),
        slotGrid: document.getElementById('nmSlotGrid'),
        tableHead: document.getElementById('nmTableHead'),
        tableBody: document.getElementById('nmTableBody'),
        tableFoot: document.getElementById('nmTableFoot'),
        copyTsv: document.getElementById('nmCopyTsv'),
        downloadCsv: document.getElementById('nmDownloadCsv'),
        bookFill: document.getElementById('nmBookFill')
    };

    var methodText = {
        collated: {
            title: 'Metode Susun Tumpuk (Collated)',
            hint: 'Urutan nomor meluncur ke bawah menembus tumpukan kertas. Setelah dipotong, tumpukan bukti langsung tersusun runut tanpa perlu sortir manual.',
            slotLabel: 'Tumpuk vertikal'
        },
        straight: {
            title: 'Metode Sejajar (Straight)',
            hint: 'Urutan nomor berjalan dari kiri ke kanan pada satu lembar kertas, berlanjut ke lembar berikutnya.',
            slotLabel: 'Sejajar'
        }
    };

    function intValue(input, fallback, min, max) {
        var value = parseInt(input.value, 10);
        if (!isFinite(value)) {
            value = fallback;
        }
        value = Math.max(min, value);
        if (typeof max === 'number') {
            value = Math.min(max, value);
        }
        return value;
    }

    function settings() {
        var cols = intValue(el.cols, 2, 1, 12);
        var rows = intValue(el.rows, 2, 1, 12);
        var slots = Math.max(1, cols * rows);
        var qty = intValue(el.qty, 1000, 1, 999999);
        var totalSheets = Math.max(1, Math.ceil(qty / slots));

        return {
            start: intValue(el.start, 1, 1, 999999),
            qty: qty,
            cols: cols,
            rows: rows,
            slots: slots,
            digits: intValue(el.digits, 4, 0, 12),
            prefix: el.prefix.value || '',
            suffix: el.suffix.value || '',
            totalSheets: totalSheets,
            totalSlots: totalSheets * slots
        };
    }

    function formatNumber(number, current) {
        if (number === null) {
            return 'BLANKO';
        }
        var raw = current.digits > 0 ? String(number).padStart(current.digits, '0') : String(number);
        return current.prefix + raw + current.suffix;
    }

    function rawNumberFor(sheet, slotIndex, current) {
        var offset = state.method === 'collated'
            ? (slotIndex * current.totalSheets) + (sheet - 1)
            : ((sheet - 1) * current.slots) + slotIndex;
        var number = current.start + offset;
        return number <= current.start + current.qty - 1 ? number : null;
    }

    function rowForSheet(sheet, current) {
        var row = [];
        for (var slot = 0; slot < current.slots; slot++) {
            row.push(formatNumber(rawNumberFor(sheet, slot, current), current));
        }
        return row;
    }

    function allRows(current) {
        var rows = [];
        for (var sheet = 1; sheet <= current.totalSheets; sheet++) {
            rows.push({
                sheet: sheet,
                values: rowForSheet(sheet, current)
            });
        }
        return rows;
    }

    function syncInputs(current) {
        el.start.value = current.start;
        el.qty.value = current.qty;
        el.cols.value = current.cols;
        el.rows.value = current.rows;
        el.digits.value = current.digits;
        if (state.sheet > current.totalSheets) {
            state.sheet = current.totalSheets;
        }
        if (state.sheet < 1) {
            state.sheet = 1;
        }
    }

    function renderStats(current) {
        var first = formatNumber(current.start, current);
        var last = formatNumber(current.start + current.qty - 1, current);
        el.firstNumber.textContent = first;
        el.lastNumber.textContent = last;
        el.totalSheets.textContent = current.totalSheets;
        el.totalSlots.textContent = current.totalSlots;
        el.slotCapacity.textContent = 'Kapasitas slot (' + current.slots + ' up)';
        el.overage.textContent = current.totalSlots - current.qty;
        el.outputTitle.textContent = methodText[state.method].title;
        el.methodHint.textContent = methodText[state.method].hint;
    }

    function updateSlotTypography(current, values) {
        var maxLength = values.reduce(function (longest, value) {
            return Math.max(longest, String(value).length);
        }, 1);
        var isDense = current.slots > 24;
        var isVeryDense = current.slots > 48;
        var gap = isVeryDense ? 6 : (isDense ? 8 : 12);
        var fallbackWidth = 476;
        var fallbackHeight = 700;
        var gridWidth = el.slotGrid.clientWidth || fallbackWidth;
        var gridHeight = el.slotGrid.clientHeight || fallbackHeight;
        var slotWidth = Math.max(1, (gridWidth - ((current.cols - 1) * gap)) / current.cols);
        var slotHeight = Math.max(1, (gridHeight - ((current.rows - 1) * gap)) / current.rows);
        var horizontalFit = (slotWidth - (isVeryDense ? 10 : 18)) / (maxLength * 0.62);
        var verticalFit = slotHeight * (isVeryDense ? 0.28 : (isDense ? 0.32 : 0.36));
        var fontSize = Math.max(4.8, Math.min(17, horizontalFit, verticalFit));

        el.slotGrid.classList.toggle('is-dense', isDense);
        el.slotGrid.classList.toggle('is-very-dense', isVeryDense);
        el.slotGrid.style.setProperty('--nm-slot-gap', gap + 'px');
        el.slotGrid.style.setProperty('--nm-slot-font-size', fontSize.toFixed(2) + 'px');
    }

    function renderSheet(current) {
        el.sheetRange.max = current.totalSheets;
        el.sheetRange.value = state.sheet;
        el.sheetLabel.textContent = 'Lembar ' + state.sheet + ' / ' + current.totalSheets;
        el.stageLabel.textContent = 'Lembar ke-' + state.sheet;
        el.prevSheet.disabled = state.sheet <= 1;
        el.nextSheet.disabled = state.sheet >= current.totalSheets;

        el.slotGrid.style.gridTemplateColumns = 'repeat(' + current.cols + ', minmax(0, 1fr))';
        el.slotGrid.style.gridTemplateRows = 'repeat(' + current.rows + ', minmax(0, 1fr))';
        el.slotGrid.innerHTML = '';

        var values = rowForSheet(state.sheet, current);
        updateSlotTypography(current, values);

        values.forEach(function (value, index) {
            var raw = rawNumberFor(state.sheet, index, current);
            var slot = document.createElement('div');
            slot.className = 'nm-slot' + (raw === null ? ' is-blank' : '');
            slot.innerHTML =
                '<div class="nm-slot-top"><span>Slot ' + (index + 1) + '</span><strong>' + (raw === null ? 'blanko' : '#' + raw) + '</strong></div>' +
                '<strong class="nm-slot-number">' + value + '</strong>' +
                '<small>' + methodText[state.method].slotLabel + '</small>';
            el.slotGrid.appendChild(slot);
        });
    }

    function renderTable(current) {
        var head = '<tr><th>Lembar</th>';
        for (var slot = 0; slot < current.slots; slot++) {
            head += '<th>Slot ' + (slot + 1) + ' (Up ' + (slot + 1) + ')</th>';
        }
        head += '</tr>';
        el.tableHead.innerHTML = head;

        var maxRows = Math.min(50, current.totalSheets);
        var rows = [];
        for (var sheet = 1; sheet <= maxRows; sheet++) {
            var values = rowForSheet(sheet, current);
            rows.push('<tr><td># ' + sheet + '</td>' + values.map(function (value) {
                return '<td' + (value === 'BLANKO' ? ' class="is-blank"' : '') + '>' + value + '</td>';
            }).join('') + '</tr>');
        }
        el.tableBody.innerHTML = rows.join('');
        el.tableFoot.textContent = current.totalSheets > 50
            ? '... Menampilkan 50 lembar pertama dari ' + current.totalSheets + ' lembar total ...'
            : 'Menampilkan semua ' + current.totalSheets + ' lembar kerja.';
    }

    function update() {
        var current = settings();
        syncInputs(current);
        renderStats(current);
        renderSheet(current);
        renderTable(current);
    }

    function updatePresetFromGrid() {
        var value = el.cols.value + 'x' + el.rows.value;
        var matched = false;
        Array.prototype.forEach.call(el.layoutPreset.options, function (option) {
            if (option.value === value) {
                matched = true;
            }
        });
        el.layoutPreset.value = matched ? value : 'custom';
    }

    function exportMatrix() {
        var current = settings();
        var header = ['Lembar'];
        for (var slot = 0; slot < current.slots; slot++) {
            header.push('Slot ' + (slot + 1));
        }

        var matrix = [header];
        allRows(current).forEach(function (row) {
            matrix.push([row.sheet].concat(row.values));
        });

        return matrix;
    }

    function serializeRows(separator) {
        return exportMatrix().map(function (row) {
            return row.map(function (value) {
                value = String(value);
                if (separator === ',' && /[",\r\n]/.test(value)) {
                    return '"' + value.replace(/"/g, '""') + '"';
                }
                if (separator === '\t') {
                    return value.replace(/\t/g, ' ').replace(/\r?\n/g, ' ');
                }
                return value;
            }).join(separator);
        }).join('\r\n');
    }

    function setButtonStatus(button, message, defaultText) {
        var label = button.querySelector('span');
        label.textContent = message;
        button.classList.add('is-success');
        window.setTimeout(function () {
            label.textContent = defaultText;
            button.classList.remove('is-success');
        }, 1400);
    }

    function fallbackCopy(text) {
        var textarea = document.createElement('textarea');
        textarea.value = text;
        textarea.setAttribute('readonly', 'readonly');
        textarea.style.position = 'fixed';
        textarea.style.left = '-9999px';
        textarea.style.top = '0';
        document.body.appendChild(textarea);
        textarea.select();
        var ok = document.execCommand('copy');
        document.body.removeChild(textarea);
        return ok;
    }

    function copyText(text) {
        if (navigator.clipboard && window.isSecureContext) {
            return navigator.clipboard.writeText(text).catch(function () {
                if (!fallbackCopy(text)) {
                    throw new Error('Gagal menyalin data.');
                }
            });
        }

        return new Promise(function (resolve, reject) {
            if (fallbackCopy(text)) {
                resolve();
            } else {
                reject(new Error('Gagal menyalin data.'));
            }
        });
    }

    function showExportError(error) {
        var message = error && error.message ? error.message : 'Ekspor gagal diproses.';
        if (typeof sweet === 'function') {
            sweet('Peringatan!!!', message, 'warning', 'warning');
        } else {
            alert(message);
        }
    }

    [el.start, el.qty, el.cols, el.rows, el.digits, el.prefix, el.suffix].forEach(function (input) {
        input.addEventListener('input', function () {
            if (input === el.cols || input === el.rows) {
                updatePresetFromGrid();
            }
            update();
        });
    });

    el.layoutPreset.addEventListener('change', function () {
        if (el.layoutPreset.value !== 'custom') {
            var parts = el.layoutPreset.value.split('x');
            el.cols.value = parts[0];
            el.rows.value = parts[1];
        }
        update();
    });

    document.addEventListener('click', function (event) {
        var stepper = event.target.closest('[data-step-target]');
        if (!stepper) {
            return;
        }
        var target = document.getElementById(stepper.getAttribute('data-step-target'));
        var next = intValue(target, 1, 1, 12) + parseInt(stepper.getAttribute('data-delta'), 10);
        target.value = Math.max(1, Math.min(12, next));
        updatePresetFromGrid();
        update();
    });

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

    el.tabGroup.addEventListener('click', function (event) {
        var button = event.target.closest('[data-tab]');
        if (!button) {
            return;
        }
        state.tab = button.getAttribute('data-tab');
        Array.prototype.forEach.call(el.tabGroup.querySelectorAll('button'), function (item) {
            item.classList.remove('is-active');
        });
        button.classList.add('is-active');
        el.visualPanel.classList.toggle('d-none', state.tab !== 'visual');
        el.tablePanel.classList.toggle('d-none', state.tab !== 'table');
    });

    el.prevSheet.addEventListener('click', function () {
        state.sheet--;
        update();
    });
    el.nextSheet.addEventListener('click', function () {
        state.sheet++;
        update();
    });
    el.firstSheet.addEventListener('click', function () {
        state.sheet = 1;
        update();
    });
    el.lastSheetBtn.addEventListener('click', function () {
        state.sheet = settings().totalSheets;
        update();
    });
    el.sheetRange.addEventListener('input', function () {
        state.sheet = parseInt(el.sheetRange.value, 10) || 1;
        update();
    });

    el.bookFill.addEventListener('click', function () {
        state.sheet = settings().totalSheets;
        update();
    });

    el.copyTsv.addEventListener('click', function () {
        copyText(serializeRows('\t')).then(function () {
            setButtonStatus(el.copyTsv, 'Tersalin', 'Salin Excel TSV');
        }).catch(showExportError);
    });

    el.downloadCsv.addEventListener('click', function () {
        var blob = new Blob(['\ufeff' + serializeRows(',')], { type: 'text/csv;charset=utf-8;' });
        var url = URL.createObjectURL(blob);
        var link = document.createElement('a');
        link.href = url;
        link.download = 'nomorator.csv';
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        URL.revokeObjectURL(url);
        setButtonStatus(el.downloadCsv, 'Terunduh', 'Unduh File CSV');
    });

    state.sheet = settings().totalSheets;
    update();
})();
</script>
