/**
 * CaptureFormatting — display strings for capture/EXIF fields.
 *
 * Direct port of the Swift app's `CaptureFormatting`
 * (Data/Capture/CaptureFormatting.swift), which is canonical. Swift keeps this
 * as one shared type precisely so its Scan Summary and its PDF report can never
 * drift; the same reason applies here, where CaptureDetailsPanel serves both the
 * analysis screen and the report surfaces.
 *
 * Keep the output strings byte-identical to Swift's. A scan captured on the
 * phone and the same scan viewed on the web should read the same, and the two
 * files should be diffable when either changes.
 *
 * Written in ES5 for Babel 6 compatibility.
 */
var CaptureFormatting = (function () {

    var CARDINAL_NAMES = [
        'North', 'North-east', 'East', 'South-east',
        'South', 'South-west', 'West', 'North-west',
    ];

    /** Spelled-out 8-point direction. Parity: `cardinalName(forDegrees:)`. */
    function cardinalName(degrees) {
        if (typeof degrees !== 'number' || !isFinite(degrees)) return null;
        var normalized = degrees % 360;
        var positive = normalized < 0 ? normalized + 360 : normalized;
        var index = Math.floor((positive / 45) + 0.5) % 8;
        return CARDINAL_NAMES[index];
    }

    /**
     * "iPhone 15 Pro · 24mm f/1.8".
     * Parity: `cameraLabel(model:lensModel:)` — including the fallbacks, where
     * either side alone is returned on its own and neither yields null.
     */
    function cameraLabel(model, lensModel) {
        var m = _clean(model);
        var l = _clean(lensModel);
        if (m && l) return m + ' · ' + l;
        if (m) return m;
        if (l) return l;
        return null;
    }

    /**
     * "ISO 400 · 1/60s · f/1.8".
     * Parity: `exposureLabel(iso:exposureSeconds:fNumber:)` — same ordering,
     * same " · " join, same sub-second reciprocal form, and the same rule that
     * a non-positive exposure is omitted rather than rendered as "1/Infinitys".
     */
    function exposureLabel(iso, exposureSeconds, fNumber) {
        var parts = [];

        if (_isNum(iso)) parts.push('ISO ' + Math.round(iso));

        if (_isNum(exposureSeconds) && exposureSeconds > 0) {
            if (exposureSeconds >= 1) {
                parts.push(exposureSeconds.toFixed(1) + 's');
            } else {
                parts.push('1/' + Math.round(1 / exposureSeconds) + 's');
            }
        }

        if (_isNum(fNumber)) parts.push('f/' + fNumber.toFixed(1));

        return parts.length ? parts.join(' · ') : null;
    }

    /** "24 mm (26 mm equivalent)" — focal length with its 35mm equivalent. */
    function focalLengthLabel(focalLengthMM, focalLength35mm) {
        if (!_isNum(focalLengthMM)) {
            return _isNum(focalLength35mm) ? Math.round(focalLength35mm) + ' mm equivalent' : null;
        }
        var base = _trimNumber(focalLengthMM) + ' mm';
        if (_isNum(focalLength35mm) && Math.round(focalLength35mm) !== Math.round(focalLengthMM)) {
            base += ' (' + Math.round(focalLength35mm) + ' mm equivalent)';
        }
        return base;
    }

    /** "3024 × 4032" — the multiplication sign, not a lowercase x. */
    function dimensionsLabel(pixelWidth, pixelHeight) {
        if (!_isNum(pixelWidth) || !_isNum(pixelHeight)) return null;
        return Math.round(pixelWidth) + ' × ' + Math.round(pixelHeight);
    }

    /** "12.2 MP" — derived, so it is never stored. */
    function megapixelLabel(pixelWidth, pixelHeight) {
        if (!_isNum(pixelWidth) || !_isNum(pixelHeight)) return null;
        var mp = (pixelWidth * pixelHeight) / 1000000;
        if (!isFinite(mp) || mp <= 0) return null;
        return (mp >= 10 ? mp.toFixed(0) : mp.toFixed(1)) + ' MP';
    }

    /**
     * "33.8688° S, 151.2093° E" — hemisphere letters rather than a minus sign,
     * which reads better in a report and avoids the "is that a range?" ambiguity
     * of "-33.8688, 151.2093".
     */
    function coordinatesLabel(latitude, longitude) {
        if (!_isNum(latitude) || !_isNum(longitude)) return null;
        var latHem = latitude < 0 ? 'S' : 'N';
        var lonHem = longitude < 0 ? 'W' : 'E';
        return Math.abs(latitude).toFixed(4) + '° ' + latHem + ', ' +
               Math.abs(longitude).toFixed(4) + '° ' + lonHem;
    }

    /** "Flash fired" / "No flash". Null when the tag was absent entirely —
     *  "we don't know" and "it didn't fire" are different facts. */
    function flashLabel(flashFired) {
        if (flashFired === true) return 'Flash fired';
        if (flashFired === false) return 'No flash';
        return null;
    }

    /**
     * "Jul 15, 2026, 2:22 PM" — when the photo was taken, which is often not
     * when it was analysed. Delegates to FormatUtils.dateTime so the app has one
     * date style; renders in the viewer's local timezone, while the stored value
     * stays UTC.
     */
    function photoTimestampLabel(ms) {
        if (!_isNum(ms)) return null;
        var d = new Date(ms);
        if (isNaN(d.getTime())) return null;
        if (typeof FormatUtils !== 'undefined' && FormatUtils.dateTime) {
            return FormatUtils.dateTime(ms);
        }
        return d.toLocaleString();
    }

    // --- internals ---------------------------------------------------------

    function _isNum(v) {
        return typeof v === 'number' && isFinite(v);
    }

    function _clean(s) {
        if (typeof s !== 'string') return null;
        var t = s.replace(/^\s+|\s+$/g, '');
        return t.length ? t : null;
    }

    /** 24 -> "24", 4.5 -> "4.5" — no trailing ".0" on whole numbers. */
    function _trimNumber(n) {
        var r = Math.round(n * 10) / 10;
        return (r % 1 === 0) ? String(Math.round(r)) : r.toFixed(1);
    }

    return {
        cardinalName:        cardinalName,
        cameraLabel:         cameraLabel,
        exposureLabel:       exposureLabel,
        focalLengthLabel:    focalLengthLabel,
        dimensionsLabel:     dimensionsLabel,
        megapixelLabel:      megapixelLabel,
        coordinatesLabel:    coordinatesLabel,
        flashLabel:          flashLabel,
        photoTimestampLabel: photoTimestampLabel,
    };
})();

window.CaptureFormatting = CaptureFormatting;
