-- menusystem :: the controller of the menu system. This object handle the
-- different menu objects

--[[
  Copyright © 2019 Kazhnuz

  Permission is hereby granted, free of charge, to any person obtaining a copy of
  this software and associated documentation files (the "Software"), to deal in
  the Software without restriction, including without limitation the rights to
  use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
  the Software, and to permit persons to whom the Software is furnished to do so,
  subject to the following conditions:

  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
  FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
  IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
]]

local cwd  = (...):gsub('%.init$', '') .. "."

local MenuSystem = Object:extend()

-- Load the differents menu object to get an easy access
MenuSystem.Parent       = require(cwd .. "menus.parent")
MenuSystem.ListBox      = require(cwd .. "menus.listbox")
MenuSystem.FlowBox      = require(cwd .. "menus.flowbox")
MenuSystem.Grid         = require(cwd .. "menus.grid")

-- load widgets object
MenuSystem.Widget       = require(cwd .. "menus.widgets")

-- INIT FUNCTIONS
-- Initialize and configure the menu controller

function MenuSystem:new(scene)
  self.scene = scene
  self.menus = {}
  self.focusedMenu = ""
  self.isActive   = true
  self.lockWorld    = false
  self.lockAssets   = false
end

function MenuSystem:reset()
  self.menus = {}
end

-- ACTIVATION FUNCTIONS
-- Activate and deactivate the whole menusystem

function MenuSystem:activate()
  self.isActive = true

  if (self.lockWorld) then
    if (self.scene.world ~= nil) then
      self.scene.world:setActivity(false)
    end
  end

  if (self.lockAssets) then
    if (self.scene.assets ~= nil) then
      self.scene.assets:setActivity(false)
    end
  end
end

function MenuSystem:deactivate()
  self.isActive = false

  if (self.lockWorld) then
    if (self.scene.world ~= nil) then
      self.scene.world:setActivity(true)
    end
  end

  if (self.lockAssets) then
    if (self.scene.assets ~= nil) then
      self.scene.assets:setActivity(true)
    end
  end
end

function MenuSystem:getActiveState()
  return self.isActive
end

function MenuSystem:lockWorldWhenActive(state)
  self.lockWorld = state
end

function MenuSystem:lockAssetsWhenActive(state)
  self.lockAssets = state
end


-- MENUS FUNCTIONS
-- Controle the menus of the menusystem

function MenuSystem:addMenu(name, menu)
  self.menus[name] = menu
end

function MenuSystem:getMenu(name)
  return self.menus[name]
end

function MenuSystem:menuExist(name)
  return (self.menus[name] ~= nil)
end

function MenuSystem:switchMenu(menuName)
  for name, guiElement in pairs(self.menus) do
    if (name == menuName) then
      guiElement:getFocus()
      guiElement:setVisibility(true)
      guiElement:setActivity(true)
    else
      guiElement:setVisibility(false)
      guiElement:setActivity(false)
    end
  end
end

function MenuSystem:lockMenu(menu, lock)
  local lock = lock or true
  if self:menuExist(menu) then
    self.menus[menu]:lock(lock)
  end
end

function MenuSystem:lockMenuVisibility(menu, lock)
  local lock = lock or true
  if self:menuExist(menu) then
    self.menus[menu]:lockVisibility(lock)
  end
end

function MenuSystem:setMenuActivity(menu, activity)
  local activity = activity or true
  if self:menuExist(menu) then
    self.menus[menu]:setActivity(activity)
    if activity == true then
      -- if we make the menu active, he have to be visible too
      self.menus[menu]:setVisibility(true)
    end
  end
end

function MenuSystem:setMenuVisibility(menu, visibility)
  local visibility = visibility or true
  if self:menuExist(menu) then
    self.menus[menu]:setVisibility(visibility)
  end
end

function MenuSystem:setAllMenuVisibility(visibility)
  for _, guiElement in pairs(self.menus) do
    guiElement:setVisibility(visibility)
  end
end

function MenuSystem:setAllMenuActivity(activity)
  for _, guiElement in pairs(self.menus) do
    guiElement.isActive = activity
  end
end

function MenuSystem:removeDestroyedMenus()
 -- On retire les entitées marquées comme supprimées
  for name, guiElement in pairs(self.menus) do
    if (guiElement.isDestroyed == true) then
      self.menus[name] = nil
    end
  end
end

-- SOUND FUNCTIONS
-- Add sounds to every menus
-- TODO: rework to be used directly by widgets later

function MenuSystem:setSoundFromSceneAssets(soundname)
  for _, guiElement in pairs(self.menus) do
    guiElement:setSoundFromSceneAssets(soundname)
  end
end

function MenuSystem:setSound(soundasset)
  for _, guiElement in pairs(self.menus) do
    guiElement:setSound(soundasset)
  end
end

-- UPDATE FUNCTIONS
-- Update the menus of the menusystem

function MenuSystem:update(dt)
  if (self.isActive) then
    self:removeDestroyedMenus()
    for _, guiElement in pairs(self.menus) do
      guiElement:updateElement(dt)
    end

    if self.menus[self.focusedMenu] ~= nil then
      -- Only check buttons if the current focused menu is actually active
      if self.menus[self.focusedMenu].isActive then
        for keyname, keydata in pairs(self.keys) do
          if self.keys[keyname].isPressed then
            self.menus[self.focusedMenu]:keyreleased(keyname)
          end
        end
      end
    end
  end
end

-- MOUSE FUNCTIONS
-- Send mouse inputs to the menu

function MenuSystem:mousemoved(x, y, dx, dy)
  if (self.isActive) then

    for _, guiElement in pairs(self.menus) do
      if guiElement.isActive then
        if guiElement:areCoordInside(x, y) then
          local xx, yy = guiElement:getRelativeCoordinate(x, y)
          guiElement:mousemoved(xx, yy)
          break;
        end
      end
    end

  end
end

function MenuSystem:mousepressed( x, y, button, istouch )
  if (self.isActive) then
    for _, guiElement in pairs(self.menus) do
      if guiElement.isActive then
        if guiElement:areCoordInside(x, y) then
          local xx, yy = guiElement:getRelativeCoordinate(x, y)
          guiElement:mousepressed(xx, yy, button, istouch )
          break;
        end
      end
    end
  end
end

-- DRAW FUNCTIONS
-- All functions to draw the menus of the menusystem

function MenuSystem:getDrawList()
  local drawList = {}
  for name, guiElement in pairs(self.menus) do
    local drawObject = {}
    drawObject.name   = name
    drawObject.depth  = guiElement.depth
    table.insert(drawList, drawObject)
  end
  table.sort(drawList, function(a,b) return a.depth > b.depth end)

  return drawList
end

function MenuSystem:draw(dt)
  if (self.isActive) then
    -- Draw all the menus
    self.drawList = self:getDrawList()

    for _, drawObject in ipairs(self.drawList) do
      local guiElement = self.menus[drawObject.name]
      if (guiElement.isVisible) then
        guiElement:drawElement(dt)
      end
    end
  end
end

return MenuSystem
