#include "sim.h"
#include "map.h"
#include "tanks_path.h"
#include "tanks_turn.h"
#include "tanks_move.h"

/* place a tank centred in world cell (wcx,wcy), facing discrete direction di */
static void place(World* w, uint32_t t, uint32_t wcx, uint32_t wcy, uint32_t di) {
  w->tank_xy[t]  = xy_pack((int32_t)wcx * SUB + SUB / 2, (int32_t)wcy * SUB + SUB / 2);
  w->tank_ang[t] = (uint16_t)(di << ANGLE_SHIFT);
  w->tank_in[t] = 0; w->tank_vxy[t] = 0; w->tank_hit[t] = 0;
}

void sim_init(World* w) {
  for (uint32_t i = 0; i < N_SCREENS * GRID_H; i++) w->grid[i] = GRID_INIT[i];

  /* all tanks are identical; start them on the always-open ring of screen 0 so
   * they are visible immediately, each UNSELECTED with no destination */
  place(w, 0,  1,  7, 0);
  place(w, 1, 18,  7, 0);
  place(w, 2, 10,  1, 8);
  place(w, 3, 10, 13, 24);
  for (uint32_t t = 0; t < N_TANKS; t++) {
    w->tstate[t] = TS_UNSELECTED; w->phas[t] = 0; w->pgoal[t] = DIR_NONE; w->pstatus[t] = PS_IDLE;
    w->pdest_screen[t] = 0; w->pdest_cell[t] = 0;
  }
  w->selected = SEL_NONE;

  w->frame = 0;
  w->move_speed = 16;         /* subcells per tick (16 ticks per cell) */
  w->turn_rate  = 1024;       /* angle units/tick: ~16 ticks per 90 degrees */
  w->collide_scale = 128;     /* Q0.8: 50% while colliding */

  /* Build the rarely-changing path tables once: Level 1, then Level 2 on top, then
   * each tank's remaining-distance vector (empty until a destination is set). */
  w->l1_maxdist = (uint16_t)l1_build_all(w->l1dist, w->grid, w->l1_screen_max);
  l2_build(w);
  for (uint32_t t = 0; t < N_TANKS; t++) l2_compute_pg(w, t);
}

void sim_tick(World* w) {
  tanks_path(w);                                    /* path-follow input for non-manual tanks */
  tanks_turn(w->tank_xy, w->tank_ang, w->tank_in, w->tank_hit,
             N_TANKS, w->turn_rate, w->grid);       /* turn + auto-steer */
  tanks_move(w->tank_xy, w->tank_vxy, w->tank_hit, w->tank_ang, w->tank_in,
             N_TANKS, (int32_t)w->move_speed, (int32_t)w->collide_scale, w->grid);
  w->frame++;
}

/* ---- mutators: rebuild only the tables a change can affect ---------------- */

void sim_toggle_wall(World* w, uint32_t wcx, uint32_t wcy) {
  wcx = (uint32_t)wrap_wcx((int32_t)wcx); wcy = (uint32_t)wrap_wcy((int32_t)wcy);
  uint32_t screen = (wcy / GRID_H) * SCREENS_X + (wcx / GRID_W);
  uint32_t cx = wcx % GRID_W, cy = wcy % GRID_H;
  w->grid[screen * GRID_H + cy] ^= (1u << cx);

  w->l1_screen_max[screen] = (uint16_t)l1_build_screen(w->l1dist + screen * TRI, w->grid, screen);
  w->l1_maxdist = 0;
  for (uint32_t s = 0; s < N_SCREENS; s++)
    if (w->l1_screen_max[s] > w->l1_maxdist) w->l1_maxdist = w->l1_screen_max[s];
  l2_build(w);
  for (uint32_t t = 0; t < N_TANKS; t++) l2_compute_pg(w, t);
}

void sim_set_dest(World* w, uint32_t tank, uint32_t wcx, uint32_t wcy) {
  if (tank >= N_TANKS) return;
  wcx = (uint32_t)wrap_wcx((int32_t)wcx); wcy = (uint32_t)wrap_wcy((int32_t)wcy);
  w->pdest_screen[tank] = (uint8_t)((wcy / GRID_H) * SCREENS_X + (wcx / GRID_W));
  w->pdest_cell[tank]   = (uint16_t)((wcy % GRID_H) * GRID_W + (wcx % GRID_W));
  w->phas[tank] = 1;
  l2_compute_pg(w, tank);                           /* fold the matrix for this goal (rare) */
}

void sim_cycle_tank(World* w, uint32_t tank) {
  if (tank >= N_TANKS) return;
  if (w->selected != tank) {                        /* select it (into AUTOPATH); drop the old one */
    if (w->selected != SEL_NONE) w->tstate[w->selected] = TS_UNSELECTED;
    w->selected = (uint8_t)tank;
    w->tstate[tank] = TS_AUTOPATH;
  } else if (w->tstate[tank] == TS_AUTOPATH) {       /* taking manual control abandons the auto-path, */
    w->tstate[tank] = TS_MANUAL;                      /* so deselecting from MANUAL leaves it where it is */
    w->phas[tank] = 0; w->pstatus[tank] = PS_IDLE; w->pgoal[tank] = DIR_NONE;
  } else {                                           /* MANUAL -> deselect (no destination to resume) */
    w->tstate[tank] = TS_UNSELECTED;
    w->selected = SEL_NONE;
  }
}
