/**
 * Chairs. Two of them: the one at a desk and the one you wait in.
 *
 * Both face −Z, which puts the backrest at +Z — a chair at yaw zero has its
 * occupant looking the same way a desk at yaw zero does, and that is the whole
 * reason `DeskBank` can hand one rotation to both.
 */

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

type TaskChairParams = {
  width: number;
  /** Height of the seat pan, metres. */
  seatHeight: number;
  backHeight: number;
  arms: boolean;
};

/**
 * A five-star task chair. The fabric is the tintable part, which is why a
 * `colorKey` on a chair reads as upholstery and not as a coloured base.
 */
export const seatTaskChair = defineAsset<TaskChairParams>({
  id: "tera:seat.task-chair",
  label: "Task chair",
  defaults: { width: 0.5, seatHeight: 0.46, backHeight: 0.56, arms: true },

  footprint(p) {
    // The star base is the widest part of a task chair and it is wider than the
    // seat. 0.64 m is a 320 mm arm, which is a real chair.
    const span = Math.max(0.64, p.width + 0.14);
    return { width: span, depth: span, height: p.seatHeight + p.backHeight, clearance: 0.3 };
  },

  build(p, ctx) {
    const P = ctx.parts;
    const bin = new MeshBin();
    const base = ctx.materials.get("chairBase");
    const shell = ctx.materials.get("chairShell");
    const fabric = tintable(ctx, "chairFabric");

    const reach = 0.28;
    for (let i = 0; i < 5; i++) {
      const yaw = (i * Math.PI * 2) / 5;
      const sin = Math.sin(yaw);
      const cos = Math.cos(yaw);
      bin.add(P.box(), base, {
        x: sin * reach * 0.5,
        y: 0.035,
        z: cos * reach * 0.5,
        size: [0.05, 0.03, reach],
        yaw,
      });
      bin.add(P.cylinder(8), base, {
        x: sin * reach,
        z: cos * reach,
        size: [0.055, 0.048, 0.055],
      });
    }

    const panY = p.seatHeight - 0.08;
    bin.add(P.rod(), base, { y: 0.05, size: [0.06, panY - 0.05, 0.06] });
    // The parts sharing a material must all be indexed or all not be, or the
    // merge fails and drops the material entirely (see `common.ts`). Extruded
    // `roundedBox` carries no index, so the shell keeps to plain boxes and the
    // soft parts — arm pads included — go in with the fabric.
    bin.add(P.box(), shell, { y: panY - 0.04, size: [p.width * 0.6, 0.05, 0.24] });
    bin.add(P.roundedBox(0.07), fabric, { y: panY, size: [p.width, 0.08, 0.48] });

    // The backrest leans back by rotating about its own base, so the lumbar
    // stays where the spine is and only the shoulders move.
    const lean = 0.13;
    bin.add(P.box(), shell, { y: panY, z: 0.2, size: [0.08, 0.14, 0.14] });
    bin.add(P.roundedBox(0.06), fabric, {
      y: p.seatHeight + 0.04,
      z: 0.21,
      size: [p.width - 0.05, p.backHeight - 0.04, 0.06],
      pitch: lean,
    });

    if (p.arms) {
      for (const sx of [-1, 1]) {
        const x = sx * (p.width / 2 + 0.02);
        bin.add(P.box(), shell, { x, y: panY, size: [0.03, 0.19, 0.03] });
        bin.add(P.roundedBox(0.08), fabric, {
          x,
          y: panY + 0.19,
          size: [0.06, 0.025, 0.24],
        });
      }
    }

    return bin.build("seat.task-chair");
  },
});

type LoungeParams = {
  width: number;
  depth: number;
  seatHeight: number;
  backHeight: number;
  arms: boolean;
};

/**
 * A low armchair for a breakout or a reception. `width: 1.6, arms: true` is a
 * two-seat sofa and looks like one, which is why there is no separate sofa
 * asset — the difference between the two is one number.
 *
 * The upholstery is the tintable part.
 */
export const seatLounge = defineAsset<LoungeParams>({
  id: "tera:seat.lounge",
  label: "Lounge chair",
  defaults: { width: 0.84, depth: 0.82, seatHeight: 0.4, backHeight: 0.76, arms: true },

  footprint(p) {
    return { width: p.width, depth: p.depth, height: p.backHeight, clearance: 0.5 };
  },

  build(p, ctx) {
    const P = ctx.parts;
    const bin = new MeshBin();
    const base = ctx.materials.get("chairBase");
    const shell = ctx.materials.get("chairShell");
    const cushion = tintable(ctx, "upholstery");

    const foot = 0.08;
    for (const sx of [-1, 1]) {
      for (const sz of [-1, 1]) {
        bin.add(P.rod(), base, {
          x: sx * (p.width / 2 - 0.1),
          z: sz * (p.depth / 2 - 0.1),
          size: [0.045, foot, 0.045],
        });
      }
    }

    const cushionH = 0.16;
    const seatY = p.seatHeight - cushionH;
    bin.add(P.box(), shell, {
      y: foot,
      size: [p.width - 0.12, seatY - foot, p.depth - 0.14],
    });
    bin.add(P.roundedBox(0.07), cushion, {
      y: seatY,
      z: 0.02,
      size: [p.width - (p.arms ? 0.3 : 0.08), cushionH, p.depth - 0.2],
    });

    // The back cushion leans, and it is the lean that stops a lounge chair from
    // reading as a cardboard box with a pillow on it.
    bin.add(P.roundedBox(0.07), cushion, {
      y: seatY + 0.02,
      z: p.depth / 2 - 0.12,
      size: [p.width - (p.arms ? 0.3 : 0.08), p.backHeight - seatY - 0.02, 0.16],
      pitch: 0.12,
    });

    if (p.arms) {
      for (const sx of [-1, 1]) {
        bin.add(P.roundedBox(0.09), cushion, {
          x: sx * (p.width / 2 - 0.07),
          y: foot,
          size: [0.14, p.seatHeight + 0.18 - foot, p.depth - 0.1],
        });
      }
    }

    return bin.build("seat.lounge");
  },
});
