--- Layout Container for SeamoreSpheres unit frames
--
local Layout = {}
local addon, nsVars = ...
local ss = nsVars.SS

local GetShapeshiftFormID, InCombatLockdown = GetShapeshiftFormID, InCombatLockdown
local CreateFrame, UIParent, pairs = ss.CreateFrame, UIParent, pairs
local strlower = strlower

Layout.spheres = {}
Layout.artwork = {}

--- Adds the artwork to the layout.
-- @param texture The texture to display.
-- @param sphere The sphere to use for an achor.
-- @param name The name of the frame.
-- @param offsetX The x offset.
-- @param offsetY The y offset.
-- @param height The height.
-- @param width The width.
-- @return The created frame.
--
function Layout:AddArtwork(texture,sphere,name,offsetX,offsetY,height,width)
  local art = CreateFrame("Frame",ss._f..name,sphere)
  art:SetPoint      ("CENTER",offsetX,offsetY)
  art:SetHeight     (height)
  art:SetWidth      (width)
  art:SetFrameStrata("MEDIUM")
  art:SetFrameLevel (9)
  art.texture = art:CreateTexture(nil,"OVERLAY")
  art.texture:SetTexture  (texture)
  art.texture:SetAllPoints(art)

  return art
end

--- Displays the micro resource.
-- @param sphere The sphere resource to display.
--
function Layout:AddMicroSpheres(sphere)
  sphere = sphere or "health"

  -- Clear current values.
  local hasResource
  local microResource = (sphere == "mainResource") and ss.profile.microResourcesSecondary or ss.profile.microResources

  if ss.curClass == "druid" then
    if microResource and microResource[strlower(ss.curSpecName)] then
      for k,v in pairs(microResource[strlower(ss.curSpecName)]) do
        if ss.curShapeshift and (k == ss.curShapeshift) then
          hasResource = true
          self.spheres[sphere]:AddResourceGroup(v, (sphere == "mainResource") and "secondary" or nil)
          break
        end
      end
    end
  elseif microResource then
    for k,v in pairs(microResource) do
      if k == strlower(ss.curSpecName) then
        hasResource = true
        self.spheres[sphere]:AddResourceGroup(v, (sphere == "mainResource") and "secondary" or nil)
        break
      end
    end
  end
  
  if not hasResource then
    self.spheres[sphere]:AddResourceGroup()
  end
end

--- Adds a new unit frame to the layout.
-- @param name The db config index.
-- @param config The config previewSphereList index's to add.
--
function Layout:Add(name, config)
  if name and ss.db[name] then
    config = config or {}
    self.spheres[name] = SeamoreSpheresUnitFrame:New(ss.db[name], UIParent, "active")
  
    ss.Config.previewSphereList = ss.Config.previewSphereList or {}
    
    for k,v in pairs(config) do
      -- Add entries to the preview sphere list.
      ss.Config.previewSphereList[k] = v
    end
  end
end

--- Ends combat mode.
--
function Layout:EndCombat()
  if self.inCombat then
    self.inCombat = nil
    
    if ss.global.track.combat then
      for k,v in pairs(self.spheres) do
        local configName = v:Get("configName")

        if ss.db[configName].modes[ss.profile.modeInfo[configName].modeSpec] and v:Get("prevMode") then
          if (configName ~= "pet") and (configName == "health" or ss.curClass ~= "druid") then
            v:setMode(ss.db[configName].modes[ss.profile.modeInfo[configName].modeSpec][ss.profile.modeInfo[configName].mode]);
          end
        end
      end
    end
  end
end

--- Returns combat status.
-- @return Flag for combat status.
--
function Layout:InCombat()
  return self.inCombat
end

--- Toggle the PlayerFrame visibility.
--
function Layout:UpdatePlayerFrame()
  if ss.global.show.playerFrame then
    PlayerFrame:Show()
  else
    PlayerFrame:Hide()
  end
end

--- Initializes the layout.
--
function Layout:Initialize()
  self:UpdatePlayerFrame()
  ss:InitializeConfigPanel()

  self:Add("health", {
    ["health"] = {ss.T["resource_name_health"], "Health"},
    ["vehicle"] = {ss.T["resource_name_vehicle"], "Vehicle" }
  })
  self:AddMicroSpheres()
  
  -- Create main resource sphere
  self:Add("mainResource", {
    ["mainResource"] = {ss.T["resource_name_mainResource"], "MainResource"},
    ["vehicleResource"] = {ss.T["resource_name_vehicleResource"], "VehicleResource" }
  })
  self:AddMicroSpheres("mainResource")

  self:TrackAdditionalResource(ss.global.track.additionalResource and ss.ClassResources.hasSecondaryResource())
  self:ShowArtwork            (ss.global.show.artwork)

  if ss.curClass == "druid" then
    self:Shapeshift(ss.APIUtils.shapeshiftToString(GetShapeshiftFormID() or 0))
  elseif (ss.curClass == "hunter") or (ss.curClass == "warlock") or (ss.curClass == "mage") or (ss.curClass == "deathknight") then
    -- Preload the pet frame.
    self:TrackPet(true)
    self:TrackPet(false)
  end
  
  self:UpdateScale()
end

--- Hides the layout during pet battles.
--
function Layout:OnPetBattleStart()
  if ss.global.track.petBattles then
    for k,v in pairs(self.spheres) do
      v:Hide()
    end
  end
end

--- Displays the layout once a pet battle ends.
--
function Layout:OnPetBattleEnd()
  if ss.global.track.petBattles then
    for k,v in pairs(self.spheres) do
      v:Show()
    end
  end
end

--- Enables vehicle modes.
--
function Layout:OnPlayerEnteredVehicle()
  if not self.inVehicle then
    self.inVehicle = true
    
    self.spheres["health"]      :setResource(ss.db["vehicle"])
    self.spheres["mainResource"]:setResource(ss.db["vehicleResource"])

    if self.inCombat then
      self.spheres["health"]:setMode(ss.db["vehicle"].modes[ss.profile.modeInfo["vehicle"].modeSpec]["combat"]);

      if ss.curClass ~= "druid" then
        self.spheres["mainResource"]:setMode(ss.db["vehicleResource"].modes[ss.profile.modeInfo["vehicleResource"].modeSpec]["combat"]);
      end
    end

    -- Hide micro spheres.
    self.spheres.health:AddResourceGroup()
    self.spheres.mainResource:AddResourceGroup()
  end
end

--- Disables vehicle modes.
--
function Layout:OnPlayerExitedVehicle()
  if self.inVehicle then
    self.inVehicle = nil
    
    self.spheres["health"]      :setResource(ss.db["health"])
    self.spheres["mainResource"]:setResource(ss.db["mainResource"])

    if self.inCombat then
      self.spheres["health"]:setMode(ss.db["health"].modes[ss.profile.modeInfo["health"].modeSpec]["combat"]);

      if ss.curClass ~= "druid" then
        self.spheres["mainResource"]:setMode(ss.db["mainResource"].modes[ss.profile.modeInfo["mainResource"].modeSpec]["combat"]);
      end
    end

    -- Reload micro spheres.
    self:AddMicroSpheres()
    self:AddMicroSpheres("mainResource")
  end
end

--- Refreshes the micro spheres.
--
function Layout:OnUnitMaxPowerChanged()
  self:AddMicroSpheres()
  self:AddMicroSpheres("mainResource")
end

--- Refreshes the layout.
--
function Layout:Refresh()
  ss.profile.modeInfo.mainResource.modeSpec       = ss.db.mainResource.modes[ss.curSpec]       and ss.curSpec or "defaultSpec"
  ss.profile.modeInfo.additionalResource.modeSpec = ss.db.additionalResource.modes[ss.curSpec] and ss.curSpec or "defaultSpec"
  ss.profile.modeInfo.health.modeSpec             = ss.db.health.modes[ss.curSpec]             and ss.curSpec or "defaultSpec"
  
  self.spheres.health      :setResource(self.inVehicle and ss.db.vehicle or ss.db.health)
  self.spheres.mainResource:setResource(self.inVehicle and ss.db.vehicleResource or ss.db.mainResource)
  self:TrackAdditionalResource(ss.global.track.additionalResource and ss.ClassResources.hasSecondaryResource())
  
  if ss.ClassResources.hasSecondaryResource() then
    ss.Config.buttonFrame.UsePowerTrackerCheckButton:Show()
  else
    ss.Config.buttonFrame.UsePowerTrackerCheckButton:Hide()
  end
  
  self:AddMicroSpheres()
  self:AddMicroSpheres("mainResource")
  
  if self.inCombat then
    self.inCombat = nil
    self:StartCombat()
  end
end

--- Updates the layour for the current shapeshift.
-- @param shapeshift The shapeshift name.
--
function Layout:Shapeshift(shapeshift)
  if shapeshift ~= ss.curShapeshift then
    ss.curShapeshift = shapeshift
    
    for k,v in pairs(self.spheres) do
      if ss.db[k].modes[ss.profile.modeInfo[k].modeSpec] and ss.db[k].modes[ss.profile.modeInfo[k].modeSpec][shapeshift] then
        
        if k == "health" and self.inCombat then
          v:Set("prevMode", ss.db[k].modes[ss.profile.modeInfo[k].modeSpec][shapeshift])
          ss.profile.modeInfo[k].mode = shapeshift
        else
          v:setMode(ss.db[k].modes[ss.profile.modeInfo[k].modeSpec][shapeshift], shapeshift);
        end
      end
    end
    
    self:AddMicroSpheres()
    self:AddMicroSpheres("mainResource")
  end
end

--- Toggles the artwork display.
-- @param show Flag to display or hide the frames.
--
function Layout:ShowArtwork(show)
  if show then
    self.healthArtwork       = self.healthArtwork       or self:AddArtwork(ss.private.textures.healthArtwork,       self.spheres.health      :Get("sphere"), "HealthArtwork",      -90, 5, 160, 160)
    self.mainResourceArtwork = self.mainResourceArtwork or self:AddArtwork(ss.private.textures.mainResourceArtwork, self.spheres.mainResource:Get("sphere"), "MainResourceArtwork",70,  5, 160, 160)

    self.healthArtwork      :Show()
    self.mainResourceArtwork:Show()
  else
    if self.healthArtwork       then self.healthArtwork      :Hide() end
    if self.mainResourceArtwork then self.mainResourceArtwork:Hide() end
  end
end

--- Toggles the texture gradients.
-- @param show Flag to display or hide the frames.
--
function Layout:ShowGradients(show)
  for k,v in pairs(self.spheres) do
    if show then
      v:showGradients()
    else
      v:hideGradients()
    end
  end

  ss.global.show.gradient  = show
end

--- Enables combat modes.
--
function Layout:StartCombat()
  if not self.inCombat then
    self.inCombat = true
    
    if ss.global.track.combat then
      for k,v in pairs(self.spheres) do
        local configName = v:Get("configName")
        
        if ss.db[configName].modes[ss.profile.modeInfo[configName].modeSpec] and ss.db[configName].modes[ss.profile.modeInfo[configName].modeSpec]["combat"] then
          if (configName ~= "pet") and (configName == "health" or configName == "vehicle" or ss.curClass ~= "druid") then
            v:setMode(ss.db[configName].modes[ss.profile.modeInfo[configName].modeSpec]["combat"])
          end
        end
      end
    end
  end
end

--- Toggles the additional resource frames.
-- @param track Flag to display or hide the frames.
--
function Layout:TrackAdditionalResource(track)
  if track and not self.spheres.additionalResource then
    self:Add("additionalResource", {
      ["additionalResource"] = {ss.T["resource_name_additionalResource"], "AdditionalResource" }
    })
  elseif track and self.spheres.additionalResource then
    self.spheres.additionalResource:setResource(ss.db.additionalResource)
    self.spheres.additionalResource:show       (true)
    ss.Config.previewSphereList.additionalResource = {ss.T["resource_name_additionalResource"], "AdditionalResource"}
  elseif not track and self.spheres.additionalResource then
    self.spheres.additionalResource:show(false)
    ss.Config.previewSphereList.additionalResource = nil
  end
end

--- Toggles the pet resource frames.
-- @param track Flag to display or hide the frames.
--
function Layout:TrackPet(track)
  if track and not self.spheres.pet then
    self:Add("pet", {
      ["pet"] = {ss.T["resource_name_pet"], "Pet" }
    })
  elseif track and self.spheres.pet then
    self.spheres.pet:setResource(ss.db.pet)
    self.spheres.pet:show       (true)

    ss.Config.previewSphereList.pet = {ss.T["resource_name_pet"], "Pet"}
  elseif not track and self.spheres.pet then
    self.spheres.pet:show(false)
    ss.Config.previewSphereList.pet = nil
  end
end

--- Updates the micro sphere fill textures.
-- @param group The texture group.
-- @param texture The texture identifier.
-- @param sphere The parent sphere.
--
function Layout:UpdateMicroSphereTextures(group, texture, sphere)
  sphere = sphere or "health"
  self.spheres[sphere]:updateResourceGroupTextures(1, group, texture)
  self.spheres[sphere]:updateResourceGroupTextures(2, group, texture)
end

--- Refreshes the layout scales.
--
function Layout:UpdateScale()
  if not InCombatLockdown() then
    for k,v in pairs(self.spheres) do
      v:UpdateScale()
    end
  end
end

ss.Utils.addModule("Layout", Layout)
