//Pandemic
//Version: 20200507
//Author: DarkRanger <darkranger.no-ip.org>
//License: MIT License
var keysDown = {};
var zombieCount = 1;
var sizeMultiplier = 0.02;
var defaultWidth = 740;
var defaultHeight = 270;
var mouseX = 0;
var mouseY = 0;
var mouseClicked = false;
var mouseMovement = false;
var gamepadEnabled = false;
var showTarget = false;
var showGamepad = false;
var darkMode = false;
var torchRadius = 6;
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
document.body.appendChild(canvas);

class Area {
    constructor (x, y, width, height) {
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
    }

    has(x, y) {
        if (x > this.x && x < (this.x + this.width) && y > this.y && y <(this.y + this.height)) {
            return true;
        }
        else {
            return false;
        }
    }

    draw() {
        ctx.beginPath();
        ctx.rect(this.x, this.y, this.width, this.height);
        ctx.lineWidth = 1;
        ctx.strokeStyle = "black";
        ctx.stroke();
        ctx.closePath();
    }
}

class Individual {
    constructor (x, y, color) {
        this.x = x;
        this.y = y;
        this.targetX = x;
        this.targetY = y;
        this.radius = canvas.width * sizeMultiplier;
        this.color = color;
    }

    draw() {
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI, false);
        ctx.fillStyle = this.color;
        ctx.fill();
        ctx.closePath();

        if (showTarget) {
            ctx.beginPath();
            ctx.moveTo(this.x, this.y);
            ctx.lineTo(this.targetX, this.targetY);
            ctx.lineWidth = 1;
            ctx.strokeStyle = "grey";
            ctx.stroke();
            ctx.fill();
            ctx.closePath();
        }
    }

    chase(x, y, radius) {
        this.targetX = this.x;
        this.targetY = this.y;
        let speed = canvas.width * 0.004;
        let distance = getDistance(x, y, this.x, this.y);
        let interval = canvas.width / 3;

        if (this.color == "blue") {
            return;
        }
        if (safetyArea.has(this.x, this.y)) {//Electrocute it.
            this.color = "blue";
            return;
        }
        if (distance < radius * 2) {
            if (zombieCount > 1) {
                zombieCount = zombieCount - 1;
            }
            return newGame();
        }
        
        if (safetyArea.has(this.x, this.y) || safetyArea.has(x, y) || distance >= interval * 2) {
            x = getRandom(canvas.width / 8, canvas.width);
            y = getRandom(0, canvas.height);
        }
        else if (distance >= interval) {
            x = getRandom(x - radius * 10, x + radius * 10);
            y = getRandom(y - radius * 10, y + radius * 10);
            speed = canvas.width * 0.008;
        }
        else if (x < canvas.width / 8 && this.y > canvas.height / 3 && this.y < canvas.height / 3 * 2) {//Don't let zombies too easily to get electrocuted.
            x = getRandom(canvas.width / 8, canvas.width);
            y = getRandom(y - radius * 4, y + radius * 4);
            speed = canvas.width * 0.01;
        }
        else {
            if (x > this.x) {
                x = getRandom(x - radius * 3, x + radius * 3);
                y = getRandom(y - radius * 3, y + radius * 3);
            }
            else {
                x = getRandom(x - radius * 4, x + radius * 4);
                y = getRandom(y - radius * 4, y + radius * 4); 
            }

            speed = canvas.width * 0.01;
        }

        if (this.x > x) {
            this.x = this.x - speed;
        }
        else if (this.x < x) {
            this.x = this.x + speed;
        }
        if (this.y > y) {
            this.y = this.y - speed;
        }
        else if (this.y < y) {
            this.y = this.y + speed;
        }

        this.targetX = x;
        this.targetY = y;
    }
}

function getRandom(min, max) {
    return Math.random() * (max - min) + min;
}

function getDistance(ax, ay, bx, by) {
    return Math.hypot(ax - bx, ay - by);
}

function preventBehavior(event) {
    event.preventDefault(); 
}

function getMousePos(x, y) {
    let rect = canvas.getBoundingClientRect();
    mouseX = (x - rect.left) / (rect.right - rect.left) * canvas.width;
    mouseY = (y - rect.top) / (rect.bottom - rect.top) * canvas.height;
}

function mouseDown(event) {
    getMousePos(event.clientX, event.clientY);
    mouseClicked = true;
    mouseMovement = true;
}

function mouseMove(event) {
    getMousePos(event.clientX, event.clientY);

    if (mouseClicked) {
        mouseMovement = true;
    }
}

function mouseUp(event) {
    mouseMovement = false;
    mouseClicked = false;
}

function touchStart(event) {
    getMousePos(event.touches[0].clientX, event.touches[0].clientY);
    mouseMovement = true;
}

function touchMove(event) {
    getMousePos(event.touches[0].clientX, event.touches[0].clientY);
    mouseMovement = true;
}

function touchEnd(event) {
    mouseMovement = false;
    mouseClicked = false;
}

function gamepadOn(event) {
    gamepadEnabled = true;
    showGamepad = true;
}

function gamepadOff(event) {
    gamepadEnabled = false;
    showGamepad = true;
}

function settingsToggle(keyCode) {
    if (keyCode == 66) {//Key 'b'.
        showTarget = !showTarget;
    }
    if (keyCode == 68) {//Key 'd'.
        darkMode = !darkMode;
    }
}

function canvasResize() {
    if (window.innerWidth < defaultWidth) {
        canvas.width = window.innerWidth;
        canvas.height = (window.innerWidth / defaultWidth) * defaultHeight;
    }
    else {
        canvas.width = defaultWidth;
        canvas.height = defaultHeight;
    }

    newGame();
}

function newGame() {
    zombies = [];
    safetyArea = new Area(0, canvas.height / 3, canvas.width / 8, canvas.height / 3);
    evacuationArea = new Area(canvas.width / 8 * 7, canvas.height / 3, canvas.width / 8, canvas.height / 3);
    player = new Individual(canvas.width / 12 / 2, canvas.height / 2, "red");

    for (let i = 0; i < zombieCount; i++) {
        let zombie = new Individual(getRandom(canvas.width / 8, canvas.width), getRandom(0, canvas.height), "black");
        zombies.push(zombie);
    }

    //console.log(`zombies: ${zombies.length}`);
}

function gameUpdate() {
    let playerSpeed = canvas.width * 0.01;
    let moving = false;

    if (32 in keysDown) {//Key 'spacebar'.
        return newGame();
    }
    if (gamepadEnabled) {
        let gamepads = navigator.getGamepads();

        for (let i = 0; i < gamepads.length; i++) {
            if (!gamepads[i]) {
                continue;
            }
            if (gamepads[i].axes[0] < 0 && player.x > player.radius) {
                player.x = player.x - playerSpeed;
                moving = true;
            }
            if (gamepads[i].axes[0] > 0 && player.x < canvas.width - player.radius) {
                player.x = player.x + playerSpeed;
                moving = true;
            }
            if (gamepads[i].axes[1] < 0 && player.y > player.radius) {
                player.y = player.y - playerSpeed;
                moving = true;
            }
            if (gamepads[i].axes[1] > 0 && player.y < canvas.height - player.radius) {
                player.y = player.y + playerSpeed;
                moving = true;
            }
        }
    }
    if (mouseMovement) {
        if (mouseX < player.x && player.x > player.radius) {
            player.x = player.x - playerSpeed;
            moving = true;
        } 
        if (mouseX > player.x && player.x < canvas.width - player.radius) {
            player.x = player.x + playerSpeed;
            moving = true;
        }
        if (mouseY < player.y && player.y > player.radius) {
            player.y = player.y - playerSpeed;
            moving = true;
        }
        if (mouseY > player.y && player.y < canvas.height - player.radius) {
            player.y = player.y + playerSpeed;
            moving = true;
        }
    }
    if (37 in keysDown && player.x > player.radius) {//Go left.
        player.x = player.x - playerSpeed;
        moving = true;
    }
    if (39 in keysDown && player.x < canvas.width - player.radius) {//Go right.
        player.x = player.x + playerSpeed;
        moving = true;
    }
    if (38 in keysDown && player.y > player.radius) {//Go up.
        player.y = player.y - playerSpeed;
        moving = true;
    }
    if (40 in keysDown && player.y < canvas.height - player.radius) {//Go down.
        player.y = player.y + playerSpeed;
        moving = true;
    }
    if (moving) {
        showGamepad = false;
        player.targetX = player.x;
        player.targetY = player.y;

        if (evacuationArea.has(player.x, player.y)) {
            zombieCount = zombieCount + 1;
            newGame();
        }

        zombies.forEach(function(zombie) {
            zombie.chase(player.x, player.y, player.radius);
        });
    }

    settingsToggle();
}

function statusBoard() {
    let fontColor = "black";

    if (darkMode) {
        fontColor = "white";
    }

    if (showGamepad) {
        let gamepadStatus = "Gamepad disabled.";

        if (gamepadEnabled) {
            gamepadStatus = "Gamepad enabled.";
        }

        ctx.beginPath();
        ctx.font = `bold ${canvas.width / 60}px Arial`;
        ctx.fillStyle = fontColor;
        ctx.fillText(gamepadStatus, canvas.width / 24, canvas.height / 8);
        ctx.fill();
        ctx.closePath();
    }

    ctx.beginPath();
    ctx.font = `bold ${canvas.width / 30}px Arial`;
    ctx.fillStyle = fontColor;
    ctx.fillText(`Z:${zombieCount}`, canvas.width / 10 * 9, canvas.height / 8);
    ctx.fill();
    ctx.closePath();
}

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

    if (darkMode) {
        ctx.fillStyle = "black";
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        ctx.beginPath();
        ctx.arc(player.x, player.y, player.radius * torchRadius, 0, 2 * Math.PI, false);
        ctx.fillStyle = "white";
        ctx.fill();
        ctx.closePath();
    }

    safetyArea.draw();
    evacuationArea.draw();

    zombies.forEach(function(zombie) {
        if (zombie.color == "blue") {
            zombie.draw();
        }
    });

    player.draw();

    zombies.forEach(function(zombie) {
        if (zombie.color == "black") {
            zombie.draw();
        }
    });

    statusBoard();
};

function gameMain() {
    var fps = 24;

    gameUpdate();
    gameRender();
    
    setTimeout(function() {
        requestAnimationFrame(gameMain);
    }, 1000 / fps);
};

window.addEventListener("resize", canvasResize, false);
window.addEventListener("gamepadconnected", gamepadOn);
window.addEventListener("gamepaddisconnected", gamepadOff);
canvas.addEventListener("mousedown", mouseDown, false);
canvas.addEventListener("mousemove", mouseMove, false);
canvas.addEventListener("mouseup", mouseUp, false);
canvas.addEventListener("touchstart", touchStart, false);
canvas.addEventListener("touchmove", touchMove, false);
canvas.addEventListener("touchend", touchEnd, false);

document.addEventListener("keydown", function (event) {
    keysDown[event.keyCode] = true;
}, false);

document.addEventListener("keyup", function (event) {
    settingsToggle(event.keyCode);
    delete keysDown[event.keyCode];
}, false);

document.addEventListener("touchstart", preventBehavior, {passive: false});
document.addEventListener("touchmove", preventBehavior, {passive: false});
document.addEventListener("touchend", preventBehavior, {passive: false});

canvasResize();
gameMain();