import {
    BuildingState, CartState,
    Event,
    Kill,
    ParsedDemo,
    PlayerState,
    ProjectileState,
    ProjectileType,
    WorldBoundaries
} from "./Parser";

function getCacheBuster(): string {
    const url = document.querySelector('script[src*="viewer"]').attributes.src.value;
    return url.substring("/viewer.js".length);
}

export class AsyncParser {
    buffer: ArrayBuffer;
    demo: ParsedDemo;
    world: WorldBoundaries;
    progressCallback: (progress: number) => void;

    constructor(buffer: ArrayBuffer, progressCallback: (progress: number) => void) {
        this.buffer = buffer;
        this.progressCallback = progressCallback;
    }

    cache(): Promise<ParsedDemo> {
        return new Promise((resolve, reject) => {
            const worker = new Worker(`/parse-worker.js${getCacheBuster()}`);
            worker.postMessage({
                buffer: this.buffer
            }, [this.buffer]);
            worker.onmessage = (event) => {
                if (event.data.error) {
                    reject(event.data.error);
                    return;
                } else if (event.data.progress) {
                    this.progressCallback(event.data.progress);
                    return;
                } else if (event.data.demo) {
                    const cachedData: ParsedDemo = event.data.demo;
                    console.log(`packed data: ${(cachedData.data.length / (1024 * 1024)).toFixed(1)}MB`);
                    this.world = cachedData.world;
                    this.demo = new ParsedDemo(
                        cachedData.playerCount,
                        cachedData.buildingCount,
                        cachedData.projectileCount,
                        cachedData.hasCart,
                        cachedData.world,
                        cachedData.header,
                        cachedData.data,
                        cachedData.kills,
                        cachedData.playerInfo,
                        cachedData.events,
                        cachedData.tickCount
                    );
                    resolve(this.demo);
                }
            }
        });
    }

    getPlayersAtTick(tick: number): PlayerState[] {
        const players: PlayerState[] = [];
        for (let i = 0; i < this.demo.playerCount; i++) {
            players.push(this.demo.getPlayer(tick, i));
        }

        return players;
    }

    getBuildingsAtTick(tick: number): BuildingState[] {
        const buildings: BuildingState[] = [];
        for (let i = 0; i < this.demo.buildingCount; i++) {
            const building = this.demo.getBuilding(tick, i);
            if (building.health > 0) {
                buildings.push(building);
            }
        }

        return buildings;
    }

    getProjectilesAtTick(tick: number): ProjectileState[] {
        const projectiles: ProjectileState[] = [];
        for (let i = 0; i < this.demo.projectileCount; i++) {
            const projectile = this.demo.getProjectile(tick, i);
            if (projectile.projectileType !== ProjectileType.Unknown && projectile.position.x > this.world.boundary_min.x && projectile.position.y > this.world.boundary_min.y) {
                projectiles.push(projectile);
            }
        }

        return projectiles;
    }

    getCart(tick: number): CartState | null {
        return this.demo.getCart(tick);
    }

    getKills(): Kill[] {
        return this.demo.kills
    }

    getEvents(): Event[] {
        return this.demo.events
    }
}
