-- ========================================================================= --
--                              SylingTracker                                --
--           https://www.curseforge.com/wow/addons/sylingtracker             --
--                                                                           --
--                               Repository:                                 --
--                   https://github.com/Skamer/SylingTracker                 --
--                                                                           --
-- ========================================================================= --
Syling                  "SylingTracker.Keystone.ContentView"                 ""
-- ========================================================================= --
namespace                          "SLT"
-- ========================================================================= --
-- Iterator helper for ignoring the children are used for backdrop, and avoiding
-- they are taken as account for their parent height
IterateFrameChildren  = Utils.IterateFrameChildren
-- ========================================================================= --
ValidateFlags         = System.Toolset.validateflags
ResetStyles           = Utils.ResetStyles
-- ========================================================================= --
GameTooltip           = GameTooltip
-- ========================================================================= --
__Recyclable__ "SylingTracker_KeyStoneAffix%d"
class "KeystoneAffix" (function(_ENV)
  inherit "Frame"
  -----------------------------------------------------------------------------
  --                               Handlers                                  --
  -----------------------------------------------------------------------------
  local function OnSetTexture(self, new)
    Style[self].Icon.fileID = new
  end

  local function UpdateTooltip(self, new)
    if new then 
      self.OnEnter = function()
        GameTooltip:SetOwner(self, "ANCHOR_LEFT")
        GameTooltip:SetText(self.Name, 1, 1, 1, 1, true)
        GameTooltip:AddLine(new, nil, nil, nil, true)
        GameTooltip:Show()
      end

      self.OnLeave = function() GameTooltip:Hide() end 
    else 
      self.OnEnter  = nil 
      self.OnLeave  = nil
    end
  end
  -----------------------------------------------------------------------------
  --                               Properties                                --
  -----------------------------------------------------------------------------
  property "Name" {
    type    = String,
  }

  property "Desc" {
    type    = String,
    handler = UpdateTooltip
  }

  property "Texture" {
    type    = String + Number,
    handler = OnSetTexture
  }
  -----------------------------------------------------------------------------
  --                            Constructors                                 --
  -----------------------------------------------------------------------------
  __Template__{
    Icon = Texture
  }
  function __ctor(self) 
    self:InstantApplyStyle()
  end

end)

__Recyclable__ "SylingTracker_KeystoneAffixes%d"
class "KeystoneAffixes" (function(_ENV)
  inherit "Frame" 
  -----------------------------------------------------------------------------
  --                               Properties                                --
  -----------------------------------------------------------------------------
  local function OnAffixesCountChanged(self, new, old)
    if new > old then 
      for i = 1, new - old do 
        local affix = self:AcquireAffix(new)
      end 
    end
        
    -- Update the anchor
    local previousAffix
    local width = 0
    for i = 1, new do
      local affix = self:AcquireAffix(i)
      if i == 1 then
        affix:SetPoint("TOP")
        affix:SetPoint("LEFT")
      else 
        affix:SetPoint("TOP")
        affix:SetPoint("LEFT", previousAffix, "RIGHT", self.AffixSpacing, 0)
      end 

      width = width + affix:GetWidth()

      previousAffix = affix
    end
    
    self:SetWidth(width + math.max(0, new-1) * self.AffixSpacing)

    self:ReleaseUnusedAffixes(new)
  end
  -----------------------------------------------------------------------------
  --                               Methods                                   --
  -----------------------------------------------------------------------------
  function AcquireAffix(self, index)
    local affix = self.affixesCache[index]
    if not affix then 
      affix = KeystoneAffix.Acquire()
      affix:SetParent(self)

      self.affixesCache[index] = affix 
    end
    
    return affix
  end

  function ReleaseUnusedAffixes(self, releaseAbove)
    for index, affix in ipairs(self.affixesCache) do 
      if index > releaseAbove then 
        affix:Release()
        self.affixesCache[index] = nil 
      end
    end 
  end 
  -----------------------------------------------------------------------------
  --                               Methods                                   --
  -----------------------------------------------------------------------------

  -----------------------------------------------------------------------------
  --                               Properties                                --
  -----------------------------------------------------------------------------
  property "AffixesCount" {
    type    = Number,
    default = 0,
    handler = OnAffixesCountChanged
  }

  property "AffixSpacing" {
    type = Number,
    default = 5,
  }
  -----------------------------------------------------------------------------
  --                            Constructors                                 --
  -----------------------------------------------------------------------------
  function __ctor(self)
    self:InstantApplyStyle()

    self.affixesCache = setmetatable({}, { __mode = "v"})
  end
end)


class "KeystoneTimer" (function(_ENV)
  inherit "Frame" extend "ITimer"
  -----------------------------------------------------------------------------
  --                               Handlers                                  --
  -----------------------------------------------------------------------------  
  local function OnTimeLimitChanged(self, new, old)
      local timeLimit2Key = new * 0.8
      local timeLimit3Key = new * 0.6

      local timerText         = self:GetChild("TimerText")
      local timeLimitPlus2FS  = self:GetChild("TimeLimitPlus2")
      local timeLimitPlus3FS  = self:GetChild("TimeLimitPlus3")


      if self.ShowRemainingTime then 
        Style[timerText].text = HIGHLIGHT_FONT_COLOR:WrapTextInColorCode(SecondsToClock(self.TimeLimit))
      else 
        Style[timerText].text = string.format("%s / %s", 
        HIGHLIGHT_FONT_COLOR:WrapTextInColorCode(SecondsToClock(0)), 
        SecondsToClock(self.TimeLimit))
      end

      Style[timeLimitPlus2FS].text = string.format("[+2] %s", SecondsToClock(timeLimit2Key))
      Style[timeLimitPlus3FS].text = string.format("[+3] %s", SecondsToClock(timeLimit3Key))
  end

  local function OnPlusFailedChanged(self, new, old, prop)
    local color
    if new then 
      color = Color(1, 0, 0)
    else 
      color = Color(38/255, 127/255, 0)
    end

    if prop == "Plus2Failed" then 
      local timeLimitPlus2FS = self:GetChild("TimeLimitPlus2")
      Style[timeLimitPlus2FS].textColor = color 
    elseif prop == "Plus3Failed" then 
      local timeLimitPlus3FS = self:GetChild("TimeLimitPlus3")
      Style[timeLimitPlus3FS].textColor = color 
    end 
  end
  -----------------------------------------------------------------------------
  --                               Methods                                   --
  -----------------------------------------------------------------------------
  function OnTimerUpdate(self, elapsedTime)
    local timerBar    = self:GetChild("TimerBar")
    local timerText   = self:GetChild("TimerText")

    local notTimed = elapsedTime > self.TimeLimit
    local timeLeft = math.max(0, self.TimeLimit - elapsedTime)

    local CURRENT_FONT_COLOR = HIGHLIGHT_FONT_COLOR
    if notTimed then 
      CURRENT_FONT_COLOR = RED_FONT_COLOR
    end
    local timeLeft = math.max(0, self.TimeLimit - elapsedTime)

    if self.ShowRemainingTime then 
      Style[timerText].text = CURRENT_FONT_COLOR:WrapTextInColorCode(SecondsToClock(timeLeft))
    else 
      Style[timerText].text = string.format("%s / %s", 
      CURRENT_FONT_COLOR:WrapTextInColorCode(SecondsToClock(elapsedTime)), 
      SecondsToClock(self.TimeLimit))
    end

    timerBar:SetMinMaxValues(0, self.TimeLimit)
    timerBar:SetValue(timeLeft)

    self.Plus2Failed = elapsedTime > (self.TimeLimit * 0.8)
    self.Plus3Failed = elapsedTime > (self.TimeLimit * 0.6)
  end
  -----------------------------------------------------------------------------
  --                               Properties                                --
  -----------------------------------------------------------------------------
  property "TimeLimit" {
    type = Number,
    default = 0,
    handler = OnTimeLimitChanged
  }
  
  property "ShowRemainingTime" {
    type = Boolean,
    default = false 
  }

  property "Plus2Failed" {
    type = Boolean,
    default = false,
    handler = OnPlusFailedChanged
  }

  property "Plus3Failed" {
    type = Boolean,
    default = false,
    handler = OnPlusFailedChanged
  }
  -----------------------------------------------------------------------------
  --                            Constructors                                 --
  -----------------------------------------------------------------------------
  __Template__{
    TimerText = SLTFontString,
    TimerBar = ProgressBar,
    TimeLimitPlus2 = SLTFontString,
    TimeLimitPlus3 = SLTFontString
  }
  function __ctor(self)
  end

end)

__Recyclable__ "SylingTracker_KeystoneContentView%d"
class "KeystoneContentView" (function(_ENV)
  inherit "ContentView"

  enum "State" {
    None      = 0,
    Progress  = 1,
    Finished  = 2
  }

  __Flags__()
  enum "Flags" {
    NONE            = 0,
    HAS_OBJECTIVES  = 1
  }
  -----------------------------------------------------------------------------
  --                               Methods                                   --
  -----------------------------------------------------------------------------
  function OnViewUpdate(self, data)
    local keystoneData = data.keystone 
    if not keystoneData then 
      return 
    end

    -- Get the elements
    local contentFrame    = self:GetChild("Content")
    local headerFrame     = self:GetChild("Header")
    local topInfoFrame    = contentFrame:GetChild("TopInfo")
    local dungeonNameFS   = headerFrame:GetChild("DungeonName")
    local levelFS         = topInfoFrame:GetChild("Level")
    local deathCountFrame = topInfoFrame:GetChild("DeathCount")
    local affixesFrame    = topInfoFrame:GetChild("Affixes")
    local dungeonIcon     = contentFrame:GetChild("DungeonIcon")
    local timerFrame      = contentFrame:GetChild("Timer")

    -- Determine the state
    local state = State.Progress

    -- Determine the flags 
    local flags = Flags.NONE 

    if keystoneData.objectives then 
      flags = flags + Flags.HAS_OBJECTIVES
    end

    -- ACQUIRE, RELEASE AND STYLING PART
    if state ~= self.State or flags ~= self.Flags then 
      ResetStyles(self)
      -- REVIEW Probably need adding the elements ???

      local stateStyles
      if state ~= State.None then 
        stateStyles = self.StatesStyles and self.StatesStyles[state]
        if stateStyle then 
         Style[self] = stateStyles
        end
      end
      
      if flags ~= self.Flags then

        -- Is the keystone has objectives
        if ValidateFlags(Flags.HAS_OBJECTIVES, flags) then 
          self:AcquireObjectives()
        else
          self:ReleaseObjectives() 
        end


        if flags ~= Flags.NONE then 
          -- Skin Stuffs for flags 
          local flagsStyles = self.FlagsStyles and self.FlagsStyles[flags]
          if flagsStyles then
            Style[self] = flagsStyles 
          end


          -- Override the flags styles by those of state if exists 
          flagsStyles = stateStyles and stateStyles.FlagsStyles
          if flagsStyles then 
            Style[self] = flagsStyles
          end
        end
      end
    end

    -- Update the elements with data

    -- Update dungeon Name 
    if dungeonNameFS then 
      Style[dungeonNameFS].text = keystoneData.name
    end 

    -- Update Level
    local level = keystoneData.level 
    if level then 
      Style[levelFS].text = CHALLENGE_MODE_POWER_LEVEL:format(level)
    end 

    -- Update Death
    local death = keystoneData.death
    local timeLost = keystoneData.timeLost or 0
    if death and death > 0 then 
      Style[deathCountFrame].visible = true 
      Style[deathCountFrame].TextFS.text = tostring(death)

      deathCountFrame.OnEnter = function()
        GameTooltip:SetOwner(deathCountFrame, "ANCHOR_TOPRIGHT")
        GameTooltip:SetText(CHALLENGE_MODE_DEATH_COUNT_TITLE:format(death), 1, 1, 1)
        GameTooltip:AddLine(CHALLENGE_MODE_DEATH_COUNT_DESCRIPTION:format(GetTimeStringFromSeconds(timeLost, false, true)))
        GameTooltip:Show()
      end

      deathCountFrame.OnLeave = function() GameTooltip:Hide() end 
    else
      Style[deathCountFrame].visible = false
      deathCountFrame.OnEnter = nil 
      deathCountFrame.OnLeave = nil
    end 

    -- Update Affixes
    local affixes = keystoneData.affixes 
    local numAffixes = keystoneData.numAffixes
    
    affixesFrame.AffixesCount = numAffixes
    for affixIndex, affixData in ipairs(affixes) do 
      local affixFrame = affixesFrame:AcquireAffix(affixIndex)
      affixFrame.Name = affixData.name 
      affixFrame.Texture = affixData.texture 
      affixFrame.Desc = affixData.desc 
    end

    -- Update Dungeon Icon
    local dungeonTexture = keystoneData.texture 
    if dungeonTexture then 
      Style[dungeonIcon].Icon.fileID = dungeonTexture
    end

    -- Update Timer
    local timeLimit = keystoneData.timeLimit 
    local startTime = keystoneData.startTime
    local completed = keystoneData.completed

    if timeLimit then 
      timerFrame.TimeLimit = timeLimit
    end

    if startTime then
      timerFrame.StartTime = startTime

      if not completed then 
        timerFrame:Start()
      else
        timerFrame:Stop()
      end
    end


    -- Update Objectives if exists 
    local objectivesData = keystoneData.objectives
    if objectivesData then 
      local objectivesView = self:AcquireObjectives()
      objectivesView:UpdateView(objectivesData)
    end 

    self.Flags = flags 
    self.State = state 
  end

  function AcquireObjectives(self)
    local content = self:GetChild("Content")
    local objectives = content:GetChild("Objectives")
    
    if not objectives then 
      objectives = self.ObjectivesClass.Acquire()

      -- We need to keep the old name when we'll release it
      self.__PreviousObjectivesName = objectives:GetName()

      objectives:SetParent(content)
      objectives:SetName("Objectives")
      objectives:InstantApplyStyle()

      objectives.OnSizeChanged = objectives.OnSizeChanged + self.OnObjectivesSizeChanged

      self:AdjustHeight(true)
    end

    return objectives
  end

  function ReleaseObjectives(self)
    local content = self:GetChild("Content")
    local objectives = content:GetChild("Objectives")

    if objectives then 
      -- Give its old name (generated by the recycle system)
      objectives:SetName(self.__PreviousObjectivesName)
      self.__PreviousObjectivesName = nil 

      -- Unregister the events 
      objectives.OnSizeChanged = objectives.OnSizeChanged - self.OnObjectivesSizeChanged

      -- It's better to release after events have been un registered for avoiding
      -- useless calls
      objectives:Release()

      self:AdjustHeight(true)
    end
  end

  function OnRelease(self)
    -- First, release the children
    self:ReleaseObjectives()

    -- Reset the timer 
    local timer =self:GetChild("Content"):GetChild("Timer")
    timer:Stop()
    timer.TimeLimit = nil 
    timer.ShowRemainingTime = nil 
    timer.Plus2Failed = nil 
    timer.Plus3Failed = nil

    -- We call the "Parent" onRelease (see, ContentView)
    super.OnRelease(self)

    -- Reset the class properties
    self.Flags = nil
    self.State = nil
  end 
  -----------------------------------------------------------------------------
  --                               Properties                                --
  -----------------------------------------------------------------------------
  property "Flags" {
    type    = KeystoneContentView.Flags,
    default = KeystoneContentView.Flags.NONE 
  }

  property "State" {
    type    = KeystoneContentView.State,
    default = KeystoneContentView.State.None
  }

  property "ObjectivesClass" {
    type    = ClassType,
    default = ObjectiveListView
  }


  property "FlagsStyles" {
    type = Table
  }

  property "StatesStyles" {
    type = Table
  }
  -----------------------------------------------------------------------------
  --                            Constructors                                 --
  -----------------------------------------------------------------------------
  __Template__{
    {
      Header = {
        DungeonName = SLTFontString
      },
      Content = {
        TopInfo     = Frame, 
        DungeonIcon = IconBadge,
        Timer       = KeystoneTimer,
        {
          TopInfo = {
            Level       = SLTFontString,
            Affixes     = KeystoneAffixes,
            DeathCount  = Frame,
            {
              DeathCount = {
                IconTex  = Texture,
                TextFS   = SLTFontString
              }
            }
          }
        }
      }
    }
  }
  function __ctor(self)
    self.OnObjectivesSizeChanged = function() self:AdjustHeight(true) end
  end
end)
-------------------------------------------------------------------------------
--                                Styles                                     --
-------------------------------------------------------------------------------
Style.UpdateSkin("Default", {
  [KeystoneAffix] = {
    width   = 24,
    height  = 24,

    Icon = {
      setAllPoints = true,
      texCoords = RectType(0.07, 0.93, 0.07, 0.93)
    }
  },

  [KeystoneTimer] = {
    height = 64,

    TimerText = {
      height = 24,
      textColor = Color(1, 1, 1),
      sharedMediaFont = FontType("PT Sans Narrow Bold", 21),
      location = {
        Anchor("TOP"),
        Anchor("LEFT"),
        Anchor("RIGHT")
      }
    },

    TimerBar = {
      height = 16,
      location = {
        Anchor("TOP", 0, -2, "TimerText", "BOTTOM"),
        Anchor("LEFT", 10, 0),
        Anchor("RIGHT", -10, 0)
      }
    },

    TimeLimitPlus2 = {
      height = 16,
      textColor = Color(38/255, 127/255, 0),
      sharedMediaFont = FontType("PT Sans Narrow Bold", 14),
      location = {
        Anchor("TOPRIGHT", 0, -5, "TimerBar", "BOTTOM"),
        Anchor("LEFT", 0, 0),
      }
    },
    TimeLimitPlus3 = {
      height = 16,
      textColor = Color(38/255, 127/255, 0),
      sharedMediaFont = FontType("PT Sans Narrow Bold", 14),
      location = {
        Anchor("TOPLEFT", 0, -5, "TimerBar", "BOTTOM"),
        Anchor("RIGHT", 0, 0),
      }
    }
  },

  [KeystoneContentView] = {
    Header = {
      IconBadge = {
        Icon = {
          atlas = AtlasType("Dungeon")
        }
      },

      Label = {
        text            = "Mythic +",
        sharedMediaFont = FontType("PT Sans Narrow Bold", 13),
        justifyV        = "TOP"
      },

      DungeonName = {
        sharedMediaFont = FontType("PT Sans Caption Bold", 13),
        textColor       = Color(1, 233/255, 174/255),
        justifyV        = "BOTTOM",
        textTransform   = "UPPERCASE",
        location        = {
          Anchor("TOP"),
          Anchor("LEFT", 0, 0, "IconBadge", "RIGHT"),
          Anchor("RIGHT"),
          Anchor("BOTTOM", 0, 2)
        }
      }
    },

    Content = {
      backdrop = {
        bgFile = [[Interface\AddOns\SylingTracker\Media\Textures\LinearGradient]],
      },
      backdropColor = { r = 35/255, g = 40/255, b = 46/255, a = 0.73},

      TopInfo = {
        height = 28,
        backdrop = { 
          bgFile = [[Interface\AddOns\SylingTracker\Media\Textures\LinearGradient]],
        },
        backdropColor = { r = 0.1, g = 0.1, b = 0.1, a = 0.7},
        location = {
          Anchor("TOP"),
          Anchor("LEFT"),
          Anchor("RIGHT")
        },

        Level = {
          height = 24,
          sharedMediaFont = FontType("PT Sans Narrow Bold", 13),
          textTransform   = "UPPERCASE",
          location = {
            Anchor("TOP"),
            Anchor("LEFT", 5, 0),
          }
        },

        Affixes = {
          height = 28,
          location = {
            Anchor("TOP", 0, -2)
          }
        },

        DeathCount = {
          height = 24,
          width = 38,
          location = {
            Anchor("TOP"),
            Anchor("RIGHT", -5, 0),
          },

          IconTex = {
            atlas = AtlasType("poi-graveyard-neutral", true),
            location = {
              Anchor("LEFT")
            }
          },

          TextFS = {
            sharedMediaFont = FontType("PT Sans Caption Bold", 10),
            shadowOffset = { x = 0.5, y = 0},
            shadowColor = Color(0, 0, 0, 1),
            justifyH = "LEFT",
            
            location = {
              Anchor("TOP"),
              Anchor("LEFT", 5, 0, "IconTex", "RIGHT"),
              Anchor("RIGHT"),
              Anchor("BOTTOM")              
            }
          }
        },
      },

      DungeonIcon = {
        width = 64,
        height = 64,
        location = {
          Anchor("TOP", 0, -5, "TopInfo", "BOTTOM"),
          Anchor("LEFT", 5, 0)
        }
      },

      Timer = {
        location = {
          Anchor("TOP", 0, -5, "TopInfo", "BOTTOM"),
          Anchor("LEFT", 5, 0, "DungeonIcon", "RIGHT"),
          Anchor("RIGHT", -5, 0)
        }
      }
    },

    FlagsStyles = {
      [KeystoneContentView.Flags.HAS_OBJECTIVES] = {
        Content = {
          Objectives = {
            spacing = 5,
            location = {
              Anchor("TOP", 0, -10, "DungeonIcon", "BOTTOM"),
              Anchor("LEFT", 5, 0),
              Anchor("RIGHT", -5, 0)
            }
          }
        }
      }
    }
  }
})