/**
 * Devices — the signal rung of the ladder.
 *
 * One stupid-simple primitive: an entity can EMIT a signal, and any entity can
 * SENSE whether a signal is present within a radius (world.signalAt). No
 * channels, no payloads, no wiring diagrams — on/off in a radius, like
 * redstone before it grew features. Emergence comes from composition, not from
 * a rich protocol.
 *
 * Bundled devices built on it:
 *   lever — toggled by `use`; emits while on.
 *   plate — emits while something stands on it (inhabitant, creature, or cargo).
 *   door  — solid while closed; opens when it senses a signal (or by `use`).
 *   lamp  — lights when it senses a signal; lit lamps repel prowlers.
 *
 * Authored entities join the same fabric: set `self.signal = true` in a
 * behavior to emit, call `world.signalAt(self.x, self.z)` to sense. Fork any
 * device in-world to read exactly how it works — the example is the tutorial.
 */
'use strict';

const SENSE_RADIUS = 6; // how far a door/lamp feels a signal
const PRESSER_KINDS = ['wanderer', 'hopper', 'crate', 'ball', 'villager']; // villagers count now — they live here

class Lever {
  constructor(o) {
    this.id = o.id;
    this.kind = 'lever';
    this.x = o.x; this.y = o.y; this.z = o.z;
    this.rot = o.rot || 0;
    this.scale = o.scale != null ? o.scale : 1;
    this.on = !!o.on;
  }
  get signal() { return this.on; } // levers emit while on
  use() { this.on = !this.on; return this.on; }
  serialize() {
    return { id: this.id, kind: this.kind, pose: { x: this.x, y: this.y, z: this.z, rot: this.rot }, scale: this.scale, on: this.on };
  }
}

class Plate {
  constructor(o) {
    this.id = o.id;
    this.kind = 'plate';
    this.x = o.x; this.y = o.y; this.z = o.z;
    this.rot = o.rot || 0;
    this.scale = o.scale != null ? o.scale : 1;
    this.pressed = false;
  }
  get signal() { return this.pressed; }
  update(world) {
    this.pressed = false;
    const r2 = 1.4 * 1.4;
    for (const inh of world.inhabitants.values()) {
      const dx = inh.pose.x - this.x, dz = inh.pose.z - this.z;
      if (dx * dx + dz * dz < r2) { this.pressed = true; return; }
    }
    // Creatures and cargo press plates too — via the chunk-bucket index.
    this.pressed = !!world.nearestOfKinds(PRESSER_KINDS, this.x, this.z, 1.4);
  }
  serialize() {
    return { id: this.id, kind: this.kind, pose: { x: this.x, y: this.y, z: this.z, rot: this.rot }, scale: this.scale, pressed: this.pressed };
  }
}

class Door {
  constructor(o) {
    this.id = o.id;
    this.kind = 'door';
    this.x = o.x; this.y = o.y; this.z = o.z;
    this.rot = o.rot || 0;
    this.scale = o.scale != null ? o.scale : 1;
    this.open = !!o.open;
    this.latched = !!o.latched; // manually opened via `use` — stays open until used again
  }
  use() { this.latched = !this.latched; return this.latched; }
  update(world) {
    this.open = this.latched || world.signalAt(this.x, this.z, SENSE_RADIUS, this.id);
  }
  serialize() {
    return { id: this.id, kind: this.kind, pose: { x: this.x, y: this.y, z: this.z, rot: this.rot }, scale: this.scale, open: this.open, latched: this.latched };
  }
}

class Lamp {
  constructor(o) {
    this.id = o.id;
    this.kind = 'lamp';
    this.x = o.x; this.y = o.y; this.z = o.z;
    this.rot = o.rot || 0;
    this.scale = o.scale != null ? o.scale : 1;
    this.lit = !!o.lit;
  }
  update(world) {
    this.lit = world.signalAt(this.x, this.z, SENSE_RADIUS, this.id);
  }
  serialize() {
    return { id: this.id, kind: this.kind, pose: { x: this.x, y: this.y, z: this.z, rot: this.rot }, scale: this.scale, lit: this.lit };
  }
}

/**
 * Streetlight (WP6) — a town/city fixture, not a player device: no lever or
 * plate wires into it. It lights itself at dusk and stays lit through the
 * night, same shape as Lamp but driven by the shared clock instead of
 * world.signalAt — the D2 promise that civilization density buys safety by
 * construction, not by someone remembering to flip a switch.
 */
class Streetlight {
  constructor(o) {
    this.id = o.id;
    this.kind = 'streetlight';
    this.x = o.x; this.y = o.y; this.z = o.z;
    this.rot = o.rot || 0;
    this.scale = o.scale != null ? o.scale : 1;
    this.lit = !!o.lit;
  }
  update(world) {
    const phase = world.timeInfo().phase;
    this.lit = phase === 'night' || phase === 'dusk';
  }
  serialize() {
    return { id: this.id, kind: this.kind, pose: { x: this.x, y: this.y, z: this.z, rot: this.rot }, scale: this.scale, lit: this.lit };
  }
}

module.exports = { Lever, Plate, Door, Lamp, Streetlight, SENSE_RADIUS };
