
export type Annoyer = {
    image: string,
    x: number,
    y: number,
    angle: number,
    xSpeed: number,
    ySpeed: number,
    angleSpeed: number,
    isVisible: boolean,
    wasVisible: boolean,
    age: number,
    uuid: string
};

const randomGaussian = () => {
    return Math.sqrt(-2 * Math.log(1 - Math.random())) * Math.cos(2 * Math.PI * Math.random());
}
export class AnnoyerHandler {
    public annoyers: Set<Annoyer>;
    private images: string[];
    private max_annoyers: number;

    constructor(images: string[], start_limit?: number) {
        this.annoyers = new Set<Annoyer>();
        this.images = images;
        this.max_annoyers = start_limit ?? 0;
        while (this.annoyers.size < this.max_annoyers) {
            this.annoyers.add(this.generateAnnoyer());
        }
    }

    generateAnnoyer = (): Annoyer => {
        const usedImages = new Set(Array.from(this.annoyers).map(annoyer => annoyer.image));
        const possibleImages = this.images.filter(image => !usedImages.has(image));
        let image: string;
        if (possibleImages.length !== 0) {
            //console.log("there are unused images, taking one of them");
            const ims = Array.from(possibleImages);
            image = ims[Math.floor(Math.random() * ims.length)];
        } else {
            //console.log("all images are in use, taking one at random");
            image = this.images[Math.floor(Math.random() * this.images.length)];
        }
        const maxWidth = document.documentElement.clientWidth;
        const maxHeight = document.documentElement.clientHeight;
        const outerMargin = 150;

        const startingPosition = (): [number, number] => {
            const r1 = Math.random() * 2 - 1;
            const r2 = Math.random() * 2 - 1;
            const marginX = r1 * outerMargin;
            const marginY = r2 * outerMargin;
            const x = r1 < 0 ? marginX : maxWidth + marginX;
            const y = r2 < 0 ? marginY : maxHeight + marginY;
            return [x,y];
        };
        const [startX, startY] = startingPosition();
        
        const speed = (startingPosition: [number, number]): [number, number] => {
            const r1 = Math.random();
            const r2 = Math.random();
            const targetX = r1 * maxWidth;
            const targetY = r2 * maxHeight;
            const distance = Math.sqrt(
                (targetX - startingPosition[0]) * (targetX - startingPosition[0])  +
                (targetY - startingPosition[1]) * (targetY - startingPosition[1])
                )
            const speed = ( Math.abs(randomGaussian()) + 0.5) * 2;
            const normalizedSpeed = speed / distance;
            return [(targetX - startingPosition[0]) * normalizedSpeed, (targetY - startingPosition[1]) * normalizedSpeed];
        };
        const [speedX, speedY] = speed([startX, startY]);
        const newAnnoyer =  {
            image: image,
            x: startX,
            y: startY,
            angle: 0,
            angleSpeed: randomGaussian(),
            xSpeed: speedX ,
            ySpeed: speedY ,
            age: Math.random() * -1000,
            isVisible: false,
            wasVisible: false,
            uuid: "" + Math.random()
        };
        return newAnnoyer;
    };
    
    die = (annoyer: Annoyer) => {
        this.annoyers.delete(annoyer);
        this.spawnUntilLimit();
    };

    changeLimit = (newValue: number) => {
        this.max_annoyers = newValue;
        this.spawnUntilLimit();
    };

    spawnUntilLimit = () => {
        while (this.annoyers.size < this.max_annoyers)
            this.annoyers.add(this.generateAnnoyer());
    };


    tick = () => {
        this.annoyers.forEach(annoyer => {
            //if (annoyer.age > 10000) return;
            annoyer.x += annoyer.xSpeed;
            annoyer.y += annoyer.ySpeed;
            annoyer.angle += annoyer.angleSpeed;
            annoyer.age++;

            if (annoyer.isVisible && !annoyer.wasVisible) {
                //console.log("annoyer became visible");
            } else if (annoyer.wasVisible && !annoyer.isVisible) {
                //console.log("annoyer became invisible");
                //console.log("annoyer died at the age of ",annoyer.age);
                this.die(annoyer);
            } else if (annoyer.age > 10000) this.die(annoyer);
            annoyer.wasVisible = annoyer.isVisible;
        });
    };


}