local vm = require"lib.vornmath"
local racing_force = require("love_src.src.system.racing_force")

local main_wrapper = require "love_src.wrapper.lappy.world"
---@class wrappers.Concord.world : lappy.world
local world = main_wrapper:extend()

local reap = require("lib.reap")
local BASE = reap.base_path(...)

function world:new()
  world.super.new(self, BASE, "racer")
end

local default = {
  pos = vm.vec2(44.64, 10.87),

  orientation = vm.vec2(0.0, 0.0),
  orientation_speed = vm.vec2(0.0, 0.0),
  min_orientation_speed = -1.0,
  max_orientation_speed = 1.0,

  velocity = vm.vec2(0.0, 0.0),
  max_speed = 1.0,
  min_speed = -1.0,
  inertia = vm.vec2(0.0, 0.0),

  accel = 0.1,
  brake = 0.1,
  grip = 0.0,
  steer = 0.1,
  mass = 0.0,
  streamline = 0.0
}

local magic_w = {
  tire = 0.0,
  floor = 0.0,
  forward = 0.01,
  steer = 0.1,
  drag_boost = 0.0,
  drag_halt = 0.0,
  inertia = 0.0,
  centrifugal = 0.0,
}

local debug_index = 1


function world:load(entity, data)
  self.e = entity
  self.data = data or default
  self.map = {}
end

function get(s)
  return
    s.data.max_speed,
    s.data.min_speed,
    s.data.velocity,
    s.data.accel,
    s.data.brake,
    s.data.grip,
    s.data.steer,
    s.data.inertia,
    s.data.mass,
    s.data.streamline
end

local function rotate_vector(x0, y0, theta)
  local x1 = x0 * math.cos(theta) - y0 * math.sin(theta)
  local y1 = x0 * math.sin(theta) + y0 * math.cos(theta)
  return vm.vec2(x1, y1)
end

local function forward_velocity(dt, force, mass, velocity, max_speed, min_speed)
  if (mass == 0) then
    mass = 1
  end
  local a = force[1] * dt / mass
  local new_velocity = velocity[1] + a
  new_velocity = vm.clamp(new_velocity, min_speed, max_speed)

  return new_velocity
end

local function orientation_velocity(dt, force, mass, orientation_speed, orientation, max_orientation, min_orientation)
  if (mass == 0) then
    mass = 1
  end
  local a = force[2] * dt / mass
  local new_speed = orientation_speed + a
  new_speed = vm.clamp(new_speed, min_orientation, max_orientation)
  local new_orientation = rotate_vector(orientation[1], orientation[2], new_speed)
  return  new_orientation, new_speed
end

function world:update_race(dt)
  local x, y = self.data.pos[1], self.data.pos[2]
  local or_x, or_y = self.data.orientation[1], self.data.orientation[2]

  local max_speed, min_speed, velocity, accel, brake, grip, steer, inertia, mass, streamline = get(self)
  local friction, drag, drag_movement = self.map.data.friction, self.map.data.drag, self.map.data.drag_movement

  local desire_forward, desire_steer = self.e.components.actor.data.forward, self.e.components.actor.data.steer

  local force = racing_force(
    desire_forward, desire_steer,
    grip, friction,
    drag, drag_movement, streamline,
    inertia,
    mass,
    magic_w.forward, magic_w.steer,
    magic_w.tire, magic_w.floor,
    magic_w.drag_boost, magic_w.drag_halt,
    magic_w.inertia, magic_w.centrifugal
  )
  local f_v = forward_velocity(dt, force, mass, velocity, max_speed, min_speed)
  local o, o_v = orientation_velocity(dt, force, mass, self.data.orientation_speed, self.data.orientation, self.data.max_orientation_speed, self.data.min_orientation_speed)

  self.data.velocity[1] = f_v
  self.data.orientation_speed = o_v
  self.data.orientation = o
  -- if (self.data.velocity[1] ~= 0) then
  self.data.pos = self.data.pos + o * f_v
  -- end

  self.data.inertia = force
end

function world:update(dt)
  self:update_race(dt)
end

function world:draw()
  local x, y = self.data.pos[1], self.data.pos[2]
  local or_x, or_y = self.data.orientation[1], self.data.orientation[2]
  local vx, vy = self.data.orientation[1] * self.data.velocity[1], self.data.orientation[2] * self.data.velocity[1]

  test.set_sphere(x, y, vx, vy)
end

local function debug_data(x, y, r, g, b, to_debug)
  local font_ix = test.draw_font_start()
  test.draw_font_set_base_color(r, g, b)
  for k, v in pairs(to_debug) do
    y = y + test.draw_font(font_ix, string.format("%s : %s", k, v), x, y)
  end
end

local function debug_vector(vx, vy)
  local lw, lh = love.graphics.getDimensions()

  test.draw_line_quad_start()
  test.draw_set_color(1.0, 0.0, 0.0) -- r, g, b (0.0 to 1.0)
  test.draw_line(lw/2, lh/2, lw/vx, lh/vy) -- x1, y1, x2, y2
end

function world:ui_draw()
  debug_data(650, 0, 1.0, 0.0, 1.0, self.data)
  debug_data(650, 400, 1.0, 0.0, 1.0, magic_w)

  local vx, vy = self.data.orientation[1], self.data.orientation[2]
  debug_vector(vx, vy)
end

return world
