<template>
    <div class="card">
        <div class="card-header">
            <h5 class="card-title mb-0">Label layout</h5>
        </div>
        <div class="card-body">
            <div class="template-grid d-flex flex-wrap align-items-start">
                <div v-for="t in labelTemplates" :key="t.id" class="template-option d-flex flex-column text-center"
                     :class="{'template-option-disabled': !isAvailable(t)}"
                     :title="isAvailable(t) ? '' : 'Not available - fill in the fields this layout needs above.'"
                     role="button" @click="isAvailable(t) && $emit('input', t.id)">
                    <canvas :ref="el => setTemplateCanvasRef(t.id, el)"
                           class="img-thumbnail template-thumb-canvas"
                           :class="{'border-primary': value === t.id}"></canvas>
                    <div class="small"
                        :class="{'fw-bold text-primary': value === t.id}">
                        {{ t.name }}
                    </div>
                    <div class="small text-muted">{{ t.description }}</div>
                </div>
            </div>
        </div>
    </div>
</template>

<style scoped>
/* This build pins Bootstrap 4 (no gap-* utilities, those are Bootstrap 5.1+), so the spacing
   here is plain CSS gap rather than a Bootstrap gap-N class. */
.template-grid {
    gap: 1rem;
}

.template-option {
    width: 20rem;
}

.template-option-disabled {
    opacity: .45;
    cursor: not-allowed;
}

.template-thumb-canvas {
    display: block;
    width: 100%;
    height: 10rem;
    /* The canvas itself is drawn at whatever size fits its content (see redraw/drawFallbackLabel)
       - object-fit scales that down to the thumbnail box the same way it would for an <img>,
       no manual zoom math needed. */
    object-fit: contain;
    background: #fff;
}
</style>

<script>
import {drawFallbackLabel, preloadQrEncoder} from "@/label.js";
import {LABEL_TEMPLATES, templateIsAvailable, templateContent} from "@/label-layouts.js";

export default {
    name: "LabelLayoutPreview",
    props: {
        // Named content fields the templates draw from (see label.js's buildLabelFields)
        // - kept in sync by the parent, not owned here. A field missing from this object (rather
        // than present-but-empty) means a template that needs it is unavailable right now.
        fields: {
            type: Object,
            required: true
        },
        // The selected template id.
        value: {
            type: String,
            required: true
        },
        // Print.vue's global qr/micro-qr/rmqr choice (see label.js's QR_CODE_TYPES) - passed
        // straight through to drawFallbackLabel so these thumbnails match whatever symbology the
        // main preview is actually using.
        codeType: {
            type: String,
            default: "qr"
        }
    },
    model: {
        prop: "value",
        event: "input"
    },
    emits: ["input"],
    computed: {
        labelTemplates() {
            return LABEL_TEMPLATES;
        }
    },
    watch: {
        fields() {
            this.redraw();
        },
        codeType() {
            this.redraw();
        }
    },
    methods: {
        setTemplateCanvasRef(id, el) {
            if (el) {
                this.templateCanvases[id] = el;
            } else {
                delete this.templateCanvases[id];
            }
        },

        isAvailable(t) {
            return templateIsAvailable(t, this.fields);
        },

        /* Live per-template thumbnails. Always uses the content-fit fallback renderer (rather
           than the tape-fed one), regardless of whether a real printer is connected - these are
           illustrative previews sized by CSS object-fit, not the accurate to-be-printed canvas
           the main preview is. */
        redraw() {
            for (const t of LABEL_TEMPLATES) {
                const canvas = this.templateCanvases[t.id];
                if (!canvas) {
                    continue;
                }
                const content = templateContent(t, this.fields);
                if (!this.isAvailable(t) || !content) {
                    canvas.width = 1;
                    canvas.height = 1;
                    continue;
                }
                try {
                    drawFallbackLabel(canvas, content, "along", this.codeType);
                } catch (e) {
                    // A thumbnail that can't render at this content length just stays blank
                    // rather than surfacing an error for every keystroke.
                }
            }
        }
    },
    created() {
        // Keyed by template id, populated by setTemplateCanvasRef() - a plain :ref="t.id" string
        // inside v-for would still get Vue's refInFor array-collecting behavior even though
        // each iteration uses a different name, turning this.$refs[t.id] into a one-element
        // array rather than the canvas itself. A function ref sidesteps that entirely.
        this.templateCanvases = {};
    },
    async mounted() {
        // See Print.vue's mounted() - every thumbnail here has a "qrcode" leaf, so there's
        // nothing worth drawing before this resolves.
        await preloadQrEncoder();
        this.redraw();
    }
}
</script>
