/**
 * qix_view.js
 * 描画およびアセット管理クラス
 * (文法エラー修正・完全版)
 */

class AssetManager {
    constructor() {
        this.images = {};
    }

    loadImage(key, src) {
        return new Promise((resolve, reject) => {
            const img = new Image();
            img.onload = () => {
                this.images[key] = img;
                resolve(img);
            };
            img.onerror = () => {
                console.warn(`Fallback: ${src}`);
                const dummy = document.createElement('canvas');
                dummy.width = 600; dummy.height = 600;
                const dCtx = dummy.getContext('2d');
                dCtx.fillStyle = key.includes('reveal') ? '#4B0082' : '#111';
                dCtx.fillRect(0, 0, 600, 600);
                this.images[key] = dummy; 
                resolve(dummy);
            };
            img.src = src;
        });
    }

    get(key) {
        return this.images[key];
    }
}

class Renderer {
    constructor(canvas) {
        this.ctx = canvas.getContext('2d');
        this.width = canvas.width;
        this.height = canvas.height;
        this.gridSize = CONFIG.GRID_SIZE;

        this.bgCanvas = document.createElement('canvas');
        this.bgCanvas.width = this.width;
        this.bgCanvas.height = this.height;
        this.bgCtx = this.bgCanvas.getContext('2d');

        this.maskCanvas = document.createElement('canvas');
        this.maskCanvas.width = this.width;
        this.maskCanvas.height = this.height;
        this.maskCtx = this.maskCanvas.getContext('2d');
    }

    updateBackground(gridSystem, assets, overlayAlpha = 0.6) {
        const width = this.width;
        const height = this.height;
        const cols = gridSystem.cols;
        const rows = gridSystem.rows;
        const grid = gridSystem.data;

        // Hidden画像
        this.bgCtx.globalCompositeOperation = 'source-over';
        this.bgCtx.fillStyle = '#050010'; 
        this.bgCtx.fillRect(0, 0, width, height);

        if (assets.hidden) {
            this.bgCtx.drawImage(assets.hidden, 0, 0, width, height);
            if (overlayAlpha > 0) {
                this.bgCtx.fillStyle = `rgba(10, 0, 20, ${overlayAlpha})`; 
                this.bgCtx.fillRect(0, 0, width, height);
            }
        }

        // Reveal画像
        if (assets.reveal) {
            this.maskCtx.clearRect(0, 0, width, height);
            const maskData = this.maskCtx.createImageData(width, height);
            const data = maskData.data;

            for (let i = 0; i < grid.length; i++) {
                if (grid[i] === CONFIG.STATE.WALL) {
                    const idx = i * 4;
                    data[idx] = 0; data[idx+1] = 0; data[idx+2] = 0; data[idx+3] = 255;
                }
            }
            this.maskCtx.putImageData(maskData, 0, 0);

            this.maskCtx.globalCompositeOperation = 'source-in';
            this.maskCtx.drawImage(assets.reveal, 0, 0, width, height);
            this.maskCtx.globalCompositeOperation = 'source-over';

            this.bgCtx.drawImage(this.maskCanvas, 0, 0);
        }

        // 境界線
        this.bgCtx.fillStyle = "#BC13FE"; 
        for (let y = 0; y < rows; y++) {
            for (let x = 0; x < cols; x++) {
                const i = y * cols + x;
                if (grid[i] === CONFIG.STATE.WALL) {
                    let isEdge = false;
                    if (x > 0 && grid[i - 1] === CONFIG.STATE.EMPTY) isEdge = true;
                    else if (x < cols - 1 && grid[i + 1] === CONFIG.STATE.EMPTY) isEdge = true;
                    else if (y > 0 && grid[i - cols] === CONFIG.STATE.EMPTY) isEdge = true;
                    else if (y < rows - 1 && grid[i + cols] === CONFIG.STATE.EMPTY) isEdge = true;

                    if (isEdge) {
                        this.bgCtx.fillRect(x - 1, y - 1, 4, 4);
                    }
                }
            }
        }
    }

    drawGlitch(ctx) {
        const w = this.width;
        const h = this.height;
        const slices = 10;
        for(let i=0; i<slices; i++) {
            const sh = Math.random() * 50;
            const sy = Math.random() * h;
            const ox = (Math.random() - 0.5) * 40; 
            ctx.drawImage(this.ctx.canvas, 0, sy, w, sh, ox, sy, w, sh);
        }
        ctx.save();
        ctx.globalCompositeOperation = 'source-over'; // overlayから修正
        ctx.fillStyle = Math.random() > 0.5 ? 'rgba(0, 255, 255, 0.1)' : 'rgba(255, 0, 255, 0.1)';
        ctx.fillRect(0, 0, w, h);
        
        ctx.beginPath();
        for(let i=0; i<5; i++){
            const y = Math.random() * h;
            ctx.moveTo(0, y);
            ctx.lineTo(w, y);
        }
        ctx.strokeStyle = "rgba(255, 255, 255, 0.8)";
        ctx.lineWidth = 2;
        ctx.stroke();
        ctx.restore();
    }

    drawParticles(ctx, particles) {
        ctx.save();
        particles.forEach(p => {
            ctx.globalAlpha = p.life;
            ctx.fillStyle = p.color;
            ctx.beginPath();
            ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
            ctx.fill();
        });
        ctx.restore();
    }

    drawWipe(assets, wipeY) {
        const ctx = this.ctx;
        const w = this.width;
        const h = this.height;
        if (!assets.reveal) return;

        ctx.save();
        ctx.beginPath();
        ctx.rect(0, 0, w, wipeY);
        ctx.clip(); 
        ctx.drawImage(assets.reveal, 0, 0, w, h);
        ctx.restore();

        ctx.shadowBlur = 20;
        ctx.shadowColor = "#FFFFFF";
        ctx.fillStyle = "#FFFFFF";
        ctx.fillRect(0, wipeY - 2, w, 4);
    }

    drawItems(ctx, items) {
        const time = Date.now();
        ctx.save();
        items.forEach(item => {
            const scale = 1.0 + 0.3 * Math.sin(time / 200);
            let color = '#FFFFFF';
            if (item.type === 'TIME_STOP') color = '#AAAAFF'; 
            else if (item.type === 'MP_CHARGE') color = '#AAFFAA';

            ctx.shadowBlur = 15;
            ctx.shadowColor = color;
            ctx.fillStyle = color;
            ctx.beginPath();
            ctx.arc(item.x, item.y, 6 * scale, 0, Math.PI * 2);
            ctx.fill();

            // ★ここが原因でした。引用符を修正
            ctx.fillStyle = '#FFFFFF'; 
            ctx.shadowBlur = 0;
            ctx.beginPath();
            ctx.arc(item.x, item.y, 3, 0, Math.PI * 2);
            ctx.fill();
        });
        ctx.restore();
    }
    
    // 画像鑑賞中はそのまま表示（暗転なし）
    drawIntroView(assets) { 
        const ctx = this.ctx;
        const w = this.width;
        const h = this.height;
        if (!assets.hidden) return;

        ctx.clearRect(0, 0, w, h);
        ctx.drawImage(assets.hidden, 0, 0, w, h);
    }

    // 背景画像キャッシュ（波紋レンズ用）
    _ensureBgCache(assets, w, h) {
        if (this._bgCache && this._bgCache.src === assets.hidden) return this._bgCache.data;
        const off = Object.assign(document.createElement('canvas'), { width: w, height: h });
        off.getContext('2d').drawImage(assets.hidden, 0, 0, w, h);
        const data = off.getContext('2d').getImageData(0, 0, w, h);
        this._bgCache = { src: assets.hidden, data };
        return data;
    }

    // 多重波紋演出 — 波紋リング帯がレンズ歪みを起こす水面効果
    drawIntroErosionRipples(assets, ripples, overlayAlpha) {
        const ctx = this.ctx;
        const W = this.width, H = this.height;

        ctx.clearRect(0, 0, W, H);

        if (!assets.hidden) {
            ctx.fillStyle = '#050005';
            ctx.fillRect(0, 0, W, H);
        } else {
            // ベース画像描画
            ctx.drawImage(assets.hidden, 0, 0, W, H);

            // レンズ歪み処理
            const srcID = this._ensureBgCache(assets, W, H);
            const src   = srcID.data;
            const frame = ctx.getImageData(0, 0, W, H);
            const dst   = frame.data;

            const HALF_W  = 22;  // リング帯の半幅 (px)
            const MAX_DISP = 20; // 最大変位量 (px)

            ripples.forEach(ripple => {
                const cx = ripple.x, cy = ripple.y, R = ripple.radius;
                if (R <= 0) return;
                const inner  = R - HALF_W;
                const outer  = R + HALF_W;
                const innerSq = inner > 0 ? inner * inner : 0;
                const outerSq = outer * outer;

                const x0 = Math.max(0,   Math.floor(cx - outer));
                const y0 = Math.max(0,   Math.floor(cy - outer));
                const x1 = Math.min(W-1, Math.ceil (cx + outer));
                const y1 = Math.min(H-1, Math.ceil (cy + outer));

                for (let py = y0; py <= y1; py++) {
                    const ddy = py - cy, ddy2 = ddy * ddy;
                    for (let px = x0; px <= x1; px++) {
                        const ddx = px - cx;
                        const d2  = ddx * ddx + ddy2;
                        if (d2 < innerSq || d2 > outerSq) continue;

                        const d = Math.sqrt(d2);
                        if (d < 0.5) continue;

                        // sin プロファイル: 帯の両端で0、中央で最大
                        const t    = (d - (inner > 0 ? inner : 0)) / (HALF_W * 2);
                        const disp = MAX_DISP * Math.sin(Math.PI * Math.max(0, Math.min(1, t)));
                        const nx = ddx / d, ny = ddy / d;

                        // ソース座標 (中央方向に引き寄せ → 凸レンズ的圧縮)
                        const sx = Math.max(0, Math.min(W-1, Math.round(px - disp * nx))) | 0;
                        const sy = Math.max(0, Math.min(H-1, Math.round(py - disp * ny))) | 0;

                        const si = (sy * W + sx) * 4;
                        const di = (py * W + px) * 4;
                        dst[di]   = src[si];
                        dst[di+1] = src[si+1];
                        dst[di+2] = src[si+2];
                        // α は変えない (255 のまま)
                    }
                }
            });

            ctx.putImageData(frame, 0, 0);
        }

        // 波紋内側: 暗いオーバーレイ (重なり不加算)
        ctx.save();
        ctx.beginPath();
        ripples.forEach(ripple => {
            ctx.moveTo(ripple.x + ripple.radius, ripple.y);
            ctx.arc(ripple.x, ripple.y, ripple.radius, 0, Math.PI * 2);
        });
        ctx.fillStyle = `rgba(10, 0, 20, ${overlayAlpha})`;
        ctx.fill();
        ctx.restore();

        // 波紋縁: 発光リング
        ripples.forEach(ripple => {
            ctx.save();
            ctx.beginPath();
            ctx.arc(ripple.x, ripple.y, ripple.radius, 0, Math.PI * 2);
            ctx.lineWidth = 15;
            const a = Math.max(0, 0.8 - ripple.radius / (CONFIG.CANVAS_SIZE * 0.8));
            ctx.strokeStyle = `rgba(188, 19, 254, ${a})`;
            ctx.shadowBlur  = 20;
            ctx.shadowColor = `rgba(255, 255, 255, ${a})`;
            ctx.stroke();
            ctx.restore();
        });
    }

    drawClearSeep(assets, seepAlpha) {
        const ctx = this.ctx;
        const w = this.width;
        const h = this.height;
        if (!assets.reveal) return;

        ctx.drawImage(assets.reveal, 0, 0, w, h);

        if (seepAlpha > 0) {
            ctx.save();
            const grad = ctx.createRadialGradient(w/2, h/2, 50, w/2, h/2, w);
            grad.addColorStop(0, `rgba(180, 0, 255, ${seepAlpha * 0.2})`);
            grad.addColorStop(1, `rgba(50, 0, 100, ${seepAlpha * 0.9})`);
            
            ctx.fillStyle = grad;
            ctx.fillRect(0, 0, w, h);
            ctx.restore();
        }
    }

    render(params) {
        const {
            gridSystem, assets, player, enemies, trailPoints, direction, 
            isGlitchActive, particles, items, state, introParams, shakeOffset,
            projectiles, claimEffects
        } = params;

        const ctx = this.ctx;
        
        if (state === 'INTRO_VIEW' || state === 'VOICE_START') {
            this.drawIntroView(assets);
            return;
        }
        if (state === 'INTRO_EROSION_RIPPLE') {
            this.drawIntroErosionRipples(assets, introParams.ripples, introParams.alpha);
            return;
        }
        
        ctx.clearRect(0, 0, this.width, this.height);
        
        ctx.save();
        if (shakeOffset && (shakeOffset.x !== 0 || shakeOffset.y !== 0)) {
            ctx.translate(shakeOffset.x, shakeOffset.y);
        }
        
        ctx.drawImage(this.bgCanvas, 0, 0);

        if (claimEffects && claimEffects.length > 0) {
            claimEffects.forEach(e => e.draw(ctx));
        }

        if (items && items.length > 0) {
            this.drawItems(ctx, items);
        }

        if (trailPoints.length > 0) {
            ctx.save();
            ctx.lineCap = "round";
            ctx.lineJoin = "round";
            ctx.beginPath();
            const drawPoints = [trailPoints[0]];
            for (let i = 1; i < trailPoints.length - 1; i++) {
                const prev = drawPoints[drawPoints.length - 1];
                const curr = trailPoints[i];
                if ((curr.x - prev.x)**2 + (curr.y - prev.y)**2 > 30) drawPoints.push(curr);
            }
            drawPoints.push(trailPoints[trailPoints.length - 1]);

            if (drawPoints.length < 3) {
                ctx.moveTo(drawPoints[0].x, drawPoints[0].y);
                for(let i=1; i<drawPoints.length; i++) ctx.lineTo(drawPoints[i].x, drawPoints[i].y);
            } else {
                ctx.moveTo(drawPoints[0].x, drawPoints[0].y);
                for (let i = 1; i < drawPoints.length - 2; i++) {
                    const xc = (drawPoints[i].x + drawPoints[i+1].x) / 2;
                    const yc = (drawPoints[i].y + drawPoints[i+1].y) / 2;
                    ctx.quadraticCurveTo(drawPoints[i].x, drawPoints[i].y, xc, yc);
                }
                const last = drawPoints[drawPoints.length-1];
                const prevLast = drawPoints[drawPoints.length-2];
                ctx.quadraticCurveTo(prevLast.x, prevLast.y, last.x, last.y);
            }

            ctx.strokeStyle = "#FF0055";
            ctx.lineWidth = 15; 
            ctx.shadowBlur = 10;
            ctx.shadowColor = "#FF0055";
            ctx.stroke();
            
            ctx.strokeStyle = "rgba(255, 215, 0, 0.5)";
            ctx.lineWidth = 4;
            ctx.globalCompositeOperation = 'lighter';
            ctx.stroke();
            ctx.strokeStyle = "#FFFFFF";
            ctx.lineWidth = 2;
            ctx.stroke();
            ctx.restore();
        }

        if (player.visible) {
            const px = player.x;
            const py = player.y;
            ctx.save();
            ctx.translate(px, py);
            
            if (state === 'INTRO_APPEAR' || state === 'INTRO_START') {
                const s = introParams.scale;
                ctx.scale(s, s);
            }

            ctx.fillStyle = "rgba(0, 0, 0, 0.85)";
            ctx.beginPath();
            ctx.arc(0, 0, 10, 0, Math.PI * 2);
            ctx.fill();
            ctx.strokeStyle = "#FFD700"; 
            ctx.lineWidth = 1;
            ctx.stroke();
            
            let angle = 0;
            if (direction) {
                if (direction.x > 0 && direction.y > 0) angle = Math.PI / 4;
                else if (direction.x > 0 && direction.y < 0) angle = -Math.PI / 4;
                else if (direction.x < 0 && direction.y > 0) angle = 3 * Math.PI / 4;
                else if (direction.x < 0 && direction.y < 0) angle = -3 * Math.PI / 4;
                else if (direction.x > 0) angle = 0;
                else if (direction.x < 0) angle = Math.PI;
                else if (direction.y > 0) angle = Math.PI / 2;
                else if (direction.y < 0) angle = -Math.PI / 2;
            }
            ctx.rotate(angle);
            ctx.fillStyle = "#FFD700"; 
            ctx.shadowBlur = 15;
            ctx.shadowColor = "#FFD700";
            ctx.beginPath();
            ctx.moveTo(8, 0);   
            ctx.lineTo(-5, 5);  
            ctx.lineTo(-2, 0);  
            ctx.lineTo(-5, -5); 
            ctx.closePath();
            ctx.fill();
            ctx.fillStyle = "#FFFFFF";
            ctx.beginPath();
            ctx.arc(0, 0, 2, 0, Math.PI*2);
            ctx.fill();
            ctx.restore();
        }

        {
            const time = Date.now();
            for (let e of enemies) {
                if (!e.active) continue;
                
                if (state === 'INTRO_APPEAR' || state === 'INTRO_START') {
                    ctx.save();
                    ctx.translate(e.x, e.y);
                    ctx.scale(introParams.scale, introParams.scale);
                    ctx.translate(-e.x, -e.y);
                    e.draw(ctx, time);
                    ctx.restore();
                } else {
                    e.draw(ctx, time);
                }
            }
        }

        if (projectiles && projectiles.length > 0) {
            for (let p of projectiles) {
                if (p.active) p.draw(ctx);
            }
        }

        if (particles && particles.length > 0) {
            this.drawParticles(ctx, particles);
        }

        if (isGlitchActive) {
            this.drawGlitch(ctx);
        }
        
        ctx.restore();

        if (state === 'CLEAR_SEEP' || state === 'CLEAR_WAIT') {
            const seepAlpha = introParams ? introParams.seepAlpha : 0;
            this.drawClearSeep(assets.reveal ? { reveal: assets.reveal } : {}, seepAlpha);
        }
    }
}