local system_constructor = require("love_src.wrapper.Concord.system")
local race = require("love_src.src.world.top_down_race.component.race")
local racing_phy = require("love_src.src.system.racing_phy")
local racing_force = require("love_src.src.system.racing_force")

local component = require("love_src.wrapper.Concord.component")

local vm = require("lib.vornmath")

local system = {}

system.__index = system

system.pool = {
  pool = {
    race.dict.max_speed,
    race.dict.min_speed,
    race.dict.velocity,
    race.dict.forward,
    race.dict.accel,
    race.dict.brake,
    race.dict.grip,
    race.dict.steer,
    race.dict.inertia,
    race.dict.mass,
    race.dict.streamline
  }
}

system.components = {
  [race.dict.max_speed] = race.max_speed,
  [race.dict.min_speed] = race.min_speed,
  [race.dict.velocity] = race.velocity,
  [race.dict.forward] = race.forward,

  [race.dict.accel] = race.accel,
  [race.dict.brake] = race.brake,
  [race.dict.grip] = race.grip,
  [race.dict.steer] = race.steer,
  [race.dict.inertia] = race.inertia,
  [race.dict.mass] = race.mass,
  [race.dict.streamline] = race.streamline
}

function system.new()
  local new_system = system_constructor.new("velocity", system.pool)
  if (new_system) then
    for k, v in pairs(system) do
		  new_system[k] = v
	  end
    return new_system
  else
    return nil
  end
end

local function get(e)
  return
    e[race.dict.max_speed].data,
    e[race.dict.min_speed].data,
    e[race.dict.velocity].data,
    e[race.dict.forward].data,
    e[race.dict.accel].data,
    e[race.dict.brake].data,
    e[race.dict.grip].data,
    e[race.dict.steer].data,
    e[race.dict.inertia].data,
    e[race.dict.mass].data,
    e[race.dict.streamline].data
end

function system:load()
  component.component("race.pos", function (c, x, y)
    c.data = vm.vec2(x, y)
  end)
  component.component("race.scale", function (c, x, y)
    c.data = vm.vec2(x, y)
  end)
  component.component("race.angle", function (c, x)
    c.data = x
  end)
  component.component("race.image", function (c, x)
    c.data = {
      image = love.graphics.newImage(x)
    }
    c.data.origin = vm.vec2(c.data.image:getWidth()/2, c.data.image:getHeight()/2)
  end)

  for _, e in ipairs(self.pool) do
    e:give("race.pos", 44.64, 10.87)
    e:give("race.scale", 1, 1)
    e:give("race.angle", 0)
    e:give("race.image", "love_src/asset/image/arrow_right.png")
  end
end

local lw, lh = love.graphics.getDimensions()
local frictions = {
  {
    name = "grass",
    colliders = {
      -- x, y, w, h
      0, 0, lw/2, lh
    },
    color = {0, 255/255, 0},
    friction = 2
  },
  {
    name = "pavement",
    colliders = {
      -- x, y, w, h
      lw/2, 0, lw/2, lh
    },
    color = {0, 255/255, 255/255},
    friction = 1
  }
}

local drags = {
  {
    drag = vm.vec2(1.0, 0.0)
  }
}

-- x1 y1        -- (x1 + w1) y1
--          x2 y2
-- x1 (y1 + h1) -- (x1 + w1) (y1 + h1)
local function is_inside(x1,y1,w1,h1, x2,y2)
  return x1 < x2 and
         x2 < x1+w1 and
         y1 < y2 and
         y2 < y1+h1
end

local function get_friction(pos_x, pos_y, floors)
  local friction = 0
  for _,f in ipairs(floors) do
    local fx, fy, fw, fh = f.colliders[1], f.colliders[2], f.colliders[3], f.colliders[4]
    if (is_inside(fx, fy, fw, fh, pos_x, pos_y)) then
      friction = f.friction
      break
    end
  end
  return friction
end

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,
  drag_movement = 0.0
}

local function handle_input(accel, brake, steer)
  local up = love.keyboard.isDown("up")
  local down = love.keyboard.isDown("down")
  local left = love.keyboard.isDown("left")
  local right = love.keyboard.isDown("right")

  local desire_forward = 0
  local desire_steer = 0
  if (up) then
    desire_forward = accel
  elseif (down) then
    desire_forward = - brake
  end
  if (left) then
    desire_steer = steer
  elseif (right) then
    desire_steer = - steer
  end
  return desire_forward, desire_steer
end

local debug_current = 1
local p_up = love.keyboard.isDown("i")
local p_down = love.keyboard.isDown("k")
local p_left = love.keyboard.isDown("j")
local p_right = love.keyboard.isDown("l")

dict_debugs = {
  race.dict.max_speed,
  race.dict.min_speed,
  race.dict.velocity,
  race.dict.forward,
  race.dict.accel,
  race.dict.brake,
  race.dict.grip,
  race.dict.steer,
  race.dict.inertia,
  race.dict.mass,
  race.dict.streamline,
}

-- local function

function system:update(dt)
  local i_up = love.keyboard.isDown("i")
  local i_down = love.keyboard.isDown("k")
  local i_left = love.keyboard.isDown("j")
  local i_right = love.keyboard.isDown("l")

  if (i_down and not p_down) then
    debug_current = debug_current + 1
  elseif (i_up and not p_up) then
    debug_current = debug_current - 1
  end

  local dict_debug_current = dict_debugs[1]
  if (debug_current > 0 and debug_current <= #dict_debugs) then
    dict_debug_current = dict_debugs[debug_current]
  end

  p_up = i_up
  p_down = i_down
  p_left = i_left
  p_right = i_right

  for _, e in ipairs(self.pool) do
    if (i_left) then
      e[dict_debug_current].data = e[dict_debug_current].data - 1
    elseif (i_right) then
      e[dict_debug_current].data = e[dict_debug_current].data + 1
    end

    local max_speed, min_speed, velocity, forward, accel, brake, grip, steer, inertia, mass, streamline = get(e)

    local pos_x, pos_y = e["race.pos"].data[1], e["race.pos"].data[2]

    local friction = frictions[1].friction
    local drag = drags[1].drag

    local desire_forward, desire_steer = handle_input(accel, brake, steer)

    local force = racing_force(
      desire_forward, desire_steer,
      grip, friction,
      drag, magic_w.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
    )

    if (mass == 0) then
      mass = 1
    end
    local a = force * dt / mass

    e[race.dict.velocity].data = e[race.dict.velocity].data + a
    e[race.dict.velocity].data[1] = vm.clamp(e[race.dict.velocity].data[1], min_speed, max_speed)
    if (e[race.dict.velocity].data[1] ~= 0) then
      e["race.pos"].data = e["race.pos"].data + e[race.dict.velocity].data
    end

    e[race.dict.inertia].data = force
  end
end

local function draw_debug(x, y, r, g, b, texts)
  local font_ix = test.draw_font_start()
  test.draw_font_set_base_color(r, g, b)
  for _, t in ipairs(texts) do
    y = y + test.draw_font(font_ix, t, x, y)
  end
end

function system:draw()
  for _, e in ipairs(self.pool) do
    local x, y = e["race.pos"].data[1], e["race.pos"].data[2]
    local vx = e[race.dict.velocity].data[1]
    local vy = e[race.dict.velocity].data[2]
    test.set_sphere(x, y, vx, vy)
  end
end

function system:ui_draw()
  for _, e in ipairs(self.pool) do
    local max_speed, min_speed, velocity, forward, accel, brake, grip, steer, inertia, mass, streamline = get(e)
    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, velocity[1], velocity[2]) -- x1, y1, x2, y2

    draw_debug(640, 0, 1.0, 0.0, 1.0, {
      string.format("%s max_speed : %s", (debug_current == 1 and ">") or "", max_speed),
      string.format("%s max_speed : %s", (debug_current == 21 and ">") or "", min_speed),
      string.format("%s velocity : %s", (debug_current == 2 and ">") or "", velocity),
      string.format("%s forward : %s", (debug_current == 22 and ">") or "", forward),
      string.format("%s accel : %s", (debug_current == 3 and ">") or "", accel),
      string.format("%s brake : %s", (debug_current == 4 and ">") or "", brake),
      string.format("%s grip : %s", (debug_current == 5 and ">") or "", grip),
      string.format("%s steer : %s", (debug_current == 6 and ">") or "", steer),
      string.format("%s inertia : %s", (debug_current == 7 and ">") or "", inertia),
      string.format("%s mass : %s", (debug_current == 8 and ">") or "", mass),
      string.format("%s streamline : %s", (debug_current == 9 and ">") or "", streamline)
    })

    draw_debug(640, 300, 1.0, 0.0, 1.0, {
      string.format("%s weight tire : %s", (debug_current == 10 and ">") or "", magic_w.tire),
      string.format("%s weight floor : %s", (debug_current == 11 and ">") or "", magic_w.floor),
      string.format("%s weight forward : %s", (debug_current == 12 and ">") or "", magic_w.forward),
      string.format("%s weight steer : %s", (debug_current == 13 and ">") or "", magic_w.steer),
      string.format("%s weight drag boost : %s", (debug_current == 14 and ">") or "", magic_w.drag_boost),
      string.format("%s weight drag halt : %s", (debug_current == 15 and ">") or "", magic_w.drag_halt),
      string.format("%s weight inertia : %s", (debug_current == 16 and ">") or "", magic_w.inertia),
      string.format("%s weight centrifugal : %s", (debug_current == 17 and ">") or "", magic_w.centrifugal),
      string.format("%s magic drag movement : %s", (debug_current == 18 and ">") or "", magic_w.drag_movement)
    })

    draw_debug(640,600, 1.0, 0.0, 1.0, {
      string.format("%s friction : %s", (debug_current == 19 and ">") or "", frictions[1].friction),
      string.format("%s drag : %s", (debug_current == 20 and ">") or "", drags[1].drag),
    })
  end
end

return system
