// src/game/state.cpp
#include "state.h"
#include "../render/player.h"
#include "../timing/speed_controller.h"
#include  "../assets/character_run_frames.h"
#include  "../assets/character_walk_frames.h"
#include  "../assets/character_crawl_frames.h"
#include <cmath>

namespace {
// Vygenerovane AI, proste to nahodne vymiena obrazky - assests
// Frame selector with two regimes:
// 1) Normal (speed <= 6): ping‑pong with holds at both ends
// 2) High‑speed (speed >= 7): 0, random(1..last-1), last, random(1..last-1)...
static inline int compute_frame_index(int frame_count,
                                      float player_speed,
                                      int tick_counter) {
  if (frame_count <= 1) return 0;
  if (player_speed <= 0.0f) return 0;

  const int last = frame_count - 1;

  // High-speed pattern: 0, rand(1..last-1), last, rand(1..last-1), ...
  if (player_speed >= 7.0f) {
    // Special-case 2-frame animations
    if (frame_count <= 2) {
      return (tick_counter & 1) ? 1 : 0;
    }

    // Even ticks → extremes (0 or last), alternating each time.
    // Odd ticks → pseudo-random mid in [1..last-1].
    if ((tick_counter & 1) == 0) {
      // Alternate 0 and last on even ticks
      bool choose_zero = ((tick_counter >> 1) & 1) == 0;
      return choose_zero ? 0 : last;
    } else {
      // Simple deterministic PRNG from tick_counter and speed
      unsigned int seed =
          static_cast<unsigned int>(tick_counter) * 1664525u + 1013904223u;
      seed ^= static_cast<unsigned int>(frame_count) * 2654435761u;
      seed ^= static_cast<unsigned int>(player_speed * 997.0f);
      int range = last - 1; // size of 1..last-1
      if (range <= 0) return 0; // safety
      int mid = 1 + static_cast<int>(seed % static_cast<unsigned int>(range));
      return mid;
    }
  }

  // Normal-speed regime: discrete ping‑pong with holds at both ends.
  // - speed 1 => 0,1,2,...,last, last, ..., 2,1, 0,0 ...
  // - speed 2 => advances 2 steps per tick (skipping every second timeline entry)
  // - holds at ends make 0 and last appear more frequently
  const int hold = 2; // how many extra steps to hold at each end

  // One full ping‑pong cycle length:
  // [hold at 0] + [ascend 1..last] + [hold at last] + [descend last-1..0]
  const int ascend_len = last;   // produces 1..last (length = last)
  const int descend_len = last;  // produces last-1..0 (length = last)
  const int cycle_len = hold + ascend_len + hold + descend_len;

  // Advance by an integer number of steps per tick based on speed
  int step = static_cast<int>(floorf(player_speed + 0.5f));
  if (step < 1) step = 1;

  // Use 64-bit for safety, then fold into cycle length
  const long long pos64 =
      (static_cast<long long>(tick_counter) *
       static_cast<long long>(step)) %
      static_cast<long long>(cycle_len);
  int pos = static_cast<int>(pos64);

  // Map timeline position to frame index
  if (pos < hold) {
    return 0; // hold at 0
  }
  pos -= hold;

  if (pos < ascend_len) {
    // ascend: 1..last
    return (pos + 1);
  }
  pos -= ascend_len;

  if (pos < hold) {
    return last; // hold at last
  }
  pos -= hold;

  // descend: last-1 .. 0
  return last - 1 - pos;
}
} // namespace

void MovementState::update() {
  // stop crawling after duration
  if ((current_state == PlayerState::Crawl1 ||
      current_state == PlayerState::Crawl2) &&
      state_timer.elapsed_time() >= CRAWL_DURATION) {
    current_state = previous_state;
    state_timer.stop();
  }
}

void MovementState::start_crawl(PlayerState crawl_type) {
  if (current_state != PlayerState::Crawl1 && current_state != PlayerState::Crawl2) {
	  previous_state = current_state;
  }

  current_state = crawl_type;
  state_timer.stop();
  state_timer.reset();
  state_timer.start();
}

void MovementState::set_state(PlayerState s) {
  if (current_state != s) {
    walk_index = run_index = crawl1_index = crawl2_index = 0;
    phase = 0.0f;
  }
  current_state = s;
}

void MovementState::set_motion_state_for_speed(int player_speed, int ground_speed) {
    int relative = player_speed - ground_speed;
    PlayerState new_state = (relative > 1) ? PlayerState::Run : PlayerState::Walk;
    if (new_state != current_state) {
        run_index = 0;
        walk_index = 0;
    }
    current_state = new_state;
}

// TODO THIS NEEDS REDESIGN FOR WALK/RUN
void MovementState::toggle_walk_frame(float player_speed, int tick_counter) {
  switch (current_state) {
  case PlayerState::Run:
    run_index = compute_frame_index(CHARACTER_RUN_FRAME_COUNT, player_speed, tick_counter);
    break;

  case PlayerState::Walk:
    walk_index = compute_frame_index(CHARACTER_WALK_FRAME_COUNT, player_speed, tick_counter);
    break;

  case PlayerState::Crawl1:
    crawl1_index = compute_frame_index(CHARACTER_CRAWL1_FRAME_COUNT, player_speed, tick_counter);
    break;

  case PlayerState::Crawl2:
    crawl2_index = compute_frame_index(CHARACTER_CRAWL2_FRAME_COUNT, player_speed, tick_counter);
    break;
  }
}

FrameSelection MovementState::get_frame_selection() const {
    FrameSelection f{};
    switch (current_state) {
    case PlayerState::Walk:
        f.movement = MovementType::Walk;
        f.frame_index = walk_index;
        break;
    case PlayerState::Run:
        f.movement = MovementType::Run;
        f.frame_index = run_index;
        break;
    case PlayerState::Crawl1:
        f.movement = MovementType::Crawl1;
        f.frame_index = crawl1_index;
        break;
    case PlayerState::Crawl2:
        f.movement = MovementType::Crawl2;
        f.frame_index = crawl2_index;
        break;
    default:
        f.movement = MovementType::Walk;
        f.frame_index = walk_index;
        break;
    }
    return f;
}
