import { AssetPreloaderService } from "../../../assetPreloaderService";
import { SplashComponent } from "../../splash/splash.component";
import { SimMain } from "../sim/SimMain";
import { VisLevel } from "./VisLevel";

export class VisMain {
    canvas: HTMLCanvasElement;
    context: CanvasRenderingContext2D;
    wallPattern: CanvasPattern;
    visLevel: VisLevel;
    simMain: SimMain;
    private startTimestamp: number = 0;
    private active: boolean = true;
    private ready: boolean = false;
    private gap: number = 0;
    assets: AssetPreloaderService;
    wrapper: HTMLDivElement;

    constructor(simMain: SimMain, assets: AssetPreloaderService, wrapper: HTMLDivElement, canvas: HTMLCanvasElement) {
        this.assets = assets;
        this.simMain = simMain;
        this.canvas = canvas;
        this.wrapper = wrapper;
        this.context = this.canvas.getContext("2d")!;
        this.context.globalCompositeOperation = "source-over";
        this.wallPattern = this.createPattern(assets.getImage("images/wall.png"), 48);
        this.visLevel = new VisLevel(this, this.simMain, this.simMain.gdRoot, assets);
        this.start();
    }

    private createPattern(image: HTMLImageElement, size: number): CanvasPattern {
        const tempCanvas = document.createElement("canvas");
        const tempContext = tempCanvas.getContext("2d")!;

        tempCanvas.width = size;
        tempCanvas.height = size;
        tempContext.drawImage(image, 0, 0, image.width, image.height, 0, 0, size, size);

        return this.context.createPattern(tempCanvas, 'repeat')!;
    }

    private step(timestamp: number) {
        if (!this.active) {
            return;
        }

        requestAnimationFrame((timestamp: number) => {
            this.step(timestamp);
        });

        if (!this.startTimestamp) {
            this.startTimestamp = timestamp;
        }

        const simLevel = this.simMain.currentLevel;
        if (!simLevel) {
            return;
        }

        let targetStep = (timestamp - this.startTimestamp) * this.simMain.gdRoot.simulation.stepsPerSecond / 1000 - this.gap;
        if (simLevel.paused) {
            this.gap += targetStep - simLevel.currentStep;
            targetStep = simLevel.currentStep;
        }
        this.simMain.executeUntilStep(targetStep);
        this.visLevel.updateEveryFrame(targetStep);
        this.onRender();
    };

    public onResized() {
        // Always get the latest wrapper size
        const width = this.wrapper.clientWidth;
        const height = this.wrapper.clientHeight;
        const ratio = window.devicePixelRatio;

        // Set canvas style to always fill the wrapper
        this.canvas.width = width * ratio;
        this.canvas.height = height * ratio;

        // Reset transform before scaling to avoid compounding
        this.context.setTransform(1, 0, 0, 1, 0, 0);
        this.context.scale(ratio, ratio);

        this.visLevel.updateSize();
    };

    private onRender() {
        this.clear();
        const ctx = this.context;
        ctx.font = "12px Tahoma";
        const simLevel = this.simMain.currentLevel;
        if (!!simLevel) {
            if (!this.ready) {
                this.onResized();
                this.ready = true;
            }
            this.visLevel.draw();
        }
    };

    private clear() {
        this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
    }

    stop() {
        this.active = false;
    }

    start() {
        this.active = true;
        requestAnimationFrame((timestamp: number) => {
            this.step(timestamp);
        });
    }
}
