import { globalConfig } from "../../core/config";
import { Loader } from "../../core/loader";
import { smoothPulse } from "../../core/utils";
import { enumItemProcessorRequirements, enumItemProcessorTypes } from "../components/item_processor";
import { Entity } from "../entity";
import { GameSystem } from "../game_system";
import { isTruthyItem } from "../items/boolean_item";
import { MapChunkView } from "../map_chunk_view";

export class ItemProcessorOverlaysSystem extends GameSystem {
    constructor(root) {
        super(root);

        this.spriteDisabled = Loader.getSprite("sprites/misc/processor_disabled.png");
        this.spriteDisconnected = Loader.getSprite("sprites/misc/processor_disconnected.png");

        this.readerOverlaySprite = Loader.getSprite("sprites/misc/reader_overlay.png");

        this.drawnUids = new Set();
        this.root.signals.gameFrameStarted.add(this.clearDrawnUids, this);
    }

    clearDrawnUids() {
        this.drawnUids.clear();
    }

    /**
     *
     * @param {import("../../core/draw_utils").DrawParameters} parameters
     * @param {MapChunkView} chunk
     */
    drawChunk(parameters, chunk) {
        const contents = chunk.containedEntitiesByLayer.regular;
        for (let i = 0; i < contents.length; ++i) {
            const entity = contents[i];
            const processorComp = entity.components.ItemProcessor;
            const filterComp = entity.components.Filter;

            // Draw processor overlays
            if (processorComp) {
                const requirement = processorComp.processingRequirement;
                if (!requirement && processorComp.type !== enumItemProcessorTypes.reader) {
                    continue;
                }

                if (this.drawnUids.has(entity.uid)) {
                    continue;
                }
                this.drawnUids.add(entity.uid);

                switch (requirement) {
                    case enumItemProcessorRequirements.painterQuad: {
                        this.drawConnectedSlotRequirement(parameters, entity, { drawIfFalse: true });
                        break;
                    }
                }

                if (processorComp.type === enumItemProcessorTypes.reader) {
                    this.drawReaderOverlays(parameters, entity);
                }
            }

            // Draw filter overlays
            else if (filterComp) {
                if (this.drawnUids.has(entity.uid)) {
                    continue;
                }
                this.drawnUids.add(entity.uid);

                this.drawConnectedSlotRequirement(parameters, entity, { drawIfFalse: false });
            }
        }
    }

    /**
     *
     * @param {import("../../core/draw_utils").DrawParameters} parameters
     * @param {Entity} entity
     */
    drawReaderOverlays(parameters, entity) {
        const staticComp = entity.components.StaticMapEntity;
        const readerComp = entity.components.BeltReader;

        this.readerOverlaySprite.drawCachedCentered(
            parameters,
            (staticComp.origin.x + 0.5) * globalConfig.tileSize,
            (staticComp.origin.y + 0.5) * globalConfig.tileSize,
            globalConfig.tileSize
        );

        parameters.context.fillStyle = "#333439";
        parameters.context.textAlign = "center";
        parameters.context.font = "bold 10px GameFont";
        parameters.context.fillText(
            "" + Math.round(readerComp.lastThroughput * 10) / 10,
            (staticComp.origin.x + 0.5) * globalConfig.tileSize,
            (staticComp.origin.y + 0.62) * globalConfig.tileSize
        );

        parameters.context.textAlign = "left";
    }

    /**
     *
     * @param {import("../../core/draw_utils").DrawParameters} parameters
     * @param {Entity} entity
     * @param {object} param0
     * @param {boolean=} param0.drawIfFalse
     */
    drawConnectedSlotRequirement(parameters, entity, { drawIfFalse = true }) {
        const staticComp = entity.components.StaticMapEntity;
        const pinsComp = entity.components.WiredPins;

        let anySlotConnected = false;

        // Check if any slot has a value
        for (let i = 0; i < pinsComp.slots.length; ++i) {
            const slot = pinsComp.slots[i];
            const network = slot.linkedNetwork;
            if (network && network.hasValue()) {
                anySlotConnected = true;

                if (isTruthyItem(network.currentValue) || !drawIfFalse) {
                    // No need to draw anything
                    return;
                }
            }
        }

        const pulse = smoothPulse(this.root.time.now());
        parameters.context.globalAlpha = 0.6 + 0.4 * pulse;
        const sprite = anySlotConnected ? this.spriteDisabled : this.spriteDisconnected;
        sprite.drawCachedCentered(
            parameters,
            (staticComp.origin.x + 0.5) * globalConfig.tileSize,
            (staticComp.origin.y + 0.5) * globalConfig.tileSize,
            globalConfig.tileSize * (0.7 + 0.2 * pulse)
        );

        parameters.context.globalAlpha = 1;
    }
}
