import Graphics from "./graphics";
import UI from "./ui";
import World from "./world";
import { pointsEquals } from "./utils";
import { prevent } from "@common/utils";

export default class Binario {
    private running = false;
    private mouseDown: false | number = false;
    private graphics;
    private world;
    private ui;
    private prevWorldPos: Point | null = null;
    private prevFrame: number = performance.now();
    private paused = false;

    constructor(private canvas: HTMLCanvasElement) {
        canvas.focus();

        canvas.addEventListener('wheel', this.onScroll);
        canvas.addEventListener('mousedown', this.onMouseDown);
        canvas.addEventListener('mousemove', this.onMouseMove);
        canvas.addEventListener('mouseup', this.onMouseUp);
        document.addEventListener('keypress', this.onKeyPress);
        document.addEventListener('contextmenu', prevent);
        document.addEventListener('select', prevent);
        document.addEventListener('selectstart', prevent);

        this.graphics = new Graphics(canvas);
        this.world = new World();
        this.ui = new UI();

        window.addEventListener('resize', this.onResize);
        this.onResize();
        this.graphics.resetView();
    }

    private onResize = () => {
        this.canvas.width = window.innerWidth;
        this.canvas.height = window.innerHeight;

        this.graphics.resetStyle();
    }

    private onScroll = (event: WheelEvent) => {
        const direction = event.deltaY / Math.abs(event.deltaY);

        let scale = 1;
        if (direction < 0) {
            scale = 1.1;
        } else if (direction > 0) {
            scale = 1 / 1.1;
        }

        this.graphics.applyScale(scale, [event.clientX, event.clientY]);

        event.preventDefault();
    }

    private onMouseDown = (event: MouseEvent) => {
        this.mouseDown = event.button;
        event.preventDefault();
    }

    private onMouseUp = (event: MouseEvent) => {
        this.canvas.style.cursor = 'default';
        this.mouseDown = false;
        this.prevWorldPos = null;
        const mousePos: Point = [event.clientX, event.clientY];
        const worldPos = this.graphics.screenToWorld(mousePos);

        if (event.button === 0 && this.ui.selectedTool.tileType != null) {
            const tileType = this.ui.selectedTool.tileType;
            this.world.placeTile(worldPos, tileType);
        } else if (event.button === 2) {
            this.world.removeTile(worldPos);
        }
        event.preventDefault();
    }

    private onMouseMove = (event: MouseEvent) => {
        const mousePos: Point = [event.clientX, event.clientY];
        const mouseDelta: Point = [event.movementX, event.movementY];
        const worldPos = this.graphics.screenToWorld(mousePos);

        if (this.mouseDown === 1) {
            this.canvas.style.cursor = 'grabbing';
            this.graphics.pan(mouseDelta);
        } else if (this.mouseDown === 0 && this.ui.selectedTool.tileType != null) {
            const tileType = this.ui.selectedTool.tileType;
            this.world.placeTile(worldPos, tileType, this.prevWorldPos);
        } else if (this.mouseDown === 2) {
            this.world.removeTile(worldPos);
        }
        if (!this.prevWorldPos || !pointsEquals(this.prevWorldPos, worldPos)) {
            this.prevWorldPos = worldPos;
        }
        this.graphics.highlight(mousePos);
        const tile = this.world.getTile(worldPos);
        if (tile) {
            this.graphics.showTooltip(mousePos, JSON.stringify(tile, null, 2));
        } else {
            this.graphics.showTooltip(mousePos, '');
        }
        event.preventDefault();
    }

    private onKeyPress = (event: KeyboardEvent) => {
        const key = event.key.toLowerCase();
        if (key === 'h') {
            this.graphics.resetView();
        } else if (key === ' ') {
            this.paused = !this.paused;
        } else if (key.length === 1 && key >= '1' && key <= '9') {
            const slot = parseInt(key);
            this.ui.useSlot(slot);
        } else {
            console.log(`Pressed: ${key}`);
        }
    }

    run() {
        this.running = true;
        this.loop();
    }

    private loop = () => {
        const now = performance.now();
        const dt = now - this.prevFrame;
        this.prevFrame = now;

        if (!this.paused) {
            this.world.update(dt);
        }

        this.graphics.clear();

        this.graphics.drawGrid();
        this.graphics.drawWorld(this.world);

        this.graphics.drawHighlight();

        this.graphics.drawTooltip();
        this.graphics.debug();

        if (this.running) {
            requestAnimationFrame(this.loop);
        }
    }
}

