/**
 * TINKERINO — Illustrator Template Filler
 * =======================================
 * Bundle dari Tinkerino Web UI berisi:
 *   bundle_folder/
 *     data.json
 *     illustrator.jsx   (file ini)
 *     images/
 *       main.jpg, layout.gif, exterior.jpg, building1.jpg, shared1.jpg,
 *       floorPlan1.jpg, gallery1.jpg, branch.jpg, accessMap.jpg, ...
 *
 * Cara pakai:
 *   1. Extract ZIP bundle dari Tinkerino di folder manapun.
 *   2. Buka file .ai template Anda di Illustrator.
 *   3. File > Scripts > Other Script...  → pilih illustrator.jsx ini
 *   4. Ketika diminta, pilih file  data.json  di folder bundle.
 *   5. Script akan:
 *        - Replace isi text frame yang namanya cocok (lihat daftar di bawah)
 *        - Swap PlacedItem (gambar ter-link) yang namanya "image.<slot>"
 *          dengan file di folder images/ yang cocok.
 *
 * ========== PENAMAAN DI ILLUSTRATOR ==========
 * Di panel LAYERS Illustrator, double-click nama setiap text frame /
 * placed item, lalu isi sesuai tabel berikut:
 *
 * -------- TEKS (nama text frame) --------
 *   HEADER
 *     propertyName         → ブリリアタワー川崎
 *     propertyNameWithFloor → ブリリアタワー川崎/24階
 *     floor                → 24階
 *     code                 → C14262R77
 *     price                → 1億3,980万円
 *
 *   LAYOUT
 *     madori               → 3LD・K
 *     orientation          → 南
 *     exclusiveArea        → 79.97m²
 *     balconyArea          → 13.87m²
 *     buildingArea         → (kodate) 99.36m²
 *     landArea             → (kodate) 132.37m²
 *     floorsInfo           → 24階／地上34階 地下1階
 *
 *   LOKASI
 *     address              → 神奈川県川崎市幸区大宮町28-2
 *     access               → (semua baris, joined " / ")
 *     accessLine1          → 京浜東北・根岸線 「川崎」駅 徒歩6分
 *     accessLine2          → 京急本線 「京急川崎」駅 徒歩11分
 *     mapUrl               → https://google.com/maps?q=...
 *
 *   OUTLINE TABLE (物件概要)
 *     rightsType           → 所有権
 *     totalUnits           → 395戸
 *     structure            → 鉄筋コンクリート造
 *     builtYear            → 2008年7月
 *     currentStatus        → 居住中
 *     deliveryDate         → 相談
 *     seller               → 東京建物株式会社 他
 *     constructor          → 三井住友建設株式会社
 *     manager              → 株式会社東京建物アメニティサポート
 *     managementType       → 全部委託
 *     managerDuty          → 日勤
 *     managementFee        → 23,700円
 *     repairFee            → 20,800円
 *     otherFee             → 990円
 *     totalFee             → 45,490円
 *     otherFeeDetail       → インターネット使用料/990円
 *     parking              → -
 *     transactionType      → 媒介(仲介)
 *     remarks              → -
 *
 *   GEDUNG (dari library page)
 *     buildingCatchphrase  → ペデストリアンデッキで駅直結...
 *     buildingDescription  → 総戸数395戸の大規模マンション...
 *     sellingPoint1..5     → 5 bullet points terpisah
 *     buildingTotalUnits   → 395戸
 *     buildingStructure    → ＲＣ一部Ｓ
 *     buildingScale        → 地上34階 地下1階
 *     buildingYear         → 2008年10月
 *     buildingOwner        → 東京建物｜ジェイアール東日本都市開発 他
 *     buildingConstruction → 三井住友建設
 *
 *   CABANG
 *     branchName           → 川崎センター
 *     branchCompany        → 東急リバブル株式会社 川崎センター
 *     branchOpening        → 10:00〜18:00
 *     branchHoliday        → 毎週火曜日・水曜日
 *     branchTel            → 044-211-1091
 *     branchFax            → 044-244-1738
 *     branchFreeCall       → 0120-055-109
 *     branchZip            → 210-0007
 *     branchAddress        → 神奈川県川崎市川崎区駅前本町11-1 ...
 *     branchAccess         → 東海道本線「川崎」駅 北口東出口徒歩1分 / ...
 *     branchStaff          → 間中 (まなか)
 *     branchUrl            → https://www.livable.co.jp/branch/kawasaki/
 *
 *   META
 *     extractedAt          → 2026-04-20 10:56
 *     sourceUrl            → https://www.livable.co.jp/mansion/...
 *
 * -------- GAMBAR (nama PlacedItem / gambar ter-link) --------
 *   Placed item name  → file di images/...
 *     image.main        → main.jpg        (foto utama property)
 *     image.layout      → layout.gif/jpg  (denah unit)
 *     image.exterior    → exterior.jpg    (foto luar gedung)
 *     image.building1   → building1.jpg   (foto gedung 1)
 *     image.building2..6 → building2..6.jpg
 *     image.shared1..6  → shared1..6.jpg  (foto fasilitas bersama)
 *     image.floorPlan1..4 → floorPlan1..4.jpg (denah/contoh interior)
 *     image.gallery1..6 → gallery1..6.jpg (galeri)
 *     image.branch      → branch.jpg      (foto cabang)
 *     image.accessMap   → accessMap.jpg   (peta akses)
 *
 * Catatan gambar:
 *   - Gambar WAJIB di-Place dengan opsi "Link" (bukan Embed).
 *     File > Place ... → centang "Link" sebelum klik Place.
 *   - Saat script jalan, script akan ganti path link-nya ke file
 *     di folder images/. Hasil akhir tetap LINKED — kalau mau
 *     embed, pilih gambar lalu Object > Embed di Illustrator.
 *
 * Tidak cocok? Text frame / placed item tanpa nama yang match
 * TIDAK akan disentuh. Aman untuk grafis lain yang tidak ingin
 * diganti.
 */

#target illustrator

(function main() {
    if (app.documents.length === 0) {
        alert('Buka file .ai template dulu sebelum menjalankan script.');
        return;
    }
    var doc = app.activeDocument;

    // 1) Pilih data.json
    var jsonFile = File.openDialog('Pilih data.json dari bundle Tinkerino',
        (File.fs === 'Windows') ? 'JSON:*.json' : undefined);
    if (!jsonFile) return;
    if (!jsonFile.exists) { alert('File tidak ditemukan: ' + jsonFile.fsName); return; }

    jsonFile.encoding = 'UTF-8';
    if (!jsonFile.open('r')) { alert('Tidak bisa buka: ' + jsonFile.fsName); return; }
    var raw = jsonFile.read();
    jsonFile.close();

    var data;
    try { data = eval('(' + raw + ')'); }
    catch (e) { alert('JSON tidak valid:\n' + e.message); return; }

    // 2) Lokasi folder images/
    var bundleDir = jsonFile.parent;
    var imagesDir = new Folder(bundleDir.fsName + '/images');
    var hasImages = imagesDir.exists;

    // 3) Bangun mapping text
    var textMap = buildTextMap(data);

    // 4) Jalankan
    var result = { textOk: 0, textSkipped: [], imgOk: 0, imgSkipped: [] };

    var textFrames = collectTextFrames(doc);
    for (var i = 0; i < textFrames.length; i++) {
        var tf = textFrames[i];
        var key = normName(tf.name);
        if (key && textMap.hasOwnProperty(key)) {
            var val = textMap[key];
            if (val !== null && val !== undefined && String(val) !== '') {
                try { tf.contents = String(val); result.textOk++; }
                catch (e) { result.textSkipped.push(key + ' (locked?)'); }
            }
        }
    }

    if (hasImages) {
        var placedItems = collectPlacedItems(doc);
        for (var j = 0; j < placedItems.length; j++) {
            var pi = placedItems[j];
            var nm = pi.name;
            if (!nm) continue;
            var m = /^image\.([A-Za-z0-9_]+)\s*$/.exec(nm);
            if (!m) continue;
            var slot = m[1];
            var localFile = findImageFile(imagesDir, slot);
            if (!localFile) {
                result.imgSkipped.push(nm + ' (file tidak ada)');
                continue;
            }
            try {
                pi.file = localFile;
                result.imgOk++;
            } catch (e) {
                // Embedded image → file property read-only
                result.imgSkipped.push(nm + ' (embedded / terkunci)');
            }
        }
    }

    // 5) Report
    var msg  = 'TINKERINO — Report\n';
        msg += '──────────────────────\n';
        msg += 'Text frame diganti : ' + result.textOk + '\n';
        msg += 'Gambar diganti     : ' + result.imgOk + '\n';
    if (result.textSkipped.length) {
        msg += '\nText di-skip:\n  ' + uniqJoin(result.textSkipped, '\n  ');
    }
    if (result.imgSkipped.length) {
        msg += '\nGambar di-skip:\n  ' + uniqJoin(result.imgSkipped, '\n  ');
    }
    if (!hasImages) {
        msg += '\n\nFolder images/ tidak ditemukan di sebelah data.json\n';
        msg += 'Gambar tidak ada yang diganti.';
    }
    alert(msg);

    // ==================== HELPERS ====================

    function buildTextMap(d) {
        var b = d.building || {};
        var br = d.branch || {};
        var al = d.accessLines || [];
        var sp = b.sellingPoints || [];
        var coord = d.coordinates || {};
        var m = {
            // Names
            'propertyName'          : d.propertyName || '',
            'propertyNameWithFloor' : d.propertyNameWithFloor || '',
            'floor'                 : d.floor || '',
            'code'                  : d.code || '',

            // Pricing
            'price'                 : d.price || '',

            // Layout
            'madori'                : d.madori || '',
            'orientation'           : d.orientation || '',
            'exclusiveArea'         : d.exclusiveArea || '',
            'balconyArea'           : d.balconyArea || '',
            'buildingArea'          : d.buildingArea || '',
            'landArea'              : d.landArea || '',
            'floorsInfo'            : d.floorsInfo || '',

            // Location
            'address'               : d.address || '',
            'access'                : d.access || '',
            'accessLine1'           : al[0] || '',
            'accessLine2'           : al[1] || '',
            'accessLine3'           : al[2] || '',
            'mapUrl'                : d.mapUrl || '',
            'latitude'              : coord.lat != null ? String(coord.lat) : '',
            'longitude'             : coord.lng != null ? String(coord.lng) : '',

            // Outline
            'rightsType'            : d.rightsType || '',
            'totalUnits'            : d.totalUnits || '',
            'structure'             : d.structure || '',
            'builtYear'             : d.builtYear || '',
            'currentStatus'         : d.currentStatus || '',
            'deliveryDate'          : d.deliveryDate || '',
            'seller'                : d.seller || '',
            'constructor'           : d.constructor || '',
            'manager'               : d.manager || '',
            'managementType'        : d.managementType || '',
            'managerDuty'           : d.managerDuty || '',
            'managementFee'         : d.managementFee || '',
            'repairFee'             : d.repairFee || '',
            'otherFee'              : d.otherFee || '',
            'totalFee'              : d.totalFee || '',
            'otherFeeDetail'        : d.otherFeeDetail || '',
            'parking'               : d.parking || '',
            'transactionType'       : d.transactionType || '',
            'remarks'               : d.remarks || '',

            // Building
            'buildingCatchphrase'   : b.catchphrase || '',
            'buildingDescription'   : b.description || '',
            'sellingPoint1'         : sp[0] || '',
            'sellingPoint2'         : sp[1] || '',
            'sellingPoint3'         : sp[2] || '',
            'sellingPoint4'         : sp[3] || '',
            'sellingPoint5'         : sp[4] || '',
            'buildingTotalUnits'    : b.totalUnits || '',
            'buildingStructure'     : b.structureMaterial || '',
            'buildingScale'         : b.buildingScale || '',
            'buildingYear'          : b.buildingYear || '',
            'buildingOwner'         : b.owner || '',
            'buildingConstruction'  : b.construction || '',

            // Branch
            'branchName'            : br.name || '',
            'branchCompany'         : br.company || '',
            'branchOpening'         : br.opening || '',
            'branchHoliday'         : br.holiday || '',
            'branchTel'             : br.tel || '',
            'branchFax'             : br.fax || '',
            'branchFreeCall'        : br.freeCall || '',
            'branchZip'             : br.zipCode || '',
            'branchAddress'         : br.address || '',
            'branchAccess'          : br.access || '',
            'branchStaff'           : br.staff || '',
            'branchUrl'             : br.url || '',

            // Meta
            'extractedAt'           : d.extractedAt || '',
            'sourceUrl'             : d.sourceUrl || ''
        };

        // Total fee fallback: compute kalau tidak ada
        if (!m.totalFee) {
            var mf = parseInt((m.managementFee || '').replace(/[^\d]/g, ''), 10) || 0;
            var rf = parseInt((m.repairFee || '').replace(/[^\d]/g, ''), 10) || 0;
            var of = parseInt((m.otherFee || '').replace(/[^\d]/g, ''), 10) || 0;
            var sum = mf + rf + of;
            if (sum > 0) m.totalFee = formatThousand(sum) + '円';
        }

        return m;
    }

    function formatThousand(n) {
        var s = String(n), out = '', c = 0;
        for (var i = s.length - 1; i >= 0; i--) {
            out = s.charAt(i) + out;
            c++;
            if (c % 3 === 0 && i > 0) out = ',' + out;
        }
        return out;
    }

    function normName(nm) {
        if (!nm) return '';
        var s = String(nm);
        s = s.replace(/^\s*(field|data|text)\s*:\s*/i, '');
        return s.replace(/^\s+|\s+$/g, '');
    }

    function findImageFile(folder, slot) {
        var exts = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'JPG', 'PNG', 'GIF'];
        for (var i = 0; i < exts.length; i++) {
            var f = new File(folder.fsName + '/' + slot + '.' + exts[i]);
            if (f.exists) return f;
        }
        return null;
    }

    function collectTextFrames(container) {
        var out = [];
        walk(container.layers, function (it) {
            if (it.typename === 'TextFrame') out.push(it);
        });
        return out;
    }

    function collectPlacedItems(container) {
        var out = [];
        walk(container.layers, function (it) {
            if (it.typename === 'PlacedItem') out.push(it);
        });
        return out;
    }

    function walk(items, cb) {
        for (var i = 0; i < items.length; i++) {
            var it = items[i];
            cb(it);
            if (it.typename === 'Layer' || it.typename === 'GroupItem') {
                if (it.pageItems && it.pageItems.length > 0) {
                    walkItems(it.pageItems, cb);
                }
                if (it.layers && it.layers.length > 0) {
                    walk(it.layers, cb);
                }
            }
        }
    }
    function walkItems(items, cb) {
        for (var i = 0; i < items.length; i++) {
            var it = items[i];
            cb(it);
            if (it.typename === 'GroupItem' && it.pageItems && it.pageItems.length > 0) {
                walkItems(it.pageItems, cb);
            }
        }
    }

    function uniqJoin(arr, sep) {
        var seen = {}, out = [];
        for (var i = 0; i < arr.length; i++) {
            if (!seen[arr[i]]) { seen[arr[i]] = 1; out.push(arr[i]); }
        }
        return out.join(sep);
    }
})();
