import { GameObject } from "./gameObject.js";
import { loadSprite } from "./loadSprite.js";
import { gameTime } from "./gameTime.js";
import { anthill } from "./anthill.js";

export let signalantArray = [];

export function addSignalant(signalant) {
    if (!signalant || signalantArray.includes(signalant)) {
        return;
    }

    signalantArray.push(signalant);
}

export function removeSignalant(signalant) {
    const index = signalantArray.indexOf(signalant);
    if (index === -1) {
        return false;
    }

    signalantArray.splice(index, 1);
    return true;
}

export class Signalant extends GameObject {
    constructor(id, options = {}) {
        super(id, options);
        this.setAnimation(options.animationFolderPath || "assets/ants/signal-ant", options.animationFrameCount || 4);
        this.setPosition(options.x || 0, options.y || 0);
        this.setSize(options.width || 24, options.height || 21);
        this.direction = options.direction || Math.random() * 360;

        this.colorChangeSpeed = options.colorChangeSpeed || 360;

        this.affectsColors = options.affectsColors || this.setRandomColors(3);
        this.affectionRadius = options.affectionRadius || Math.floor(Math.random() * 50 + 50);
        this.showAffectionRadius = options.showAffectionRadius || true;
        this.affectedColorsDirections = {};
        this.nextAffectedColorDirectionIndex = {};
        this.antDirectionAssignments = {};
        this.affectsColors.forEach(color => {
            if (!(color in this.affectedColorsDirections)) {
                this.affectedColorsDirections[color] = [];
                this.nextAffectedColorDirectionIndex[color] = 0;
                this.antDirectionAssignments[color] = {};
            }

            this.affectedColorsDirections[color].push(Math.floor(Math.random() * 360));
        });
        if (options.affectedColorsDirections) {
            this.affectedColorsDirections = {};
            this.affectedColorsDirections = options.affectedColorsDirections;
        }
        this.isActive = options.isActive || false;
        this.isFixed = options.isFixed || false;

        if (!options.toStorage) {
            addSignalant(this);
        }
    }

    setRandomColors(count) {
        const colors = Array.from(
            new Set(
                anthill.unlockedAntsData
                    .map(antData => antData.antDefinition.color)
                    .filter(Boolean)
            )
        );

        const selectedColors = [];
        while (selectedColors.length < count && colors.length > 0) {
            const index = Math.floor(Math.random() * colors.length);
            selectedColors.push(colors[index]);
        }

        return selectedColors;
    }

    drawAffectionRadius(ctx, x, y, radius, cam) {
        ctx.save();
        ctx.beginPath();
        ctx.arc(x, y, radius, 0, 2 * Math.PI);
        ctx.strokeStyle = "rgba(0, 0, 0, 0.5)";
        ctx.lineWidth = 2 * cam.zoom;
        ctx.stroke();
        ctx.restore();
    }

    getColorCode(color) {
        const matchingAnt = anthill.unlockedAntsData.find(
            antData => antData.antDefinition.color === color
        );

        return matchingAnt?.antDefinition.colorCode || color;
    }

    getRelativeDirectionForAnt(color, antId) {
        const directions = this.affectedColorsDirections[color];
        if (!Array.isArray(directions) || directions.length === 0) {
            return 0;
        }

        const assignments = this.antDirectionAssignments[color];
        if (!(antId in assignments)) {
            const nextIndex = this.nextAffectedColorDirectionIndex[color] % directions.length;
            assignments[antId] = nextIndex;
            this.nextAffectedColorDirectionIndex[color] = (nextIndex + 1) % directions.length;
        }

        return directions[assignments[antId]];
    }

    clearRelativeDirectionForAnt(antId) {
        Object.values(this.antDirectionAssignments).forEach(assignments => {
            delete assignments[antId];
        });
    }

    drawSignal(ctx, x, y, width, height, direction = 0, centered = false, options = {}) {
        const layerCount = this.affectsColors.length;

        if (layerCount === 0) {
            return;
        }

        const animateSignal = options.animateSignal ?? true;
        const layerOffset = animateSignal && this.colorChangeSpeed > 0
            ? Math.floor(gameTime.now() / this.colorChangeSpeed) % layerCount
            : 0;

        this.affectsColors.forEach((color, index) => {
            const layerIndex = ((index + layerOffset) % layerCount) + 1;
            const layerSprite = loadSprite(`assets/ants/signal-ant/layer/${color}_${layerIndex}.png`);
            if (layerSprite) {
                if (centered || direction !== 0) {
                    ctx.save();
                    ctx.translate(x, y);
                    if (direction !== 0) {
                        ctx.rotate(direction * Math.PI / 180);
                    }
                    if (centered) {
                        ctx.drawImage(layerSprite, -width / 2, -height / 2, width, height);
                    } else {
                        ctx.drawImage(layerSprite, 0, 0, width, height);
                    }
                    ctx.restore();
                } else {
                    ctx.drawImage(layerSprite, x, y, width, height);
                }
            }
        });
    }

    draw(ctx, cam) {
        super.draw(ctx, cam);
        if (this.showAffectionRadius) {
            const relativeX = Math.round((this.x + cam.x) * cam.zoom);
            const relativeY = Math.round((this.y + cam.y) * cam.zoom);
            const radius = Math.round(this.affectionRadius * cam.zoom);
            if( this.isActive ) this.drawAffectionRadius(ctx, relativeX, relativeY, radius, cam);

            const arrowLength = 10 * cam.zoom;
            const arrowAngle = 20 * Math.PI / 180;
            Object.entries(this.affectedColorsDirections).forEach(([color, relativeDirections]) => {
                relativeDirections.forEach(relativeDirection => {
                    const absoluteDirection = this.direction + relativeDirection;
                    const directionInRadians = absoluteDirection * Math.PI / 180;
                    const arrowX = relativeX + Math.cos(directionInRadians) * radius / 2;
                    const arrowY = relativeY + Math.sin(directionInRadians) * radius / 2;

                    ctx.save();
                    ctx.translate(arrowX, arrowY);
                    ctx.rotate(directionInRadians);
                    ctx.beginPath();
                    ctx.moveTo(0, 0);
                    ctx.lineTo(-arrowLength, -arrowLength * Math.tan(arrowAngle));
                    ctx.lineTo(-arrowLength, arrowLength * Math.tan(arrowAngle));
                    ctx.closePath();
                    ctx.lineWidth = 2 * cam.zoom;
                    ctx.strokeStyle = "black";
                    ctx.stroke();
                    ctx.fillStyle = this.getColorCode(color) || "rgba(255, 0, 0, 0.5)";
                    ctx.fill();
                    ctx.restore();
                });
            });
        }

        const relativeX = Math.round((this.x + cam.x) * cam.zoom);
        const relativeY = Math.round((this.y + cam.y) * cam.zoom);
        const width = Math.round(this.width * cam.zoom);
        const height = Math.round(this.height * cam.zoom);
        this.drawSignal(ctx, relativeX, relativeY, width, height, this.direction, true);
    }

    drawAt(ctx, x, y, scale = 1, options = {}) {
        super.drawAt(ctx, x, y, scale);
        this.drawSignal(ctx, x, y, this.width * scale, this.height * scale, 0, false, options);
    }
}

export function drawSignalants(ctx, cam) {
    for (let signalant of signalantArray) {
        signalant.draw(ctx, cam);
    }
}