<template>
    <div class="weightCapture">

        <!-- Accepted photo preview -->
        <div class="weightCapture__preview" v-if="accepted">
            <div class="weightCapture__previewInner">
                <a href="#" class="weightCapture__remove" @click.prevent="retakeFromPreview()" title="Retake photo">
                    <font-awesome-icon :icon="['far', 'circle-xmark']" />
                </a>
                <img :src="previewSrc" alt="Captured photo" />
            </div>
            <button type="button" class="weightCapture__retake" @click="retakeFromPreview()">Retake photo</button>
        </div>

        <!-- Start button -->
        <div class="weightCapture__start" v-else-if="!showCameraInterface">
            <button type="button" class="button buttonAlt weightCapture__startBtn" @click="initCamera()">
                Take photo now <i class="fas fa-camera"></i>
            </button>
        </div>

        <!-- Live camera -->
        <div class="weightCapture__camera" v-else>
            <video
                ref="video"
                v-show="!capturedImageSrc"
                class="weightCapture__video"
                style="transform: scaleX(-1)"
                playsinline
                autoplay
                muted
            ></video>

            <canvas ref="canvas" class="weightCapture__canvas" style="display: none"></canvas>

            <img v-if="capturedImageSrc" :src="capturedImageSrc" class="weightCapture__still" alt="Preview" />

            <p class="weightCapture__status" v-if="cameraLoading">Waiting for camera…</p>
            <p class="weightCapture__status" v-else-if="isCountingDown">Capturing in {{ countdownValue }} seconds…</p>

            <div class="weightCapture__controls">
                <button
                    type="button"
                    class="button buttonAlt"
                    v-if="!cameraLoading && !capturedImageSrc && !isCountingDown"
                    @click="capturePhoto()"
                >
                    Capture photo
                </button>

                <template v-if="capturedImageSrc">
                    <button type="button" class="button buttonAlt" @click="acceptPhoto()">Use this photo</button>
                    <button type="button" class="button buttonOutlined" @click="retakePhoto()">Retake photo</button>
                </template>
            </div>

            <p v-if="cameraError" class="weightCapture__error">{{ cameraError }}</p>
        </div>

    </div>
</template>

<script>
export default {
    name: 'weightCapture',
    data() {
        return {
            showCameraInterface: false,
            mediaStream: null,
            capturedImageSrc: null,
            capturedBlob: null,
            accepted: false,
            previewSrc: null,
            isCountingDown: false,
            countdownValue: 0,
            countdownInterval: null,
            cameraError: null,
            cameraLoading: false,
        }
    },
    beforeDestroy() {
        this.stopCamera()
    },
    methods: {
        async initCamera() {
            this.showCameraInterface = true
            this.cameraError = null
            this.capturedImageSrc = null
            this.capturedBlob = null
            this.cameraLoading = true

            const constraints = {
                audio: false,
                video: { width: 1280, height: 720 },
            }

            try {
                this.mediaStream = await navigator.mediaDevices.getUserMedia(constraints)

                await this.$nextTick()

                const video = this.$refs.video

                const onReady = () => {
                    video.play()
                    this.cameraLoading = false
                    this.startCountdown()
                }

                video.onloadedmetadata = onReady
                video.srcObject = this.mediaStream

                if (video.readyState >= 1) {
                    onReady()
                }
            } catch (err) {
                this.cameraError = 'Unable to access camera. Please check your browser permissions and try again.'
                this.showCameraInterface = false
                this.cameraLoading = false
            }
        },

        startCountdown() {
            if (this.countdownInterval) {
                clearInterval(this.countdownInterval)
                this.countdownInterval = null
            }

            this.isCountingDown = true
            this.countdownValue = 10

            this.countdownInterval = setInterval(() => {
                this.countdownValue--

                if (this.countdownValue <= 0) {
                    clearInterval(this.countdownInterval)
                    this.countdownInterval = null
                    this.isCountingDown = false
                    this.capturePhoto()
                }
            }, 1000)
        },

        capturePhoto() {
            const video = this.$refs.video
            const canvas = this.$refs.canvas

            // Downscale to a sane long edge and encode as JPEG: a review photo
            // does not need a multi-MB lossless PNG. The cap also guards against
            // cameras that ignore the 1280x720 request and deliver more.
            const MAX_EDGE = 1280
            const scale = Math.min(1, MAX_EDGE / Math.max(video.videoWidth, video.videoHeight))
            canvas.width = Math.round(video.videoWidth * scale)
            canvas.height = Math.round(video.videoHeight * scale)

            canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height)

            canvas.toBlob((blob) => {
                this.capturedBlob = blob
                this.capturedImageSrc = URL.createObjectURL(blob)
            }, 'image/jpeg', 0.85)
        },

        acceptPhoto() {
            if (!this.capturedBlob) {
                return
            }

            // Reuse the object URL created at capture time for the preview.
            this.previewSrc = this.capturedImageSrc
            this.accepted = true
            this.stopCamera()
            this.showCameraInterface = false

            this.$emit('captured', this.capturedBlob)
        },

        retakePhoto() {
            this.capturedImageSrc = null
            this.capturedBlob = null
            this.startCountdown()
        },

        retakeFromPreview() {
            this.accepted = false
            this.previewSrc = null
            this.capturedBlob = null
            this.$emit('cleared')
            this.initCamera()
        },

        stopCamera() {
            if (this.countdownInterval) {
                clearInterval(this.countdownInterval)
                this.countdownInterval = null
                this.isCountingDown = false
            }

            if (this.mediaStream) {
                this.mediaStream.getTracks().forEach((track) => track.stop())
                this.mediaStream = null
            }
        },
    },
}
</script>

<style lang="less">
.weightCapture {
    --gap: 1rem;

    &__previewInner {
        position: relative;
        width: 100%;
        max-width: 280px;
        border-radius: var(--form_border_radius);
        border: 1px solid var(--primary_color);
        overflow: hidden;

        img {
            display: block;
            width: 100%;
        }
    }

    &__remove {
        position: absolute;
        top: 0;
        right: 0;
        z-index: 2;
        width: 48px;
        height: 48px;
        display: flex;
        align-items: flex-start;
        justify-content: flex-end;
        padding: 8px 8px 0 0;
        color: var(--alert_color);
        text-decoration: none;
        .rems(20);
    }

    &__retake {
        .rems(14);
        font-weight: 600;
        color: var(--dark_primary, var(--primary_color));
        background: none;
        cursor: pointer;
        margin-top: ~'calc(var(--gap) / 2)';
    }

    &__video,
    &__still {
        display: block;
        width: 100%;
        border-radius: var(--form_border_radius);
        overflow: hidden;
        margin-bottom: var(--gap);
    }

    &__status {
        color: var(--text_color_lighter);
        margin-bottom: ~'calc(var(--gap) / 2)';
    }

    &__error {
        color: var(--alert_color);
        font-weight: 600;
    }

    &__controls {
        display: flex;
        flex-wrap: wrap;
        gap: ~'calc(var(--gap) / 2)';
    }

    &__startBtn i {
        margin-left: 8px;
    }
}
</style>
