<template>
    <view class="walker-canvas-container">
        <canvas
            canvas-id="walker"
            id="walker"
            class="walker-canvas"
            :style="{ width: canvasWidth + 'px', height: canvasHeight + 'px' }"
            @touchstart="onTouchStart"
            @touchmove="onTouchMove"
            @touchend="onTouchEnd"
            @touchcancel="onTouchCancel"
        ></canvas>
    </view>
</template>

<script>
export default {
    props: {
        isPlaying: {
            type: Boolean,
            default: false
        },
        bpm: {
            type: Number,
            default: 110
        }
    },
    data() {
        return {
            canvasWidth: 320,
            canvasHeight: 400,
            ctx: null,
            timer: null,
            renderRunning: false,

            // Interaction State (Spring Physics)
            coreScale: 1,
            coreTargetScale: 1,
            coreVelocity: 0,
            pressed: false,

            // Animation Physics (Time)
            time: 0,
            lastFrameTime: 0,
            
            // Metronome State
            lastBeatTime: 0,
            
            // Energy Waves
            waveAmplitudeMultiplier: 1,
            
            // Shockwaves
            shockwaves: [],
            
            // Rotation for outer ring
            ringAngle: 0
        };
    },
    mounted() {
        this.initCanvas();
    },
    beforeUnmount() {
        if (this.timer) {
            clearTimeout(this.timer);
            this.timer = null;
        }
        this.renderRunning = false;
    },
    watch: {
        isPlaying(newVal) {
            if (newVal) {
                this.waveAmplitudeMultiplier = 1;
                this.ensureRenderLoop();
            }
        }
    },
    methods: {
        initCanvas() {
            // Delay to ensure Flexbox layout is fully complete before measuring
            setTimeout(() => {
                const query = uni.createSelectorQuery().in(this);
                query.select('.walker-canvas-container').boundingClientRect(data => {
                    if (data && data.width > 0) {
                        this.canvasWidth = data.width;
                        this.canvasHeight = data.height > 100 ? data.height : data.width * 1.2;
                    } else {
                        const sys = uni.getSystemInfoSync();
                        this.canvasWidth = sys.windowWidth - 30; // approximate padding
                        this.canvasHeight = this.canvasWidth * 1.2;
                    }
                    
                    if (!this.ctx) {
                        this.ctx = uni.createCanvasContext('walker', this);
                        this.lastFrameTime = Date.now();
                        this.ensureRenderLoop();
                    }
                }).exec();
            }, 100);
        },
        
        // Expose a method to be called from parent when a beat hits
        triggerBeat() {
            const coreRadius = Math.min(this.canvasWidth, this.canvasHeight) * 0.32;
            // Add a shockwave
            this.shockwaves.push({
                radius: coreRadius,
                maxRadius: coreRadius * 2.2,
                opacity: 1
            });

            // Spike the wave amplitude
            this.waveAmplitudeMultiplier = 2.5;

            // Slight pulse to the core
            this.coreVelocity -= 0.05;

            this.ensureRenderLoop();
        },
        
        onTouchStart(e) {
            const touch = e.touches[0];
            if (!touch) return;

            // Center
            const cx = this.canvasWidth / 2;
            const cy = this.canvasHeight / 2 - 30;
            const coreRadius = Math.min(this.canvasWidth, this.canvasHeight) * 0.32;

            const dx = touch.x - cx;
            const dy = touch.y - cy;
            const dist = Math.sqrt(dx*dx + dy*dy);

            // Hit box slightly larger than the visual circle
            if (dist <= coreRadius + 40) {
                this.pressed = true;
                this.coreTargetScale = 0.9;
                this.ensureRenderLoop();
            }
        },
        onTouchMove(e) {
            if (!this.pressed) return;
            const touch = e.touches[0];
            if (!touch) return;

            const cx = this.canvasWidth / 2;
            const cy = this.canvasHeight / 2 - 30;
            const coreRadius = Math.min(this.canvasWidth, this.canvasHeight) * 0.32;

            const dx = touch.x - cx;
            const dy = touch.y - cy;
            const dist = Math.sqrt(dx*dx + dy*dy);

            // Slid out of the hit box - cancel the press
            if (dist > coreRadius + 40) {
                this.pressed = false;
                this.coreTargetScale = 1;
            }
        },
        onTouchEnd() {
            // Unconditionally reset the visual scale
            this.coreTargetScale = 1;
            // Only emit toggle when the press has not been cancelled by drag-out
            if (this.pressed) {
                this.$emit('toggle-play');
            }
            this.pressed = false;
        },
        onTouchCancel() {
            // System interruption (incoming call, notification pull-down, etc.):
            // reset visuals only — do NOT count this as a user toggle intent.
            this.coreTargetScale = 1;
            this.pressed = false;
        },
        
        ensureRenderLoop() {
            if (this.renderRunning) return;
            this.renderRunning = true;
            this.renderLoop();
        },

        renderLoop() {
            // Use a fixed time delta to prevent rubber-banding/jitter on unstable JS timers
            const fixedDt = 0.016;

            if (this.isPlaying) {
                this.time += fixedDt;
            } else {
                this.time += fixedDt * 0.2; // slow drift when paused
            }

            // Spring Physics for Core Scale
            const tension = 120;
            const friction = 12;
            const force = (this.coreTargetScale - this.coreScale) * tension;
            this.coreVelocity += force * fixedDt;
            this.coreVelocity *= Math.exp(-friction * fixedDt);
            this.coreScale += this.coreVelocity * fixedDt;

            // Decay wave amplitude back to 1
            if (this.waveAmplitudeMultiplier > 1) {
                this.waveAmplitudeMultiplier -= fixedDt * 3;
                if (this.waveAmplitudeMultiplier < 1) this.waveAmplitudeMultiplier = 1;
            } else if (!this.isPlaying && this.waveAmplitudeMultiplier > 0.1) {
                this.waveAmplitudeMultiplier -= fixedDt * 2;
                if (this.waveAmplitudeMultiplier < 0.1) this.waveAmplitudeMultiplier = 0.1;
            }

            this.draw();

            this.ctx.draw(false);

            // Decide whether to continue the render loop. Short-circuit on isPlaying first for perf.
            const shouldContinue =
                this.isPlaying ||
                this.shockwaves.length > 0 ||
                Math.abs(this.coreScale - this.coreTargetScale) >= 0.001 ||
                Math.abs(this.coreVelocity) >= 0.001 ||
                this.waveAmplitudeMultiplier > 0.11;

            if (shouldContinue) {
                // Polyfill requestAnimationFrame for uni-app
                this.timer = setTimeout(() => {
                    this.renderLoop();
                }, 1000 / 60);
            } else {
                this.timer = null;
                this.renderRunning = false;
            }
        },
        
        draw() {
            const ctx = this.ctx;
            const W = this.canvasWidth;
            const H = this.canvasHeight;
            
            // Clear entire canvas to ensure it is completely transparent and blends with the page background
            ctx.clearRect(0, 0, W, H);
            
            // Dynamic majestic sizing to fill the huge screen area
            const coreRadius = Math.min(W, H) * 0.32; // Make the central circle MASSIVE
            const ringRadius = coreRadius + 18;
            const cx = W / 2;
            const cy = H / 2 - 30; // Slightly above center
            
            // 3. Draw Shockwaves (Ripples)
            for (let i = this.shockwaves.length - 1; i >= 0; i--) {
                const sw = this.shockwaves[i];
                sw.radius += (sw.maxRadius - sw.radius) * 0.08;
                sw.opacity -= 0.03;
                
                if (sw.opacity <= 0) {
                    this.shockwaves.splice(i, 1);
                    continue;
                }
                
                ctx.beginPath();
                ctx.arc(cx, cy, sw.radius, 0, Math.PI * 2);
                ctx.strokeStyle = `rgba(16, 185, 129, ${sw.opacity * 0.5})`;
                ctx.lineWidth = 3;
                ctx.stroke();
            }
            
            // 4. Draw Energy Waves (Bottom area)
            this.drawWaves(ctx, W, H);
            
            // 5. Draw Interactive BPM Core
            ctx.save();
            ctx.translate(cx, cy);
            ctx.scale(this.coreScale, this.coreScale);
            
            // The Core Button (Emerald Gradient)
            ctx.setShadow(0, 12, 24, 'rgba(16, 185, 129, 0.3)');
            
            const coreGrad = ctx.createLinearGradient(-coreRadius, -coreRadius, coreRadius, coreRadius);
            coreGrad.addColorStop(0, '#34d399'); // Light Mint
            coreGrad.addColorStop(1, '#047857'); // Deep Emerald
            
            ctx.beginPath();
            ctx.arc(0, 0, coreRadius, 0, Math.PI * 2);
            ctx.fillStyle = coreGrad;
            ctx.fill();
            
            // Clear shadow for internal elements
            ctx.setShadow(0, 0, 0, 'transparent');
            
            // Inner Subtle Highlight (Glass edge)
            ctx.beginPath();
            ctx.arc(0, 0, coreRadius - 2, 0, Math.PI * 2);
            ctx.strokeStyle = 'rgba(255, 255, 255, 0.35)';
            ctx.lineWidth = 2;
            ctx.stroke();
            
            // Text - BPM Number (MASSIVE font)
            const fontSize = Math.floor(coreRadius * 0.7); // Dynamic font size based on radius
            ctx.fillStyle = '#ffffff';
            ctx.font = `bold ${fontSize}px "DIN Condensed", "Inter", sans-serif`;
            ctx.textAlign = 'center';
            ctx.textBaseline = 'middle';
            // Removed shadowBlur for text to save low-end GPU performance
            ctx.fillText(this.bpm.toString(), 0, -12);
            
            // Text - 'BPM' Label
            ctx.fillStyle = 'rgba(255, 255, 255, 0.85)';
            ctx.font = '500 16px "Inter", sans-serif';
            ctx.fillText('BPM', 0, coreRadius * 0.3);
            
            // Play/Pause Icon
            ctx.fillStyle = '#ffffff';
            const iconY = coreRadius * 0.55;
            if (this.isPlaying) {
                // Pause Icon (Two vertical bars)
                ctx.fillRect(-8, iconY - 6, 5, 14);
                ctx.fillRect(3, iconY - 6, 5, 14);
            } else {
                // Play Icon (Triangle)
                ctx.beginPath();
                ctx.moveTo(-5, iconY - 8);
                ctx.lineTo(9, iconY);
                ctx.lineTo(-5, iconY + 8);
                ctx.closePath();
                ctx.fill();
            }
            
            ctx.restore();
        },
        
        drawWaves(ctx, W, H) {
            const baseY = H * 0.82; 
            const dynamicAmp = H * 0.08; 
            // Reduced to 2 layers to save GPU/Bridge rendering time and ensure 60fps
            const waves = [
                { color: 'rgba(52, 211, 153, 0.3)', speed: 1.5, freq: 0.012, amp: dynamicAmp * 0.7, offset: 0 }, 
                { color: 'rgba(16, 185, 129, 0.6)', speed: 2.5, freq: 0.015, amp: dynamicAmp, offset: Math.PI } 
            ];
            
            const activeAmp = this.waveAmplitudeMultiplier;
            const t = this.time;
            
            waves.forEach(wave => {
                ctx.beginPath();
                ctx.moveTo(0, H);
                ctx.lineTo(0, baseY);
                
                // Increased step size to drastically reduce JS-to-Native bridge commands
                const step = 25;
                for (let x = 0; x <= W; x += step) {
                    // Removed edge envelope taper so waves crash cleanly into the edge of the screen
                    const y = baseY + Math.sin(x * wave.freq + t * wave.speed + wave.offset) * wave.amp * activeAmp;
                    ctx.lineTo(x, y);
                }
                
                // Add one final line to exactly W to ensure flush edge
                const finalY = baseY + Math.sin(W * wave.freq + t * wave.speed + wave.offset) * wave.amp * activeAmp;
                ctx.lineTo(W, finalY);
                
                ctx.lineTo(W, H);
                ctx.closePath();
                ctx.fillStyle = wave.color;
                ctx.fill();
            });
        }
    }
};
</script>

<style scoped>
.walker-canvas-container {
    width: 100%;
    height: 100%;
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
}

.walker-canvas {
    display: block;
    width: 100%;
    height: 100%;
    background: transparent; /* Pure transparent to blend into page */
}
</style>
