local system_constructor = require("love_src.wrapper.Concord.system")

local roadsegment = require("love_src.src.world.2_town_square.pseudo3d.roadsegment")

local perspective = require("love_src.src.world.2_town_square.component.perspective")
local pos3d = require("love_src.src.world.2_town_square.component.pos3d")

local projection = require("lib.choro.projection")
local ease = require("love_src.src.world.2_town_square.pseudo3d.ease")
local vm = require("lib.vornmath")
local roaddraw = require("love_src.src.world.2_town_square.pseudo3d.render.road")

local system = {}

system.__index = system

system.pool = {
  pool = {
    perspective.dict.camera_height,
    perspective.dict.draw_distance,
    perspective.dict.field_of_view,
    perspective.dict.fog_density,
    perspective.dict.lanes,
    perspective.dict.resolution,
    perspective.dict.road_width,
    perspective.dict.rumble_length,
    perspective.dict.segment_count,
    perspective.dict.segment_length,
    perspective.dict.segment_path,
    perspective.dict.segment_sprite_map_path,

    pos3d.dict.pos,
  }
}

system.components = {
  [perspective.dict.camera_height] = perspective.camera_height,
  [perspective.dict.draw_distance] = perspective.draw_distance,
  [perspective.dict.field_of_view] = perspective.field_of_view,
  [perspective.dict.fog_density] = perspective.fog_density,
  [perspective.dict.lanes] = perspective.lanes,
  [perspective.dict.resolution] = perspective.resolution,
  [perspective.dict.road_width] = perspective.road_width,
  [perspective.dict.rumble_length] = perspective.rumble_length,
  [perspective.dict.segment_count] = perspective.segment_count,
  [perspective.dict.segment_length] = perspective.segment_length,
  [perspective.dict.segment_path] = perspective.segment_path,
  [perspective.dict.segment_sprite_map_path] = perspective.segment_sprite_map_path,

  [pos3d.dict.pos] = pos3d.pos,
}
function system.new()
  local new_system = system_constructor.new("perspective", 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

function system:load()
  for _, e in ipairs(self.pool) do
    local segment_path = e[perspective.dict.segment_path].data
    local segment_sprite_map_path = e[perspective.dict.segment_sprite_map_path].data
    local segment_length = e[perspective.dict.segment_length].data
    local rumble_length = e[perspective.dict.rumble_length].data

    local player_y = e[pos3d.dict.pos].data

    local segments, track_length = roadsegment.resetRoad(segment_path, segment_sprite_map_path, segment_length, rumble_length, scroll)
    -- entityBuilder.setComponent(e, "segments", segments)
    -- entityBuilder.setComponent(e, "track_length", track_length)

    local field_of_view = e[perspective.dict.field_of_view].data
    local camera_height = e[perspective.dict.camera_height].data
    local camera_depth =  projection.distanceCamToProjection(field_of_view)
    local player_z = camera_depth * camera_height

    e[pos3d.dict.pos].data[3] = player_z

    e.segments = segments
    e.track_length = track_length

    e.camera_depth = camera_depth
  end
end

function system:update(dt)
  for _, e in ipairs(self.pool) do
    if love.keyboard.isDown("up") then
      e[pos3d.dict.pos].data[3] = e[pos3d.dict.pos].data[3] + 1000 * dt
    elseif love.keyboard.isDown("down") then
      e[pos3d.dict.pos].data[3] = e[pos3d.dict.pos].data[3] - 1000 * dt
    end
    if love.keyboard.isDown("left") then
      e[pos3d.dict.pos].data[1] = e[pos3d.dict.pos].data[1] - 1 * dt
    elseif love.keyboard.isDown("right") then
      e[pos3d.dict.pos].data[1] = e[pos3d.dict.pos].data[1] + 1 * dt
    end
    if love.keyboard.isDown("w") then
      e[pos3d.dict.pos].data[2] = e[pos3d.dict.pos].data[2] + 1000 * dt
    elseif love.keyboard.isDown("s") then
      e[pos3d.dict.pos].data[2] = e[pos3d.dict.pos].data[2] - 1000 * dt

    end
  end
end

local function drawRoad(
  zPosition, drawDistance,
  segments,
  segment_length, track_length,
  playerX, cameraHeight, cameraDepth,
  resolution, lanes,
  playerZ, fogDensity
)
  if (zPosition == 0) then
    zPosition = 1
  end
  local baseSegment = roadsegment.findSegment(zPosition, segments, segment_length);
  local basePercent = ease.percentRemaining(zPosition, segment_length);

  local playerSegment = roadsegment.findSegment(zPosition + playerZ, segments, segment_length);
  local playerPercent = ease.percentRemaining(zPosition+playerZ, segment_length);
  local playerY       = ease.interpolate(playerSegment.p1.world.y, playerSegment.p2.world.y, playerPercent);
  local maxy        = resolution[2];

  local x  = 0;
  local dx = - ((baseSegment.curve or 0) * basePercent);

  local n, segment;
  for n = 0, drawDistance do
    local index = (baseSegment.index + n) % #segments

    if (index >= #segments) then
      index = n + 1
    elseif (index <= 0) then
      index = #segments - (n + 1)
    end

    segment = segments[index];
    local loopPos = 0
    if (segment.index < baseSegment.index) then
      loopPos = track_length
    end

    local p1Width = segment.p1.world.w
    local p2Width = segment.p2.world.w

    local camera1 = vm.vec3({
      (playerX * p1Width) - x, --x
      playerY + cameraHeight, --y
      zPosition - loopPos --z
    })

    local camera2 = vm.vec3({
      (playerX * p2Width) - x - dx, --x
      playerY + cameraHeight, --y
      zPosition - loopPos --z
    })

    segment.fog    = ease.exponentialFog(n/drawDistance, fogDensity);
    segment.clip   = maxy

    local p1cam, p1screenpos, p1screensize = projection.projectWorldToCam({
      segment.p1.world.w,
      segment.p1.world.y,
      segment.p1.world.z
    }, camera1, cameraDepth, resolution, {
      p1Width,
      1
    });
    local p2cam, p2screenpos, p2screensize = projection.projectWorldToCam({
      segment.p2.world.w,
      segment.p2.world.y,
      segment.p2.world.z
    }, camera2, cameraDepth, resolution, {
      p2Width,
      1
    });

    x  = x + dx;
    dx = dx + (segment.curve or 0);

    if ((p1cam[3] <= cameraDepth) or -- behind us
        (p2screenpos[2] >= p1screenpos[2]) or -- back face cull
        (p2screenpos[2] >= maxy))          -- clip by (already rendered) segment
    then
    else
      -- segmentdebugdraw.draw(segment, resolution, maxy)
      roaddraw.draw(p1screenpos, p2screenpos, p1screensize, p2screensize, segment.texture, resolution, maxy)

      maxy = p1screenpos[2];
    end
  end

  for n = (drawDistance), 0, -1 do
    local index = (baseSegment.index + n) % #segments

    if (index >= #segments) then
      index = n + 1
    elseif (index <= 0) then
      index = #segments - (n + 1)
    end

    segment = segments[index];

    local p1Width = segment.p1.world.w

    -- for i = 1, #segment.sprites do
    --   drawstatic.drawstatic(
    --     segment, p1Width, resolution)
    -- end

  end
end

function system:draw()
  for _, e in ipairs(self.pool) do
    local player_y = e[pos3d.dict.pos].data[2] -- pos[2]
    local draw_distance = e[perspective.dict.draw_distance].data
    local camera_height = e[perspective.dict.camera_height].data
    local resolution = e[perspective.dict.resolution].data
    local lanes = e[perspective.dict.lanes].data
    local segment_length = e[perspective.dict.segment_length].data
    local fogDensity = e[perspective.dict.fog_density].data

    local segments = e.segments
    local player_x = e[pos3d.dict.pos].data[1] -- pos[1]
    local camera_depth = e.camera_depth
    local track_length = e.track_length
    local player_z = e[pos3d.dict.pos].data[3] -- pos[3]

    love.graphics.push()
    drawRoad(
      player_y, draw_distance,
      segments,
      segment_length, track_length,
      player_x, camera_height, camera_depth,
      resolution, lanes,
      player_z,
      fogDensity
    )
    love.graphics.pop()
    love.graphics.print(string.format("%s,%s,%s",player_x, player_y, player_z), 20, 20)
  end
end

return system
