local World = Object:extend()

local maputils  = require "scenes.battlesystem.utils"
local Map       = require "scenes.battlesystem.map"
local Cursor    = require "scenes.battlesystem.cursor"

local TweenManager = require "game.modules.tweenmanager"

local POSITIONS = {
  {x = 3, y = 4},
  {x = 2, y = 2},
  {x = 2, y = 6},
}

local gui         = require "game.modules.gui"

-- INIT FUNCTIONS
-- Initialize the battle world

function World:new(scene, battlefile)
  self.scene    = scene
  self.assets   = scene.assets

  self.obj      = require "scenes.battlesystem.actors"

  self.actors   = {}
  self.globalID = 0

  self.turns      = {}
  self.turns.current        = 1
  self.turns.number         = 0
  self.turns.isFinished     = true
  self.turns.changeBattler  = true
  self.battlers   = {}
  self.actionlist = {}

  self.BattlerCursor = self.turns.current

  self.heroNumber = 0
  self.ennNumber  = 0

  self.playerHUDPosition = -64

  self.map    = Map(self, "city")
  self.cursor = Cursor(self)

  self.tweens = TweenManager(self)

  self:resetActiveGrid()
  self:resetEffectGrid()

  self:initHeroes()
  self:initEnnemies()
  self:initHUD()

  self.isBattleActive = false
end

function World:initHeroes(battlefile)
  for i, hero in ipairs(game.characters.team) do
    self:addHero(POSITIONS[i].x, POSITIONS[i].y, hero, i)
  end
end

function World:initEnnemies(battlefile)
  self:addEnnemy(10, 3, "motobug")
  self:addEnnemy(10, 5, "motobug")
end

function World:registerActor(actor)
  self.globalID = self.globalID + 1

  table.insert(self.actors, actor)
  actor.id = self.globalID
end

function World:destroyActor(actorToDestroy)
  for i, actor in ipairs(self.actors) do
    if actor == actorToDestroy then
      table.remove(self.actors, i)
    end
  end
end

-- INFO FUNCTIONS
-- Get info from the world

function World:caseIsEmpty(x, y, notThisActor)
  local actor = self:getActorInCase(x, y, notThisActor)

  return (actor == nil)
end

function World:getActorInCase(x, y, notThisActor)
  for i, actor in ipairs(self.actors) do
    if (actor.x == x) and (actor.y == y) and (actor ~= notThisActor) then
      core.debug:print("cbs/world", "one actor found in case <" .. x .. ";" .. y .. ">")
      return actor
    end
  end

  return nil
end

-- BATTLER FUNCTIONS
-- Handle the actual battle participants

function World:addHero(x, y, id)
  self.heroNumber = self.heroNumber + 1
  local hero = self.obj.Hero(self, x, y, id, self.heroNumber)

  table.insert(self.battlers, hero)
end

function World:addEnnemy(x, y, id)
  self.ennNumber = self.ennNumber + 1
  local enn = self.obj.Ennemy(self, x, y, id, self.ennNumber)

  table.insert(self.battlers, enn)
end

function World:destroyBattler(actorToDestroy)
  -- remove the actor from the battler liste
  for i, actor in ipairs(self.battlers) do
    if actor == actorToDestroy then
      table.remove(self.battlers, i)
    end
  end

  -- Also remove all actions related to the actor
  for i, action in ipairs(self.actionlist) do
    if action.actor == actorToDestroy then
      table.remove(self.actionlist, i)
    end
  end
end

function World:generateActionList()
  self.actionlist = {}

  for i,v in ipairs(self.battlers) do
    for i=1, v.actionPerTurn do
      local action = {}
      action.actor  = v
      action.number = i
      table.insert(self.actionlist, action)
    end
  end
end

function World:countHeroes()
  local count = 0

  for i, battler in ipairs(self.battlers) do
    if (battler.isHero) then
      count = count + 1
    end
  end

  return count
end

function World:countEnnemies()
  local count = 0

  for i, battler in ipairs(self.battlers) do
    if (battler.isEnnemy) then
      count = count + 1
    end
  end

  return count
end

function World:getPlayerHUDPosition()
  return self.playerHUDPosition
end

-- UPDATE FUNCTION
-- Update all actors

function World:update(dt)
  for i, actor in ipairs(self.actors) do
    actor:update(dt)
  end

  if (self.isBattleActive) then
    self:updateTurns(dt)
  end

  self.cursor:update(dt)
  self.tweens:update(dt)
  self.map:update(dt)
end

function World:moveBattleCursor(dt)
  local cursorSpeed = 16

  if (self.turns.current == 1) then
    cursorSpeed = 16 * 4
  end

  if math.abs(self.BattlerCursor - self.turns.current) > (cursorSpeed * dt) then
    self.BattlerCursor = self.BattlerCursor - utils.math.sign(self.BattlerCursor - self.turns.current) * cursorSpeed * dt
  else
    self.BattlerCursor = self.turns.current
  end
end

-- TURNS FUNCTIONS
-- Handle everything related to the turn system

function World:startBattle()
  self.isBattleActive = true
  self.tweens:newTween(0, 0.4, {playerHUDPosition = 36}, 'inCubic')
end

function World:recalculateTurns()
  self:generateActionList()
  table.sort(self.actionlist, maputils.sortBattlers)
end

function World:updateTurns(dt)
  if (self.turns.changeBattler) then
    self:switchActiveBattler( )
  end
end

function World:switchActiveBattler()
  if (self:countEnnemies()==0) then
    self:finishBattle()
  else
    if (self.turns.isFinished) or (self.turns.current >= #self.actionlist) then
      self:switchToNextTurn()
    else
      self.turns.current = self.turns.current + 1
      core.debug:print("cbs/world", "switching to action number " .. self.turns.current)
    end

    self:selectNextAction()
    self.turns.changeBattler = false
    self.tweens:newTween(0, 0.2, {BattlerCursor = self.turns.current}, 'inCubic')
  end
end

function World:switchToNextTurn()
  core.debug:print("cbs/world", "turn finished")
  self.turns.current = 1
  self.turns.isFinished = false
  self.turns.number = self.turns.number + 1
  self:recalculateTurns()
end

function World:selectNextAction()
  if (self.actionlist[self.turns.current].actor.isDestroyed == true) then
    self:switchActiveBattler()
  else
    self.actionlist[self.turns.current].actor:setActive()
  end
end

function World:finishBattle()
  self.isBattleActive = false
  self.tweens:newTween(0, 0.4, {playerHUDPosition = -64}, 'inCubic')
  self.actionlist = {}
  self.scene:finishBattle()
end

function World:sendSignalToCurrentBattler(signal, subSignal)
  self.actionlist[self.turns.current].actor:receiveSignal(signal, subSignal)
end

-- ACTIVEGRID FUNCTIONS
-- Help to handle the activeGrid system

function World:resetActiveGrid()
  self.activeGrid = maputils.newFullMap()
end

function World:setActiveGrid(ox, oy, shape, size, direction)
  self.activeGrid = maputils.maskToMap(ox, oy, shape, size, direction)
end

function World:setActiveGridFromGrid(grid)
  self.activeGrid = grid
end

function World:resetEffectGrid()
  self.effectGrid = maputils.newEmptyMap()
end

function World:setEffectGrid(ox, oy, shape, size, direction)
  self.effectGrid = maputils.maskToMap(ox, oy, shape, size, direction)
end

-- DRAW FUNCTION
-- Draw the world

function World:draw()
  self.map:draw(self.activeGrid, self.effectGrid)
  self.cursor:drawBottom()
  self:drawShadows()
  self:drawActors()
  self.cursor:drawTop()

  self:drawHUD()
end

function World:drawActors()
  for i, actor in ipairs(self.actors) do
    actor:draw()
  end
end

function World:drawShadows()
  for i, actor in ipairs(self.actors) do
    actor:drawShadow()
  end
end

function World:initHUD()
  self.frame  = gui.newBorder(424, 30, 4)
end

function World:drawHUD()
  for i, battler in ipairs(self.battlers) do
    battler:drawHUD()
  end

  for i, action in ipairs(self.actionlist) do
    action.actor:drawIcon(4 + (i-1)*(20), 6)
  end
  local cursorx = self.BattlerCursor * 20 - 6

  if #self.actionlist > 0 then
    self.assets.images["menucursor"]:draw(cursorx, 26, math.rad(-90), 1, 1, 4, 8)
  end

  local x, y = 362, 3

  love.graphics.draw(self.frame, 424, 20, 0, -1, -1)
  self.assets.images["hudturn"]:draw(x, y)
  self.assets.fonts["hudnbrs"]:set()
  local turnnbr = self.turns.number
  if (turnnbr < 10) then
    turnnbr = "0" .. turnnbr
  end
  love.graphics.print(turnnbr, x + 33, y + 1)
end



return World
