// Pure geometry of the on-screen board: where the centered 8×8 grid sits inside
// a frame, and the two-way mapping between cells and screen points. Owns no
// rendering and pulls in NO GL/stb imports, so the touch→cell mapping is
// unit-testable headless. BoardView composes this for layout + hit-testing, and
// P5's swap input reuses `point_to_cell` to resolve a tap to a swap endpoint.
#import "modules/std.sx";
#import "modules/math";
#import "modules/ui/types.sx";
#import "board.sx";

BoardLayout :: struct {
    cell_size: f32;
    origin: Point;

    // Center a square 8×8 grid inside the safe-area-inset region of `frame`.
    compute :: (self: *BoardLayout, frame: Frame, safe: EdgeInsets) {
        avail := frame.inset(safe);
        cols : f32 = xx BOARD_COLS;
        board_dim := min(avail.size.width, avail.size.height);
        self.cell_size = board_dim / cols;
        total := self.cell_size * cols;
        self.origin = Point.{
            x = avail.origin.x + (avail.size.width - total) * 0.5,
            y = avail.origin.y + (avail.size.height - total) * 0.5
        };
    }

    cell_frame :: (self: *BoardLayout, col: s64, row: s64) -> Frame {
        Frame.make(
            self.origin.x + xx col * self.cell_size,
            self.origin.y + xx row * self.cell_size,
            self.cell_size,
            self.cell_size
        )
    }

    // Inverse of `cell_frame`: map a view-local point to the grid cell under it,
    // or null when the point falls outside the 8×8 grid. The `< 0.0` guards run
    // BEFORE the truncating cast, since casting a small negative float rounds
    // toward zero into a valid index. Uses the SAME origin / cell_size `compute`
    // produced, so a tap resolves to exactly the cell drawn under the finger.
    point_to_cell :: (self: *BoardLayout, p: Point) -> ?Cell {
        if self.cell_size <= 0.0 { return null; }
        fx := (p.x - self.origin.x) / self.cell_size;
        fy := (p.y - self.origin.y) / self.cell_size;
        if fx < 0.0 or fy < 0.0 { return null; }
        col : s64 = xx fx;
        row : s64 = xx fy;
        if col >= BOARD_COLS or row >= BOARD_ROWS { return null; }
        Cell.{ col = col, row = row }
    }

    // Frame of the whole 8×8 grid (origin + cols/rows × cell_size). The banner
    // and its dimming overlay are sized off this so they cover exactly the board.
    grid_frame :: (self: *BoardLayout) -> Frame {
        Frame.make(
            self.origin.x, self.origin.y,
            self.cell_size * cast(f32) BOARD_COLS,
            self.cell_size * cast(f32) BOARD_ROWS
        )
    }

    // Win/lose banner geometry (P7.2): an overlay panel centered over the board
    // grid, with the title band and the restart button inside it. Derived purely
    // from the SAME grid layout the gems use, so the restart hit-test in
    // BoardView.handle_event lands on exactly the button BoardView draws. The
    // headless banner_layout test locks the button-rect ↔ hit-test round-trip.
    banner :: (self: *BoardLayout) -> BannerLayout {
        grid := self.grid_frame();
        cx := grid.mid_x();
        cy := grid.mid_y();

        panel_w := grid.size.width * 0.84;
        panel_h := grid.size.height * 0.44;
        panel := Frame.make(cx - panel_w * 0.5, cy - panel_h * 0.5, panel_w, panel_h);

        title := Frame.make(panel.origin.x, panel.origin.y + panel_h * 0.18, panel_w, panel_h * 0.30);

        btn_w := panel_w * 0.60;
        btn_h := panel_h * 0.24;
        btn_y := panel.origin.y + panel_h - btn_h - panel_h * 0.16;
        button := Frame.make(cx - btn_w * 0.5, btn_y, btn_w, btn_h);

        BannerLayout.{ panel = panel, title = title, button = button }
    }
}

// Resolved rectangles of the win/lose banner: the centered `panel`, the `title`
// band where the win/lose headline is centered, and the restart `button` rect
// (also the hit-test target). All in the same view-local space as BoardLayout.
BannerLayout :: struct {
    panel: Frame;
    title: Frame;
    button: Frame;
}
