/*
    Hurry Curry! - a game about cooking
    Copyright (C) 2026 Hurry Curry! Contributors

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, version 3 of the License only.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.

*/

import { ItemIndex, ItemLocation, PacketC } from "../protocol.ts";
import { System } from "../system.ts";
import { lerp_exp_v2_mut, V2 } from "../util.ts";
import { MapLayer } from "./mod.ts";
import { SPlayers } from "./players.ts";
import { SSprites } from "./sprites.ts";
import { ItemSlot, SMapTiles } from "./tiles.ts";

export interface ItemData {
    kind: ItemIndex,
    x: number,
    y: number,
    tracking?: V2,
    active?: Involvement
    remove_anim?: number
}
export interface Involvement {
    position: number,
    speed: number,
    warn: boolean
}

export class SMapItems extends System implements MapLayer {
    items_removed: Set<ItemData> = new Set()

    constructor(
        public ss: SSprites,
        public ts: SMapTiles,
        public ps: SPlayers,
    ) { super() }

    override packet(p: PacketC): void {
        if (p.type == "move_item") {
            const from = this.get_item_location(p.from)
            const to = this.get_item_location(p.to)

            to.item = from.item
            to.item!.tracking = this.get_item_location_tracking(p.to)
            from.item = undefined

        }
        else if (p.type == "set_item") {
            const slot = this.get_item_location(p.location)
            const slotpos = this.get_item_location_tracking(p.location)!
            if (slot.item !== undefined && slot.item !== null) this.items_removed.add(slot.item)
            delete slot.item
            if (p.item !== undefined && p.item !== null) slot.item = { kind: p.item, x: slotpos.x, y: slotpos.y, tracking: slotpos }
        } else if (p.type == "clear_progress") {
            delete this.get_item_location(p.item).item!.active
        } else if (p.type == "set_progress") {
            const slot = this.get_item_location(p.item)
            if (!slot.item) return
            slot.item.active = {
                position: p.position,
                speed: p.speed,
                warn: p.warn
            }
        }
    }

    override tick(dt: number): void {
        const update_item = (item: ItemData) => {
            if (item.tracking) lerp_exp_v2_mut(item, item.tracking, dt * 10.)
            if (item.active) item.active.position += item.active.speed * dt
        }

        for (const [_, tile] of this.ts.tiles)
            if (tile.item !== undefined && tile.item !== null) update_item(tile.item)

        const remove: ItemData[] = []
        for (const item of this.items_removed) {
            update_item(item)
            if (item.remove_anim === undefined) item.remove_anim = 0
            item.remove_anim += dt * 4.
            if (item.remove_anim > 1.) remove.push(item)
        }
        remove.forEach(i => this.items_removed.delete(i))

    }

    get_item_location(loc: ItemLocation): ItemSlot {
        if ("tile" in loc) return this.ts.tiles.get(loc.tile.toString())!
        if ("player" in loc) return this.ps.players.get(loc.player[0])!.hands[loc.player[1]]
        throw new Error("invalid item location");
    }
    get_item_location_tracking(loc: ItemLocation): V2 | undefined {
        if ("tile" in loc) return this.ts.tiles.get(loc.tile.toString())?.position
        if ("player" in loc) return this.ps.players.get(loc.player[0])?.position
        throw new Error("invalid item location");
    }

    draw_map_layer(ctx: CanvasRenderingContext2D): void {
        for (const item of this.items_removed)
            draw_item(ctx, this.ss, item)

        for (const [_, tile] of this.ts.tiles)
            if (tile.item) draw_item(ctx, this.ss, tile.item)
    }
}

export function draw_item(ctx: CanvasRenderingContext2D, sprites: SSprites, item: ItemData) {
    ctx.save()
    ctx.translate(item.x, item.y)
    if (item.remove_anim) ctx.scale(1 - item.remove_anim, 1 - item.remove_anim)
    sprites.draw_item(ctx, item.kind)
    if (item.active) {
        ctx.beginPath()
        ctx.strokeStyle = item.active.warn
            ? `rgba(230, 58, 58, ${Math.sin(item.active.position * 100.) * 0.3 + 0.7})`
            : "rgb(115, 230, 58)"
        ctx.lineWidth = 0.1
        ctx.arc(0, 0, 0.45, -Math.PI / 2, -Math.PI / 2 + Math.PI * 2 * item.active.position)
        ctx.stroke()
    }
    ctx.restore()
}
