/**
 * Tables. A meeting table that is either a rectangle on trestles or a round one
 * on a pedestal, and the small round one that goes between two lounge chairs.
 *
 * A table has no front, so its rotation only matters for a rectangle. Its
 * footprint is still centred on its origin like everything else.
 */

import { defineAsset } from "../kit.ts";
import { MeshBin } from "../parts.ts";
import { slab } from "./common.ts";

type MeetingParams = {
  /** Length along local X. For `shape: "round"` this is the diameter. */
  length: number;
  /** Depth along local Z. Ignored when round. */
  width: number;
  height: number;
  shape: "rect" | "round";
  legs: "trestle" | "post";
};

const TOP = 0.04;

export const tableMeeting = defineAsset<MeetingParams>({
  id: "tera:table.meeting",
  label: "Meeting table",
  defaults: { length: 2.4, width: 1.2, height: 0.74, shape: "rect", legs: "trestle" },

  footprint(p) {
    const depth = p.shape === "round" ? p.length : p.width;
    // A metre of clearance is a chair pushed back plus somebody edging past it,
    // which is what the room around a meeting table has to actually allow for.
    return { width: p.length, depth, height: p.height, clearance: 1 };
  },

  build(p, ctx) {
    const P = ctx.parts;
    const bin = new MeshBin();
    const frame = ctx.materials.get("deskFrame");
    const top = ctx.materials.get("tableTop");
    const deckY = p.height - TOP;

    if (p.shape === "round") {
      // A round top takes its map as one sheet across the whole disc, which is
      // what a veneered table looks like anyway; `metricQuad` is a rectangle and
      // has nothing to say about a circle.
      bin.add(P.cylinder(28), top, { y: deckY, size: [p.length, TOP, p.length] });
      bin.add(P.cylinder(20), frame, { y: 0.02, size: [0.14, deckY - 0.02, 0.14] });
      bin.add(P.cylinder(24), frame, { size: [p.length * 0.42, 0.03, p.length * 0.42] });
      return bin.build("table.meeting");
    }

    // A 5 mm edge radius. A 2.4 m board is the longest specular highlight in the
    // room and it is worth having; the round top a few lines above already has a
    // circular edge for free, which is why only this branch asks for one.
    slab(bin, ctx, top, {
      y: deckY,
      width: p.length,
      depth: p.width,
      thickness: TOP,
      chamfer: 0.005,
    });

    if (p.legs === "post") {
      for (const sx of [-1, 1]) {
        for (const sz of [-1, 1]) {
          bin.add(P.rod(), frame, {
            x: sx * (p.length / 2 - 0.12),
            z: sz * (p.width / 2 - 0.12),
            size: [0.07, deckY, 0.07],
          });
        }
      }
      return bin.build("table.meeting");
    }

    const endX = Math.max(0.2, p.length / 2 - 0.36);
    for (const sx of [-1, 1]) {
      const x = sx * endX;
      bin.add(P.box(), frame, { x, size: [0.09, 0.05, p.width - 0.24] });
      bin.add(P.box(), frame, { x, y: 0.05, size: [0.1, deckY - 0.05, 0.1] });
      bin.add(P.box(), frame, { x, y: deckY - 0.09, size: [0.12, 0.09, p.width - 0.3] });
    }
    bin.add(P.box(), frame, { y: deckY - 0.24, size: [endX * 2, 0.09, 0.09] });

    return bin.build("table.meeting");
  },
});

type SideParams = {
  diameter: number;
  height: number;
};

/** The little round one. Coffee height by default. */
export const tableSide = defineAsset<SideParams>({
  id: "tera:table.side",
  label: "Side table",
  defaults: { diameter: 0.5, height: 0.45 },

  footprint(p) {
    return { width: p.diameter, depth: p.diameter, height: p.height, clearance: 0.2 };
  },

  build(p, ctx) {
    const P = ctx.parts;
    const bin = new MeshBin();
    const frame = ctx.materials.get("metalTrim");
    const deckY = p.height - 0.028;

    bin.add(P.cylinder(24), ctx.materials.get("tableTop"), {
      y: deckY,
      size: [p.diameter, 0.028, p.diameter],
    });
    bin.add(P.cylinder(12), frame, { y: 0.015, size: [0.06, deckY - 0.015, 0.06] });
    bin.add(P.cylinder(20), frame, { size: [p.diameter * 0.62, 0.015, p.diameter * 0.62] });

    return bin.build("table.side");
  },
});
