#include "camera.h"
#include <SDL3/SDL_gamepad.h>
#include <iostream>


/*
  I bought a very cheap ps4 knockoff controller and for some reason
  AXIS_RIGHTX and AXIS_RIGHTY are swapped with the triggers?
  this should be defined as 0 for a working one
 */ 
#define WEIRD_JOYSTICK_BEHAVIOR 1

vec3 Camera::dir() const {
  return normalize(vec3(
    cos(radians(yaw)) * cos(radians(pitch)),
    sin(radians(pitch)),
    sin(radians(yaw)) * cos(radians(pitch))
  ));
}

void Camera::update() {
  if (look_up)    pitch += look_speed;
  if (look_down)  pitch -= look_speed;
  if (look_left)  yaw   -= look_speed;
  if (look_right) yaw   += look_speed;
  pitch = clamp(pitch, -89.0f, 89.0f);

  vec3 d     = dir();
  vec3 right = normalize(cross(d, up));
  target = pos + d;

  float shift_speed = shifted? 10.0 : 1.0;
  float speed = move_speed * shift_speed;

  if (move_forward)  { pos += d     * speed; target += d     * speed; }
  if (move_backward) { pos -= d     * speed; target -= d     * speed; }
  if (move_left)     { pos -= right * speed; target -= right * speed; }
  if (move_right)    { pos += right * speed; target += right * speed; }
  if (move_up)       { pos += up    * speed; target += up    * speed; }
  if (move_down)     { pos -= up    * speed; target -= up    * speed; }
}

void Camera::on_key(SDL_Scancode scancode, bool pressed) {
  switch (scancode) {
    case SDL_SCANCODE_W:     move_forward  = pressed; break;
    case SDL_SCANCODE_S:     move_backward = pressed; break;
    case SDL_SCANCODE_A:     move_left     = pressed; break;
    case SDL_SCANCODE_D:     move_right    = pressed; break;
    case SDL_SCANCODE_SPACE: move_up       = pressed; break;
    case SDL_SCANCODE_LCTRL: move_down     = pressed; break;
    case SDL_SCANCODE_UP:    look_up       = pressed; break;
    case SDL_SCANCODE_DOWN:  look_down     = pressed; break;
    case SDL_SCANCODE_LEFT:  look_left     = pressed; break;
    case SDL_SCANCODE_RIGHT: look_right    = pressed; break;
    case SDL_SCANCODE_LSHIFT:
    case SDL_SCANCODE_RSHIFT: 
      shifted = pressed;
    break;
    default: break;
  }
}


#ifndef WEIRD_JOYSTICK_BEHAVIOR
#define WEIRD_JOYSTICK_BEHAVIOR 0
#endif
static float normalize_stick(SDL_Gamepad* gamepad, SDL_GamepadAxis axis) {
  if(axis == SDL_GAMEPAD_AXIS_COUNT || axis == SDL_GAMEPAD_AXIS_INVALID)
    return 0.0f;


  float d = static_cast<float>(SDL_GetGamepadAxis(gamepad, axis));

  bool reported_trigger = (axis == SDL_GAMEPAD_AXIS_LEFT_TRIGGER || axis == SDL_GAMEPAD_AXIS_RIGHT_TRIGGER);
  bool really_trigger = (WEIRD_JOYSTICK_BEHAVIOR)? (axis == SDL_GAMEPAD_AXIS_RIGHTX || axis == SDL_GAMEPAD_AXIS_RIGHTY) : reported_trigger;

  float min = (reported_trigger)? 0 : SDL_JOYSTICK_AXIS_MIN;
  float max = SDL_JOYSTICK_AXIS_MAX;
  float n = (d - min) / (max - min);

  return really_trigger? n : ((n - 0.5) * 2.0);
}

void Camera::on_gamepad(SDL_Gamepad* gamepad) {
  if (!gamepad) return;

  float lx = normalize_stick(gamepad, SDL_GAMEPAD_AXIS_LEFTX);
  float ly = normalize_stick(gamepad, SDL_GAMEPAD_AXIS_LEFTY);
  float ry = normalize_stick(gamepad, (WEIRD_JOYSTICK_BEHAVIOR)? SDL_GAMEPAD_AXIS_RIGHT_TRIGGER : SDL_GAMEPAD_AXIS_RIGHTY);
  float rx = normalize_stick(gamepad, (WEIRD_JOYSTICK_BEHAVIOR)? SDL_GAMEPAD_AXIS_LEFT_TRIGGER : SDL_GAMEPAD_AXIS_RIGHTX);
  static uint8_t frame = 0;
  if(frame++ == 64) {
    std::cout << "lx: " << lx << " ly: " << ly << " rx: " << rx << " ry: " << ry << std::endl;
  }

  //float lt = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER)  / 32767.0f;
  //float rt = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER) / 32767.0f;

  const float dead = 0.1f;
  if (abs(lx) < dead) lx = 0.0f;
  if (abs(ly) < dead) ly = 0.0f;
  if (abs(rx) < dead) rx = 0.0f;
  if (abs(ry) < dead) ry = 0.0f;
  // if (lt < dead) lt = 0.0f;
  // if (rt < dead) rt = 0.0f;

  
  // look — right stick only
  yaw   += rx * look_speed;
  pitch -= ry * look_speed;
  pitch  = clamp(pitch, -89.0f, 89.0f);
  

  vec3 d     = dir();
  vec3 right = normalize(cross(d, up));
  float speed = move_speed * (shifted ? 4.0f : 1.0f);

  // move — left stick
  pos += d     * (-ly * speed);
  pos += right * ( lx * speed);

  // up/down — triggers
  //pos += up * (rt - lt) * speed;

  target = pos + d;

  shifted = SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER);
}

void Camera::on_mouse_motion(float dx, float dy) {
  yaw   += dx * look_speed * 0.1f;
  pitch -= dy * look_speed * 0.1f;
  pitch  = clamp(pitch, -89.0f, 89.0f);
}

CameraUBO Camera::ubo(float aspect) const {
  mat4 v = lookAt(pos, target, up);
  mat4 p = perspective(radians(fov), aspect, 0.01f, 1000.0f);
  return CameraUBO {
    .model     = mat4(1.0f),
    .view      = v,
    .proj      = p,
    .inv_view  = inverse(v),
    .inv_proj  = inverse(p),
    .time      = vec4((float)SDL_GetTicks() / 1000.0f, 0.0, 0.0, 0.0),
    .cam_pos   = vec4(pos, 1.0),
  };
}
