--- 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
      local mode = (ss.curShapeshift and ss.curShapeshift ~= "travel" and ss.curShapeshift ~= "flight") and ss.curShapeshift or "default"

      for k, v in pairs(microResource[strlower(ss.curSpecName)]) do
        if k == mode 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 _, v in pairs(self.spheres) do
        local configName = v.configName

        if ss.db[configName].modes[ss.profile.modeInfo[configName].modeSpec] and v.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 _, v in pairs(self.spheres) do
      v:show(false)
    end
  end
end

--- Displays the layout once a pet battle ends.
--
function Layout:OnPetBattleEnd()
  if ss.global.track.petBattles then
    self.spheres.health:show(true)
    self.spheres.mainResource:show(true)
    self:TrackAdditionalResource(ss.global.track.additionalResource and ss.ClassResources.hasSecondaryResource())
    self:TrackPet(ss.global.track.pet and ss.petAvail)
  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 Additional Resource.
    if self.spheres.additionalResource then
      self.spheres.additionalResource:show(false)
    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 Additional Resource.
    self:TrackAdditionalResource(ss.global.track.additionalResource and ss.ClassResources.hasSecondaryResource())

    -- 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
    local mode = (shapeshift and shapeshift ~= "travel" and shapeshift ~= "flight") and shapeshift or "default"

    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][mode] then

        if k == "health" and self.inCombat then
          v.prevMode = ss.db[k].modes[ss.profile.modeInfo[k].modeSpec][mode]
          ss.profile.modeInfo[k].mode = mode
        else
          v:SetMode(ss.db[k].modes[ss.profile.modeInfo[k].modeSpec][mode], mode);
        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.sphere, "HealthArtwork", -90, 5, 160, 160)
    self.mainResourceArtwork = self.mainResourceArtwork or self:AddArtwork(ss.private.textures.mainResourceArtwork, self.spheres.mainResource.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 _, 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 _, v in pairs(self.spheres) do
        local configName = v.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

  if self.inVehicle and self.spheres.additionalResource then
    self.spheres.additionalResource:show(false)
  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 _, v in pairs(self.spheres) do
      v:UpdateScale()
    end
  end
end

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