boombastic = {}

dofile("system/system.lua")
dofile("system/automaton.lua")
dofile("system/controls.lua")
dofile("system/sprite.lua")
dofile("system/font.lua")
dofile("system/sound.lua")
dofile("system/particleSystem.lua")

dofile("constants.lua")
dofile("splashScreen.lua")
dofile("menu.lua")
dofile("hud.lua")
dofile("dialogs.lua")
dofile("map.lua")
dofile("rocks.lua")
dofile("bonuses.lua")
dofile("bombs.lua")
dofile("rockets.lua")
dofile("explosions.lua")
dofile("particleSystems.lua")
dofile("tank.lua")
dofile("botTank.lua")
dofile("utils.lua")


-----------------------
-- Initializes the game
-----------------------
function load()
   math.randomseed(os.time())
   
   boombastic.automaton = newAutomaton()
   boombastic.automaton:addState("init", initLoad, initUpdate)
   boombastic.automaton:addState("splash-screen", nil, splashUpdate, splashDraw)
   boombastic.automaton:addState("main-menu", nil, menuUpdate, menuDraw)
   boombastic.automaton:addState("game", gameLoad, gameUpdate, gameDraw)
end


-------------------
-- Updates the game
-------------------
function update(dt)
   boombastic.automaton:update(dt)
end


----------------
-- Draws a frame
----------------
function draw()
   boombastic.automaton:draw()
end



---------------------------
--
-- Initialization functions
--
---------------------------


-----------------------
-- Initializes the game
-----------------------
function initLoad()
   boombastic.splashScreen    = newSplashScreen()
   boombastic.menu            = newMenu()
   boombastic.hud             = newHud()
   boombastic.dialogs         = newDialogs()
   boombastic.map             = newMap()
   boombastic.rocks           = newRocks()
   boombastic.bonuses         = newBonuses()
   boombastic.bombs           = newBombs()
   boombastic.rockets         = newRockets()
   boombastic.explosions      = newExplosions()
   boombastic.particleSystems = newParticleSystems()
   boombastic.tanks           = {}
   boombastic.inPause         = false
end


----------------------------------------------------
-- After the initialization, go to the splash-screen
----------------------------------------------------
function initUpdate()
   return "splash-screen"
end



--------------------------
--
-- Splash-screen functions
--
--------------------------


----------------------------
-- Updates the splash-screen
----------------------------
function splashUpdate(dt)
   if (boombastic.splashScreen:update(dt)) then
      return "main-menu"
   end
end


--------------------------
-- Draws the splash-screen
--------------------------
function splashDraw()
   boombastic.splashScreen:draw()
end



----------------------
--
-- Main-menu functions
--
----------------------


------------------------
-- Updates the main-menu
------------------------
function menuUpdate(dt)   
   local activeEntry = nil
   
   -- Navigate throught the menu
   if (controls.isPressed(Button.UP)) then
      boombastic.menu:selectEntry(-1)
   end
   if (controls.isPressed(Button.DOWN)) then
      boombastic.menu:selectEntry(1)
   end
   if (controls.isPressed(Button.LEFT)) then
      boombastic.menu:selectValue(-1)
   end
   if (controls.isPressed(Button.RIGHT)) then
      boombastic.menu:selectValue(1)
   end
   if (controls.isPressed(Button.CROSS)) then
      activeEntry = boombastic.menu:validate()
   end
   if (controls.isPressed(Button.CIRCLE)) then
      boombastic.menu:popMenu()
   end
   
   -- Update the menu
   boombastic.menu:update(dt)
   boombastic.particleSystems:update(dt)
   
   -- Do the corresponding action if an entry of the menu has been activated
   if (activeEntry == "start") then
      boombastic.menu:goToRoot()
      return "game"
   end
end


----------------------
-- Draws the main-menu
----------------------
function menuDraw()
   boombastic.menu:draw()
   boombastic.particleSystems:draw(dt)
end



--------------------
--
-- In-game functions
--
--------------------


-----------------
-- Loads the game
-----------------
function gameLoad()
   gameReplay()
   
   local numTanks = boombastic.menu:getValue("enemies")
   boombastic.tanks = {}
   
   -- Create the player tank
   local tank = newTank("Player", 0, 0, TankDirection.RIGHT, TankColor.RED)
   table.insert(boombastic.tanks, tank)
   
   -- Create the bot tanks
   if (numTanks >= 1) then
      tank = newBotTank("Bot: 1", 10, 6, TankDirection.LEFT, TankColor.BLUE)
      table.insert(boombastic.tanks, tank)
   end
   
   if (numTanks >= 2) then
      tank = newBotTank("Bot: 2", 10, 0, TankDirection.LEFT, TankColor.YELLOW)
      table.insert(boombastic.tanks, tank)
   end
   
   if (numTanks >= 3) then
      tank = newBotTank("Bot: 3", 0, 6, TankDirection.RIGHT, TankColor.GREEN)
      table.insert(boombastic.tanks, tank)
   end
end


---------------------------------
-- Starts a new round of the game
---------------------------------
function gameReplay()
   boombastic.hud:reset()
   boombastic.map:reset()
   boombastic.rocks:reset()
   boombastic.bonuses:reset()
   boombastic.bombs:reset()
   boombastic.rockets:reset()
   boombastic.explosions:reset()
   boombastic.inPause = false

   for k, tank in ipairs(boombastic.tanks) do
      tank:relive()
   end
end


--------------------------------------------
-- Chooses a winner randomly among the tanks
-- and kills the others
--------------------------------------------
function gameChooseAWinner()
   local tanksToKill = {}
   
   for k, tank in ipairs(boombastic.tanks) do
      if (tank:isAlive()) then
         table.insert(tanksToKill, tank)
      end
   end
   table.remove(tanksToKill, math.random(#tanksToKill))
   
   for k, tank in ipairs(tanksToKill) do
      tank:kill()
   end
end


-------------------
-- Updates the game
-------------------
function gameUpdate(dt)
   
   -- Retrieve the dialog action
   local action = nil
   if (controls.isPressed(Button.CROSS)) then
      action = boombastic.dialogs:seedButtonPress(Button.CROSS)
   elseif (controls.isPressed(Button.CIRCLE)) then
      action = boombastic.dialogs:seedButtonPress(Button.CIRCLE)
   end
   
   -- Apply the dialog action if there is one
   if (action) then
      boombastic.dialogs:setActiveDialog(nil)
      
      if (action == "resume") then
         boombastic.inPause = false
      
      elseif (action == "replay") then
         gameReplay()
         
      elseif (action == "finish-round") then
         gameChooseAWinner()
      
      elseif (action == "main-menu") then
         return "main-menu"
      end
   
   -- If the game is in pause, we only update the dialogs
   elseif (boombastic.inPause) then
      boombastic.dialogs:update(dt)
   
   -- If the game is not in pause, we update all the elements of the game
   else
      if (not boombastic.dialogs:getActiveDialog()) then
         
         -- Move the tank
         if (controls.isHeld(Button.LEFT)) then
            boombastic.tanks[1]:move(TankDirection.LEFT, dt)
         end
         if (controls.isHeld(Button.RIGHT)) then
            boombastic.tanks[1]:move(TankDirection.RIGHT, dt)
         end
         if (controls.isHeld(Button.UP)) then
            boombastic.tanks[1]:move(TankDirection.UP, dt)
         end
         if (controls.isHeld(Button.DOWN)) then
            boombastic.tanks[1]:move(TankDirection.DOWN, dt)
         end
         
         -- Drop a bomb when "cross" is pressed
         if (controls.isPressed(Button.CROSS)) then
            boombastic.tanks[1]:dropBomb()
         end
         
         -- Launch a rocket when "circle" is pressed
         if (controls.isPressed(Button.CIRCLE)) then
            boombastic.tanks[1]:launchRocket()
         end
         
         -- Pause the game when start is pressed
         if (controls.isHeld(Button.START)) then
            boombastic.dialogs:setActiveDialog("pause")
            boombastic.inPause = true
         end
      end
      
      -- Update the elements of the game
      for k, tank in ipairs(boombastic.tanks) do
         tank:update(dt)
      end
      boombastic.hud:update(dt)
      boombastic.dialogs:update(dt)
      boombastic.bonuses:update(dt)
      boombastic.bombs:update(dt)
      boombastic.rockets:update(dt)
      boombastic.explosions:update(dt)
      boombastic.particleSystems:update(dt)
      
      -- Determine if there is a winner
      local numAliveTanks = 0
      local winner = nil
      for k, tank in ipairs(boombastic.tanks) do
         if (tank:isAlive()) then
            numAliveTanks = numAliveTanks + 1
            winner = tank
         end
      end
      
      -- Show the corresponding dialog if the game is over
      if (numAliveTanks <= 1) then
         if (winner and not boombastic.dialogs:getActiveDialog()) then
            winner:win()
         end
         
         if (winner == boombastic.tanks[1]) then
            boombastic.dialogs:setActiveDialog("win")
         else
            boombastic.dialogs:setActiveDialog("lose-round-over")
         end
      elseif (not boombastic.tanks[1]:isAlive()) then
         boombastic.dialogs:setActiveDialog("lose-round-not-over")
      end
   end
end


---------------------------------
-- Draws the elements of the game
---------------------------------
function gameDraw()
   boombastic.hud:draw()
   boombastic.map:draw()
   boombastic.rocks:draw()
   boombastic.bonuses:draw()
   boombastic.bombs:draw()
   for k, tank in ipairs(boombastic.tanks) do
      tank:draw()
   end
   boombastic.explosions:draw()
   boombastic.particleSystems:draw()
   boombastic.rockets:draw()
   boombastic.dialogs:draw()
end



------------
--
-- Main-loop
--
------------

---------------------------------
-- Runs the main-loop of the game
---------------------------------
system.run()
