/*
    Hurry Curry! - a game about cooking
    Copyright (C) 2025 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 { tr } from "./locale.ts";
import { ItemData, MessageData, MessageStyle, PlayerData, TileData, camera, camera_scale, canvas, ctx, data, get_interact_target, global_message, interact_active_anim, interact_possible_anim, interact_target_anim, is_lobby, items_removed, keys_down, my_id, overlay_vis_anim, players, score, server_hints, tiles } from "./main.ts";
import { PLAYER_SIZE } from "./movement.ts";
import { draw_item_sprite, draw_tile_sprite, ItemName, TileName } from "./tiles.ts";
import { V2, ceil_v2, floor_v2 } from "./util.ts";
import { Message, PlayerClass } from "./protocol.ts";
import { draw_particles, particle_count } from "./particles.ts";

export function draw_wait(text: string) {
    ctx.fillStyle = "#444"
    ctx.fillRect(0, 0, canvas.width, canvas.height)
    ctx.fillStyle = "#555"
    ctx.font = "50px sans-serif"
    ctx.strokeStyle = "black"
    ctx.fillStyle = "white"
    ctx.lineWidth = 10
    ctx.textAlign = "center"
    ctx.textBaseline = "middle"
    ctx.lineJoin = "round"
    ctx.lineCap = "round"
    ctx.strokeText(text, canvas.width / 2, canvas.height / 2)
    ctx.fillText(text, canvas.width / 2, canvas.height / 2)
}

export function draw_ingame() {
    ctx.fillStyle = "#111"
    ctx.fillRect(0, 0, canvas.width, canvas.height)

    ctx.save()
    ctx.translate(canvas.width / 2, canvas.height / 2)
    ctx.scale(camera_scale, camera_scale)
    ctx.translate(-camera.x, -camera.y)

    draw_grid()

    for (const [_, tile] of tiles)
        draw_tile(tile)

    for (const [_, player] of players)
        draw_player(player)

    for (const item of items_removed)
        draw_item(item)

    for (const [_, tile] of tiles)
        if (tile.item) draw_item(tile.item)

    draw_particles()

    draw_interact_target()

    for (const [_, player] of players)
        if (player.message) draw_message(player.message, true)

    for (const [_, player] of players)
        if (player.message_persist) draw_message(player.message_persist, true)

    for (const [_, player] of players)
        draw_player_nametag(player)

    for (const [_, message] of server_hints)
        draw_message(message, true)

    ctx.restore()
    ctx.save()
    ctx.translate(50, 50)
    ctx.scale(80, 80)

    for (const [_, player] of players)
        if (player.message_pinned) draw_message(player.message_pinned, false)

    ctx.restore()

    draw_global_message()

    if (!is_lobby)
        draw_score()

    if (keys_down.has("KeyP"))
        draw_debug()
}

function draw_score() {
    ctx.fillStyle = "white"
    ctx.textAlign = "left"
    ctx.textBaseline = "bottom"
    ctx.font = "20px sans-serif"
    ctx.fillText(`${tr("c.score.time_remaining")}: ${score.time_remaining?.toFixed(2)}`, 10, canvas.height - 90)
    ctx.font = "30px sans-serif"
    ctx.fillText(`${tr("c.score.points")}: ${score.points}`, 10, canvas.height - 60)
    ctx.font = "20px sans-serif"
    ctx.fillText(`${tr("c.score.completed")}: ${score.demands_completed}`, 10, canvas.height - 30)
    ctx.fillText(`${tr("c.score.failed")}: ${score.demands_failed}`, 10, canvas.height - 10)
}

function draw_debug() {
    ctx.fillStyle = "white"
    ctx.textAlign = "left"
    ctx.textBaseline = "bottom"
    ctx.font = "20px sans-serif"
    ctx.fillText(`position = ${JSON.stringify(players.get(my_id)?.anim_position)}`, 10, 30)
    ctx.fillText(`velocity = ${JSON.stringify(players.get(my_id)?.vel)}`, 10, 50)
    ctx.fillText(`interact = ${JSON.stringify(get_interact_target())}`, 10, 70)
    ctx.fillText(`particle_count = ${particle_count()}`, 10, 90)
}

function draw_tile(tile: TileData) {
    ctx.save()
    ctx.translate(tile.x + 0.5, tile.y + 0.5)
    draw_tile_sprite(ctx, data.tile_names[tile.kind] as TileName)
    ctx.restore()
}

function draw_item(item: ItemData) {
    ctx.save()
    ctx.translate(item.x, item.y)
    if (item.remove_anim) ctx.scale(1 - item.remove_anim, 1 - item.remove_anim)
    draw_item_sprite(ctx, data.item_names[item.kind] as ItemName)
    if (item.active) {
        ctx.fillStyle = item.active.warn ? "rgba(230, 58, 58, 0.66)" : "rgba(115, 230, 58, 0.66)"
        ctx.fillRect(-0.5, -0.5, 1, item.active.position)
    }
    ctx.restore()
}

function draw_player(player: PlayerData) {
    ctx.save()
    ctx.translate(player.anim_position.x, player.anim_position.y)
    ctx.rotate(-player.rot)
    if (player.boosting) ctx.scale(1.3, 1.3)
    draw_character(player.class, player.character)
    ctx.restore()
    ctx.save()
    for (const h of player.hands) {
        if (h.item) draw_item(h.item)
        ctx.translate(0.2, 0.0)
    }
    ctx.restore()
}

function draw_player_nametag(player: PlayerData) {
    if (overlay_vis_anim > 0.01) {
        ctx.save()
        ctx.translate(player.anim_position.x, player.anim_position.y)
        ctx.translate(0, -1)
        ctx.textAlign = "center"
        ctx.font = "15px sans-serif"
        ctx.scale(overlay_vis_anim / camera_scale, overlay_vis_anim / camera_scale)
        const w = ctx.measureText(player.name).width + 20
        ctx.fillStyle = "#fffa"
        ctx.beginPath()
        ctx.roundRect(-w / 2, -10, w, 20, 5)
        ctx.fill()
        ctx.fillStyle = "black"
        ctx.textBaseline = "middle"
        ctx.fillText(player.name, 0, 0)
        ctx.restore()
    }
}

function draw_interact_target() {
    ctx.save()
    ctx.translate(interact_target_anim.x, interact_target_anim.y)

    ctx.lineCap = "round"
    ctx.lineJoin = "round"
    ctx.lineWidth = 0.06 + 0.03 * Math.sin(Date.now() / 100) * interact_possible_anim
    ctx.strokeStyle = `hsla(${(1 - interact_active_anim) * 225 + interact_active_anim * 125}deg, ${interact_possible_anim * 100}%, 62.70%, ${interact_possible_anim * 0.7 + 0.3})`
    ctx.strokeRect(0, 0, 1, 1)

    ctx.restore()
}

function draw_grid() {
    ctx.strokeStyle = "#333"
    ctx.lineWidth = 0.01
    ctx.beginPath()
    const min = floor_v2(map_screen_to_world({ x: 0, y: 0 }))
    const max = ceil_v2(map_screen_to_world({ x: canvas.width, y: canvas.height }))
    for (let x = min.x; x < max.x; x++) {
        ctx.moveTo(x, min.y)
        ctx.lineTo(x, max.y)
    }
    for (let y = min.y; y < max.y; y++) {
        ctx.moveTo(min.x, y)
        ctx.lineTo(max.x, y)
    }
    ctx.stroke()
}

function draw_character(pclass: PlayerClass, character: number) {
    ctx.fillStyle = `hsl(${character}rad, 50%, 50%)`
    ctx.beginPath()
    ctx.arc(0, 0, PLAYER_SIZE, 0, Math.PI * 2)
    ctx.fill()

    if (pclass != "customer") {
        ctx.fillStyle = `hsl(${character}rad, 80%, 10%)`
        ctx.beginPath()
        ctx.arc(0, -0.2, PLAYER_SIZE, 0, Math.PI * 2)
        ctx.fill()
    }
    if (pclass != "bot") {
        ctx.fillStyle = `hsl(${character}rad, 80%, 70%)`
        ctx.beginPath()
        ctx.moveTo(-0.04, 0.25)
        ctx.lineTo(0.04, 0.25)
        ctx.lineTo(0, 0.4)
    }
    ctx.fill()
}

function message_str(m: Message): string {
    if ("text" in m) return m.text
    if ("translation" in m) return tr(m.translation.id, ...m.translation.params.map(message_str))
    if ("tile" in m) return data.tile_names[m.tile]
    if ("item" in m) return data.item_names[m.item]
    return "[unknown message type]"
}

const MESSAGE_BG: { [key in MessageStyle]: string } = { normal: "#fff", hint: "#111", error: "#fff", pinned: "rgb(4, 32, 0)" }
const MESSAGE_FG: { [key in MessageStyle]: string } = { normal: "#000", hint: "#fff", error: "#a00", pinned: "#000" }
function draw_message(m: MessageData, world: boolean) {
    ctx.save()
    ctx.translate(m.anim_position.x, m.anim_position.y)
    const scale = Math.min(m.anim_size, 1 - overlay_vis_anim);
    ctx.scale(scale, scale)
    if ("item" in m.inner) {
        ctx.fillStyle = MESSAGE_BG[m.style]
        if (m.style == "pinned") {
            ctx.translate(0, 1)
            ctx.beginPath()
            ctx.arc(0, -1, 0.5, 0, Math.PI * 2)
            ctx.fill()
        } else {
            ctx.beginPath()
            ctx.moveTo(0, -0.3)
            ctx.arc(0, -1, 0.5, Math.PI / 4, Math.PI - Math.PI / 4, true)
            ctx.closePath()
            ctx.fill()
        }

        if (m.timeout) {
            const t = m.timeout.remaining / m.timeout.initial;
            ctx.beginPath()
            ctx.strokeStyle = `hsl(${Math.sqrt(t) * 0.3}turn, 100%, 50%)`
            ctx.lineWidth = 0.1
            ctx.arc(0, -1, 0.45, -Math.PI / 2, -Math.PI / 2 + Math.PI * 2 * (1 - t))
            ctx.stroke()
        }

        ctx.translate(0, -1)
        draw_item_sprite(ctx, data.item_names[m.inner.item] as ItemName)
        ctx.translate(0, 1)
    }
    if ("text" in m.inner || "translation" in m.inner) {
        ctx.translate(0, -1)

        ctx.textAlign = "center"
        ctx.font = "15px " + (world ? "sans-serif" : "monospace")
        if (world) ctx.scale(2 / camera_scale, 2 / camera_scale)

        const lines = message_str(m.inner).split("\n")
        const w = lines.reduce((a, v) => Math.max(a, ctx.measureText(v).width), 0) + 10

        if (world) ctx.translate(0, -lines.length * 15 / 2)

        ctx.fillStyle = MESSAGE_BG[m.style]
        ctx.beginPath()
        ctx.roundRect(-w / 2, -5, w, lines.length * 15 + 10, 5)
        ctx.fill()

        ctx.fillStyle = MESSAGE_FG[m.style]
        ctx.textAlign = "left"
        ctx.textBaseline = "top"
        for (let i = 0; i < lines.length; i++)
            ctx.fillText(lines[i], -w / 2 + 5, i * 15)

        ctx.translate(0, 1)
    }
    ctx.restore()
}

function draw_global_message() {
    if (!global_message) return
    ctx.save()
    ctx.translate(canvas.width / 2, canvas.height / 6)
    ctx.scale(2, 2)
    draw_message(global_message, false)
    ctx.restore()
}

function map_screen_to_world(screen: V2): V2 {
    return {
        x: ((screen.x - canvas.width / 2) / camera_scale) + camera.x,
        y: ((screen.y - canvas.height / 2) / camera_scale) + camera.y,
    }
}

