// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
// ┃ ██████ ██████ ██████       █      █      █      █      █ █▄  ▀███ █       ┃
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█  ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄  ▀█ █ ▀▀▀▀▀ ┃
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄   █ ▄▄▄▄▄ ┃
// ┃ █      ██████ █  ▀█▄       █ ██████      █      ███▌▐███ ███████▄ █       ┃
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
// ┃ Copyright (c) 2017, the Perspective Authors.                              ┃
// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
// ┃ This file is part of the Perspective library, distributed under the terms ┃
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

import type { Context2D } from "../charts/canvas-types";
import type { PlotRect } from "../layout/plot-layout";

export const TICK_SIZE = 5;

/**
 * One horizontal row of numeric axis ticks + labels at CSS-pixel `axisY`.
 * `side` selects tick direction (down into the bottom margin or up into
 * the top margin) and the corresponding label baseline. Caller owns
 * `strokeStyle`, `fillStyle`, `font`, and `lineWidth`.
 */
export function drawXTickRow(
    ctx: Context2D,
    plot: PlotRect,
    ticks: number[],
    axisY: number,
    side: "top" | "bottom",
    xToPixel: (v: number) => number,
    format: (v: number) => string,
): void {
    const dir = side === "bottom" ? 1 : -1;
    ctx.textAlign = "center";
    ctx.textBaseline = side === "bottom" ? "top" : "bottom";
    const labelOffset = dir * (TICK_SIZE + 3);
    for (const tick of ticks) {
        const px = xToPixel(tick);
        if (px < plot.x - 1 || px > plot.x + plot.width + 1) {
            continue;
        }

        ctx.beginPath();
        ctx.moveTo(px, axisY);
        ctx.lineTo(px, axisY + dir * TICK_SIZE);
        ctx.stroke();
        ctx.fillText(format(tick), px, axisY + labelOffset);
    }
}

/**
 * One vertical column of numeric axis ticks + labels at CSS-pixel `axisX`.
 * `side` selects tick direction (out toward the left or right margin) and
 * the corresponding label alignment. Caller owns styling state.
 */
export function drawYTickColumn(
    ctx: Context2D,
    plot: PlotRect,
    ticks: number[],
    axisX: number,
    side: "left" | "right",
    yToPixel: (v: number) => number,
    format: (v: number) => string,
): void {
    const dir = side === "left" ? -1 : 1;
    ctx.textAlign = side === "left" ? "right" : "left";
    ctx.textBaseline = "middle";
    const labelOffset = dir * (TICK_SIZE + 3);
    for (const tick of ticks) {
        const py = yToPixel(tick);
        if (py < plot.y - 1 || py > plot.y + plot.height + 1) {
            continue;
        }

        ctx.beginPath();
        ctx.moveTo(axisX, py);
        ctx.lineTo(axisX + dir * TICK_SIZE, py);
        ctx.stroke();
        ctx.fillText(format(tick), axisX + labelOffset, py);
    }
}

/**
 * Vertical gridlines at numeric X ticks, clipped to `plot`.
 */
export function drawGridlinesX(
    ctx: Context2D,
    plot: PlotRect,
    ticks: number[],
    xToPixel: (v: number) => number,
): void {
    for (const tick of ticks) {
        const px = Math.round(xToPixel(tick)) + 0.5;
        if (px < plot.x || px > plot.x + plot.width) {
            continue;
        }

        ctx.beginPath();
        ctx.moveTo(px, plot.y);
        ctx.lineTo(px, plot.y + plot.height);
        ctx.stroke();
    }
}

/**
 * Horizontal gridlines at numeric Y ticks, clipped to `plot`.
 */
export function drawGridlinesY(
    ctx: Context2D,
    plot: PlotRect,
    ticks: number[],
    yToPixel: (v: number) => number,
): void {
    for (const tick of ticks) {
        const py = Math.round(yToPixel(tick)) + 0.5;
        if (py < plot.y || py > plot.y + plot.height) {
            continue;
        }

        ctx.beginPath();
        ctx.moveTo(plot.x, py);
        ctx.lineTo(plot.x + plot.width, py);
        ctx.stroke();
    }
}
