const canvas = document.getElementById("gameCanvas");
const ctx = canvas.getContext("2d");

let isAnimating = false;

const GRID_SIZE = 6;
const TILE = 64;
const GEM_TYPES = 5;
const SPEED = 8;

let dragStart = null;
let dragActive = false;
const DRAG_THRESHOLD = 20;

const MATERIALS = [
    { name: "Sand", value: 5 },
    { name: "Rock", value: 10 },
    { name: "Iron", value: 20 },
    { name: "Gold", value: 40 },
    { name: "Diamond", value: 100 }
];

const dpr = window.devicePixelRatio || 1;

const cssSize = GRID_SIZE * TILE;

canvas.style.width = cssSize + "px";
canvas.style.height = cssSize + "px";

canvas.width = cssSize * dpr;
canvas.height = cssSize * dpr;

ctx.scale(dpr, dpr);

let board = [];
let selected = null;
let score = 0;
let moves = 20;
const targetScore = 5000;

const scoreEl = document.getElementById("score");
const movesEl = document.getElementById("moves");
const targetEl = document.getElementById("target");
targetEl.textContent = targetScore;

let level = 1;

const levelEl = document.getElementById("level");
const progressBar = document.getElementById("progressBar");

levelEl.textContent = level;

function updateProgress() {
    const percent = Math.min(score / targetScore, 1) * 100;
    progressBar.style.width = percent + "%";
}

const particles = [];

class Particle {
    constructor(x, y, color) {
        this.x = x;
        this.y = y;
        this.vx = (Math.random() - 0.5) * 6;
        this.vy = (Math.random() - 0.5) * 6;
        this.life = 1;
        this.color = color;
    }

    update() {
        this.x += this.vx;
        this.y += this.vy;
        this.vy += 0.2;
        this.life -= 0.03;
    }

    draw() {
        if (this.life <= 0) return;
        ctx.globalAlpha = this.life;
        ctx.fillStyle = this.color;
        ctx.beginPath();
        ctx.arc(this.x, this.y, 3, 0, Math.PI * 2);
        ctx.fill();
        ctx.globalAlpha = 1;
    }
}


class Gem {
    constructor(type, row, col) {
        this.type = type;
        this.row = row;
        this.col = col;

        this.x = col * TILE;
        this.y = row * TILE;
        this.targetX = this.x;
        this.targetY = this.y;

        this.scale = 1;
        this.alpha = 1;
        this.popping = false;
    }

    update() {
        this.x += (this.targetX - this.x) / SPEED;
        this.y += (this.targetY - this.y) / SPEED;

        if (this.popping) {
            this.scale += 0.1;
            this.alpha -= 0.15;
        }
    }

    draw() {
        if (this.alpha <= 0) return;

        ctx.save();
        ctx.globalAlpha = this.alpha;
        ctx.translate(this.x + TILE / 2, this.y + TILE / 2);
        ctx.scale(this.scale, this.scale);

        ctx.beginPath();
        ctx.moveTo(-18, -12);
        ctx.lineTo(12, -18);
        ctx.lineTo(20, 0);
        ctx.lineTo(10, 18);
        ctx.lineTo(-14, 14);
        ctx.lineTo(-20, 0);
        ctx.closePath();

        ctx.fillStyle = gemColor(this.type);
        ctx.fill();

        ctx.strokeStyle = "rgba(255,255,255,0.25)";
        ctx.stroke();

        ctx.restore();
    }
}

function resizeCanvasToContainer() {
  const frame = document.getElementById("game-frame");
  const rect = frame.getBoundingClientRect();

  // Available width inside frame (minus padding)
  const size = rect.width - 20;

  const dpr = window.devicePixelRatio || 1;

  canvas.width = size * dpr;
  canvas.height = size * dpr;

  canvas.style.width = size + "px";
  canvas.style.height = size + "px";

  ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
}
function pointerDown(e) {
    if (isAnimating || moves <= 0) return;

    const pos = getPointerPos(e);
    dragStart = pos;
    selected = getCellFromPos(pos);
    dragActive = true;
}

function getPointerPos(e) {
  const rect = canvas.getBoundingClientRect();
  const point = e.touches ? e.touches[0] : e;

  return {
    x: point.clientX - rect.left,
    y: point.clientY - rect.top
  };
}

function getCellFromPos(pos) {
  return {
    row: Math.floor(pos.y / TILE),
    col: Math.floor(pos.x / TILE)
  };
}
function pointerMove(e) {
    if (!dragActive || !selected || isAnimating) return;

    const pos = getPointerPos(e);
    const dx = pos.x - dragStart.x;
    const dy = pos.y - dragStart.y;

    if (Math.abs(dx) < DRAG_THRESHOLD && Math.abs(dy) < DRAG_THRESHOLD) return;

    let target = null;

    if (Math.abs(dx) > Math.abs(dy)) {
        target = {
            row: selected.row,
            col: selected.col + (dx > 0 ? 1 : -1)
        };
    } else {
        target = {
            row: selected.row + (dy > 0 ? 1 : -1),
            col: selected.col
        };
    }

    if (
        target.row < 0 || target.row >= GRID_SIZE ||
        target.col < 0 || target.col >= GRID_SIZE
    ) {
        dragActive = false;
        selected = null;
        return;
    }

    dragActive = false;

    animatedSwap(selected, target, () => {
        if (!removeMatches()) {
            animatedSwap(target, selected, () => { });
        } else {
            moves--;
            movesEl.textContent = moves;
        }
    });

    selected = null;
}

function pointerUp() {
    dragActive = false;
    selected = null;
}


function gemColor(type) {
    return [
        "#fff5cbff", // Sand
        "#6f6157ff", // Rock
        "#6f747dff", // Iron
        "#876c00ff", // Gold
        "#acd1ffff"  // Diamond
    ][type];
}


function randomGem(row, col) {
    return new Gem(Math.floor(Math.random() * GEM_TYPES), row, col);
}

function initBoard() {
    board = [];
    for (let r = 0; r < GRID_SIZE; r++) {
        board[r] = [];
        for (let c = 0; c < GRID_SIZE; c++) {
            board[r][c] = randomGem(r, c);
        }
    }
    removeMatches();
}

function draw() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    for (let row of board) {
        for (let gem of row) {
            if (gem) gem.draw();
        }
    }

    if (selected) {
        ctx.strokeStyle = "white";
        ctx.lineWidth = 3;
        ctx.strokeRect(
            selected.col * TILE + 2,
            selected.row * TILE + 2,
            TILE - 4,
            TILE - 4
        );
    }
}

function update() {
    // Update gems (movement + pop animation)
    for (let row of board) {
        for (let gem of row) {
            if (gem) gem.update();
        }
    }

    // Update particles
    for (let i = particles.length - 1; i >= 0; i--) {
        particles[i].update();
        if (particles[i].life <= 0) {
            particles.splice(i, 1);
        }
    }
}

function animatedSwap(a, b, onComplete) {
    const g1 = board[a.row][a.col];
    const g2 = board[b.row][b.col];

    isAnimating = true;

    g1.targetX = b.col * TILE;
    g1.targetY = b.row * TILE;
    g2.targetX = a.col * TILE;
    g2.targetY = a.row * TILE;

    const check = () => {
        if (!gemsAreMoving()) {
            board[a.row][a.col] = g2;
            board[b.row][b.col] = g1;

            g1.row = b.row;
            g1.col = b.col;
            g2.row = a.row;
            g2.col = a.col;

            isAnimating = false;
            onComplete();
        } else {
            requestAnimationFrame(check);
        }
    };

    check();
}



function areAdjacent(a, b) {
    return Math.abs(a.row - b.row) + Math.abs(a.col - b.col) === 1;
}

function findMatches() {
    const matches = new Set();

    for (let r = 0; r < GRID_SIZE; r++) {
        let count = 1;
        for (let c = 1; c <= GRID_SIZE; c++) {
            if (
                c < GRID_SIZE &&
                board[r][c] &&
                board[r][c].type === board[r][c - 1].type
            ) count++;
            else {
                if (count >= 3)
                    for (let i = 0; i < count; i++)
                        matches.add(`${r},${c - 1 - i}`);
                count = 1;
            }
        }
    }

    for (let c = 0; c < GRID_SIZE; c++) {
        let count = 1;
        for (let r = 1; r <= GRID_SIZE; r++) {
            if (
                r < GRID_SIZE &&
                board[r][c] &&
                board[r][c].type === board[r - 1][c].type
            ) count++;
            else {
                if (count >= 3)
                    for (let i = 0; i < count; i++)
                        matches.add(`${r - 1 - i},${c}`);
                count = 1;
            }
        }
    }

    return matches;
}

function removeMatches() {
    const matches = findMatches();
    if (matches.size === 0) return false;

    isAnimating = true;

    // start pop animation
    matches.forEach(key => {
        const [r, c] = key.split(",").map(Number);
        const gem = board[r][c];
        if (gem) {
            gem.popping = true;
            score += MATERIALS[gem.type].value;


            for (let i = 0; i < 8; i++) {
                particles.push(
                    new Particle(
                        gem.x + TILE / 2,
                        gem.y + TILE / 2,
                        gemColor(gem.type)
                    )
                );
            }
        }

    });

    scoreEl.textContent = score;

    // wait for pop animation to finish
    const waitForPop = () => {
        let stillPopping = false;

        matches.forEach(key => {
            const [r, c] = key.split(",").map(Number);
            const gem = board[r][c];
            if (gem && gem.alpha > 0) stillPopping = true;
        });

        if (!stillPopping) {
            matches.forEach(key => {
                const [r, c] = key.split(",").map(Number);
                board[r][c] = null;
            });

            isAnimating = false;
            collapse();
        } else {
            requestAnimationFrame(waitForPop);
        }
    };

    waitForPop();
    return true;
}


function collapse() {
    for (let c = 0; c < GRID_SIZE; c++) {
        for (let r = GRID_SIZE - 1; r >= 0; r--) {
            if (!board[r][c]) {
                for (let k = r - 1; k >= 0; k--) {
                    if (board[k][c]) {
                        board[r][c] = board[k][c];
                        board[k][c] = null;

                        board[r][c].row = r;
                        board[r][c].targetY = r * TILE;
                        break;
                    }
                }
            }
        }
    }
    refill();
}


function refill() {
    for (let r = 0; r < GRID_SIZE; r++) {
        for (let c = 0; c < GRID_SIZE; c++) {
            if (!board[r][c]) {
                const gem = randomGem(r, c);
                gem.y = -TILE;
                gem.targetY = r * TILE;
                board[r][c] = gem;
            }
        }
    }

    const wait = () => {
        if (!gemsAreMoving()) {
            removeMatches();
        } else {
            requestAnimationFrame(wait);
        }
    };

    wait();
}

function gemsAreMoving() {
    for (let row of board) {
        for (let gem of row) {
            if (!gem) continue;
            if (
                Math.abs(gem.x - gem.targetX) > 0.5 ||
                Math.abs(gem.y - gem.targetY) > 0.5
            ) {
                return true;
            }
        }
    }
    return false;
}
function getCell(e) {
    const rect = canvas.getBoundingClientRect();
    const x = (e.touches ? e.touches[0].clientX : e.clientX) - rect.left;
    const y = (e.touches ? e.touches[0].clientY : e.clientY) - rect.top;
    return { row: Math.floor(y / TILE), col: Math.floor(x / TILE) };
}

function input(e) {
    if (moves <= 0 || isAnimating) return;

    const cell = getCell(e);

    if (!selected) {
        selected = cell;
        return;
    }

    if (areAdjacent(selected, cell)) {
        animatedSwap(selected, cell, () => {
            if (!removeMatches()) {
                animatedSwap(cell, selected, () => { });
            } else {
                moves--;
                movesEl.textContent = moves;
            }
        });
    }

    selected = null;
}

canvas.addEventListener("mousedown", pointerDown);
canvas.addEventListener("mousemove", pointerMove);
canvas.addEventListener("mouseup", pointerUp);

canvas.addEventListener("touchstart", pointerDown, { passive: true });
canvas.addEventListener("touchmove", pointerMove, { passive: true });
canvas.addEventListener("touchend", pointerUp);



function loop() {
    update();
    draw();
    requestAnimationFrame(loop);
}

initBoard();
loop();
