Quantcast
local AddOnName, XIVBar = ...;
local _G = _G;
local xb = XIVBar;
local L = XIVBar.L;

local TravelModule = xb:NewModule("TravelModule", 'AceEvent-3.0')

function TravelModule:GetName()
  return L['Travel'];
end

function TravelModule:OnInitialize()
  self.iconPath = xb.constants.mediaPath..'datatexts\\repair'
  self.garrisonHearth = 110560
  self.hearthstones = {
    64488, -- Innkeeper's Daughter
    54452, -- Ethereal Portal
    93672, -- Dark Portal
    6948,  -- Hearthstone
    556,   -- Astral Recall
    28585, -- Ruby Slippers
    37118, -- Scroll of Recall 1
    44314, -- Scroll of Recall 2
    44315, -- Scroll of Recall 3
    142542, -- Tome of Town Portal
  }

  self.portButtons = {}
  self.extraPadding = (xb.constants.popupPadding * 3)
  self.optionTextExtra = 4
end

function TravelModule:OnEnable()
  if self.hearthFrame == nil then
    self.hearthFrame = CreateFrame("FRAME", nil, xb:GetFrame('bar'))
    xb:RegisterFrame('travelFrame', self.hearthFrame)
  end
  self.hearthFrame:Show()
  self:CreateFrames()
  self:RegisterFrameEvents()
  self:Refresh()
end

function TravelModule:OnDisable()
  self.hearthFrame:Hide()
  self:UnregisterEvent('SPELLS_CHANGED')
  self:UnregisterEvent('BAG_UPDATE_DELAYED')
  self:UnregisterEvent('HEARTHSTONE_BOUND')
end

function TravelModule:CreateFrames()
  self.hearthButton = self.hearthButton or CreateFrame('BUTTON', "hearthButton", self.hearthFrame, "SecureActionButtonTemplate")
  self.hearthIcon = self.hearthIcon or self.hearthButton:CreateTexture(nil, 'OVERLAY')
  self.hearthText = self.hearthText or self.hearthButton:CreateFontString(nil, 'OVERLAY')

  self.portButton = self.portButton or CreateFrame('BUTTON', "portButton", self.hearthFrame, "SecureActionButtonTemplate")
  self.portIcon = self.portIcon or self.portButton:CreateTexture(nil, 'OVERLAY')
  self.portText = self.portText or self.portButton:CreateFontString(nil, 'OVERLAY')

  self.portPopup = self.portPopup or CreateFrame('BUTTON', "portPopup", self.portButton)
  self.popupTexture = self.popupTexture or self.portPopup:CreateTexture(nil, 'BACKGROUND')
end

function TravelModule:RegisterFrameEvents()
  self:RegisterEvent('SPELLS_CHANGED', 'Refresh')
  self:RegisterEvent('BAG_UPDATE_DELAYED', 'Refresh')
  self:RegisterEvent('HEARTHSTONE_BOUND', 'Refresh')
  self.hearthButton:EnableMouse(true)
  self.hearthButton:RegisterForClicks("AnyUp")
  self.hearthButton:SetAttribute('type', 'macro')

  self.portButton:EnableMouse(true)
  self.portButton:RegisterForClicks("AnyUp")
  self.portButton:SetAttribute('*type1', 'macro')
  self.portButton:SetAttribute('*type2', 'portFunction')

  self.portPopup:EnableMouse(true)
  self.portPopup:RegisterForClicks('RightButtonUp')

  self.portButton.portFunction = self.portButton.portFunction or function()
    if TravelModule.portPopup:IsVisible() then
      TravelModule.portPopup:Hide()
    else
      TravelModule:CreatePortPopup()
      TravelModule.portPopup:Show()
    end
  end

  self.portPopup:SetScript('OnClick', function(self, button)
    if button == 'RightButton' then
      self:Hide()
    end
  end)

  self.hearthButton:SetScript('OnEnter', function()
    TravelModule:SetHearthColor()
  end)

  self.hearthButton:SetScript('OnLeave', function()
    TravelModule:SetHearthColor()
  end)

  self.portButton:SetScript('OnEnter', function()
    TravelModule:SetPortColor()
    if InCombatLockdown() then return; end

    GameTooltip:SetOwner(self.portButton, 'ANCHOR_'..xb.miniTextPosition)
    GameTooltip:ClearLines()
    GameTooltip:AddLine("[|cff6699FF"..L['Travel Cooldowns'].."|r]")
    for i, v in pairs(self.portOptions) do
      if IsUsableItem(v.portId) or IsPlayerSpell(v.portId) then
        if IsUsableItem(v.portId) then
          local _, cd, _ = GetItemCooldown(v.portId)
          local cdString = self:FormatCooldown(cd)
          GameTooltip:AddDoubleLine(v.text, cdString, 1, 1, 0, 1, 1, 1)
        end
        if IsPlayerSpell(v.portId) then
          local _, cd, _ = GetSpellCooldown(v.portId)
          local cdString = self:FormatCooldown(cd)
          GameTooltip:AddDoubleLine(v.text, cdString, 1, 1, 0, 1, 1, 1)
        end
      end
    end
    GameTooltip:AddLine(" ")
    GameTooltip:AddDoubleLine('<'..L['Right-Click']..'>', L['Change Port Option'], 1, 1, 0, 1, 1, 1)
    GameTooltip:Show()
  end)

  self.portButton:SetScript('OnLeave', function()
    TravelModule:SetPortColor()
    GameTooltip:Hide()
  end)
end

function TravelModule:UpdatePortOptions()
  local compassName, _ = GetItemInfo(128353)
  if not self.portOptions then
    self.portOptions = {}
  end
  if IsUsableItem(128353) and not self.portOptions[128353] then
    self.portOptions[128353] = {portId = 128353, text = compassName} -- admiral's compass
  end
  if IsUsableItem(140192) and not self.portOptions[140192] then
    self.portOptions[140192] = {portId = 140192, text = GetMapNameByID(1014)} -- dalaran hearthstone
  end
  if IsUsableItem(self.garrisonHearth) and not self.portOptions[self.garrisonHearth] then
    self.portOptions[self.garrisonHearth] = {portId = self.garrisonHearth, text = GARRISON_LOCATION_TOOLTIP} -- needs to be var for default options
  end

  if xb.constants.playerClass == 'DRUID' then
    if IsPlayerSpell(193753) then
      if not self.portOptions[193753] then
        self.portOptions[193753] = {portId = 193753, text = ORDER_HALL_DRUID}
      end
    else
      if not self.portOptions[18960] then
        self.portOptions[18690] = {portId = 18960, text = GetMapNameByID(241)}
      end
    end
  end

  if xb.constants.playerClass == 'DEATHKNIGHT' and not self.portOptions[50977] then
    self.portOptions[50977] = {portId = 50977, text = ORDER_HALL_DEATHKNIGHT}
  end

  if xb.constants.playerClass == 'MAGE' and not self.portOptions[193759] then
    self.portOptions[193759] = {portId = 193759, text = ORDER_HALL_MAGE}
  end

  if xb.constants.playerClass == 'MONK' and not self.portOptions[193759] then
    local portText = GetMapNameByID(809)
    if IsPlayerSpell(200617) then
      portText = ORDER_HALL_MONK
    end
    self.portOptions[193759] = {portId = 193759, text = portText}
  end
end

function TravelModule:FormatCooldown(cdTime)
  if cdTime <= 0 then
    return L['Ready']
  end
  local hours = string.format("%02.f", math.floor(cdTime / 3600))
  local minutes = string.format("%02.f", math.floor(cdTime / 60 - (hours * 60)))
  local seconds = string.format("%02.f", math.floor(cdTime - (hours * 3600) - (minutes * 60)))
  local retString = ''
  if tonumber(hours) ~= 0 then
    retString = hours..':'
  end
  if tonumber(minutes) ~= 0 or tonumber(hours) ~= 0 then
    retString = retString..minutes..':'
  end
  return retString..seconds
end

function TravelModule:SetHearthColor()
  if InCombatLockdown() then return; end

  local db = xb.db.profile
  if self.hearthButton:IsMouseOver() then
    self.hearthText:SetTextColor(unpack(xb:HoverColors()))
  else
    self.hearthIcon:SetVertexColor(db.color.inactive.r, db.color.inactive.g, db.color.inactive.b, db.color.inactive.a)
    local hearthName = ''
    local hearthActive = false
    for i,v in ipairs(self.hearthstones) do
      if (PlayerHasToy(v) or IsUsableItem(v)) then
        if GetItemCooldown(v) == 0 then
          hearthName, _ = GetItemInfo(v)
          if hearthName ~= nil then
            hearthActive = true
            self.hearthButton:SetAttribute("macrotext", "/cast "..hearthName)
            break
          end
        end
      end -- if toy/item
      if IsPlayerSpell(v) then
        if GetSpellCooldown(v) == 0 then
          hearthName, _ = GetSpellInfo(v)
          hearthActive = true
          self.hearthButton:SetAttribute("macrotext", "/cast "..hearthName)
        end
      end -- if is spell
    end -- for hearthstones
    if hearthActive then
      self.hearthIcon:SetVertexColor(db.color.normal.r, db.color.normal.g, db.color.normal.b, db.color.normal.a)
    end
    self.hearthText:SetTextColor(db.color.inactive.r, db.color.inactive.g, db.color.inactive.b, db.color.inactive.a)
  end --else
end

function TravelModule:SetPortColor()
  if InCombatLockdown() then return; end

  local db = xb.db.profile
  local v = xb.db.char.portItem.portId

  if not (self:IsUsable(v)) then
    v = self:FindFirstOption()
    v = v.portId
    if not (self:IsUsable(v)) then
      --self.portButton:Hide()
      return
    end
  end



  if self.portButton:IsMouseOver() then
    self.portText:SetTextColor(unpack(xb:HoverColors()))
  else
    local hearthname = ''
    local hearthActive = false
    if (PlayerHasToy(v) or IsUsableItem(v)) then
      if GetItemCooldown(v) == 0 then
        hearthName, _ = GetItemInfo(v)
        if hearthName ~= nil then
          hearthActive = true
          self.portButton:SetAttribute("macrotext", "/cast "..hearthName)
        end
      end
    end -- if toy/item
    if IsPlayerSpell(v) then
      if GetSpellCooldown(v) == 0 then
        hearthName, _ = GetSpellInfo(v)
        if hearthName ~= nil then
          hearthActive = true
          self.portButton:SetAttribute("macrotext", "/cast "..hearthName)
        end
      end
    end -- if is spell

    if hearthActive then
      self.portIcon:SetVertexColor(db.color.normal.r, db.color.normal.g, db.color.normal.b, db.color.normal.a)
    else
      self.portIcon:SetVertexColor(db.color.inactive.r, db.color.inactive.g, db.color.inactive.b, db.color.inactive.a)
    end
    self.portText:SetTextColor(db.color.inactive.r, db.color.inactive.g, db.color.inactive.b, db.color.inactive.a)
  end --else
end

function TravelModule:CreatePortPopup()
  if not self.portPopup then return; end

  local db = xb.db.profile
  self.portOptionString = self.portOptionString or self.portPopup:CreateFontString(nil, 'OVERLAY')
  self.portOptionString:SetFont(xb:GetFont(db.text.fontSize + self.optionTextExtra))
  self.portOptionString:SetTextColor(db.color.normal.r, db.color.normal.g, db.color.normal.b, db.color.normal.a)
  self.portOptionString:SetText(L['Port Options'])
  self.portOptionString:SetPoint('TOP', 0, -(xb.constants.popupPadding))
  self.portOptionString:SetPoint('CENTER')

  local popupWidth = self.portPopup:GetWidth()
  local popupHeight = xb.constants.popupPadding + db.text.fontSize + self.optionTextExtra
  local changedWidth = false
  for i, v in pairs(self.portOptions) do
    if self.portButtons[v.portId] == nil then
      if IsUsableItem(v.portId) or IsPlayerSpell(v.portId) then
        local button = CreateFrame('BUTTON', nil, self.portPopup)
        local buttonText = button:CreateFontString(nil, 'OVERLAY')

        buttonText:SetFont(xb:GetFont(db.text.fontSize))
        buttonText:SetTextColor(db.color.inactive.r, db.color.inactive.g, db.color.inactive.b, db.color.inactive.a)
        buttonText:SetText(v.text)
        buttonText:SetPoint('LEFT')
        local textWidth = buttonText:GetStringWidth()

        button:SetID(v.portId)
        button:SetSize(textWidth, db.text.fontSize)
        button.isSettable = true
        button.portItem = v

        button:EnableMouse(true)
        button:RegisterForClicks('LeftButtonUp')

        button:SetScript('OnEnter', function()
          buttonText:SetTextColor(db.color.normal.r, db.color.normal.g, db.color.normal.b, db.color.normal.a)
        end)

        button:SetScript('OnLeave', function()
          buttonText:SetTextColor(db.color.inactive.r, db.color.inactive.g, db.color.inactive.b, db.color.inactive.a)
        end)

        button:SetScript('OnClick', function(self)
          xb.db.char.portItem = self.portItem
          TravelModule:Refresh()
        end)

        self.portButtons[v.portId] = button

        if textWidth > popupWidth then
          popupWidth = textWidth
          changedWidth = true
        end
      end -- if usable item or spell
    else
      if not (IsUsableItem(v.portId) or IsPlayerSpell(v.portId)) then
        self.portButtons[v.portId].isSettable = false
      end
    end -- if nil
  end -- for ipairs portOptions
  for portId, button in pairs(self.portButtons) do
    if button.isSettable then
      button:SetPoint('LEFT', xb.constants.popupPadding, 0)
      button:SetPoint('TOP', 0, -(popupHeight + xb.constants.popupPadding))
      button:SetPoint('RIGHT')
      popupHeight = popupHeight + xb.constants.popupPadding + db.text.fontSize
    else
      button:Hide()
    end
  end -- for id/button in portButtons
  if changedWidth then
    popupWidth = popupWidth + self.extraPadding
  end

  if popupWidth < self.portButton:GetWidth() then
    popupWidth = self.portButton:GetWidth()
  end

  if popupWidth < (self.portOptionString:GetStringWidth()  + self.extraPadding) then
    popupWidth = (self.portOptionString:GetStringWidth()  + self.extraPadding)
  end
  self.portPopup:SetSize(popupWidth, popupHeight + xb.constants.popupPadding)
end

function TravelModule:Refresh()
  if self.hearthFrame == nil then return; end

  if not xb.db.profile.modules.travel.enabled then self:Disable(); return; end
  if InCombatLockdown() then
    self.hearthText:SetText(GetBindLocation())
    self.portText:SetText(xb.db.char.portItem.text)
    self:SetHearthColor()
    self:SetPortColor()
    return
  end

  self:UpdatePortOptions()

  local db = xb.db.profile
  --local iconSize = (xb:GetHeight() / 2)
  local iconSize = db.text.fontSize + db.general.barPadding

  self.hearthText:SetFont(xb:GetFont(db.text.fontSize))
  self.hearthText:SetText(GetBindLocation())

  self.hearthButton:SetSize(self.hearthText:GetWidth() + iconSize + db.general.barPadding, xb:GetHeight())
  self.hearthButton:SetPoint("RIGHT")

  self.hearthText:SetPoint("RIGHT")

  self.hearthIcon:SetTexture(xb.constants.mediaPath..'datatexts\\hearth')
  self.hearthIcon:SetSize(iconSize, iconSize)

  self.hearthIcon:SetPoint("RIGHT", self.hearthText, "LEFT", -(db.general.barPadding), 0)

  self:SetHearthColor()

  self.portText:SetFont(xb:GetFont(db.text.fontSize))
  self.portText:SetText(xb.db.char.portItem.text)

  self.portButton:SetSize(self.portText:GetWidth() + iconSize + db.general.barPadding, xb:GetHeight())
  self.portButton:SetPoint("LEFT", -(db.general.barPadding), 0)

  self.portText:SetPoint("RIGHT")

  self.portIcon:SetTexture(xb.constants.mediaPath..'datatexts\\garr')
  self.portIcon:SetSize(iconSize, iconSize)

  self.portIcon:SetPoint("RIGHT", self.portText, "LEFT", -(db.general.barPadding), 0)

  self:SetPortColor()

  self:CreatePortPopup()

  local popupPadding = xb.constants.popupPadding
  local popupPoint = 'BOTTOM'
  local relPoint = 'TOP'
  if db.general.barPosition == 'TOP' then
    popupPadding = -(popupPadding)
    popupPoint = 'TOP'
    relPoint = 'BOTTOM'
  end

  self.portPopup:ClearAllPoints()
  self.popupTexture:ClearAllPoints()

  self.portPopup:SetPoint(popupPoint, self.portButton, relPoint, 0, popupPadding)
  self.popupTexture:SetColorTexture(db.color.barColor.r, db.color.barColor.g, db.color.barColor.b, db.color.barColor.a)
  self.popupTexture:SetAllPoints()
  self.portPopup:Hide()

  local totalWidth = self.hearthButton:GetWidth() + db.general.barPadding
  self.portButton:Show()
  if self.portButton:IsVisible() then
    totalWidth = totalWidth + self.portButton:GetWidth()
  end
  self.hearthFrame:SetSize(totalWidth, xb:GetHeight())
  self.hearthFrame:SetPoint("RIGHT", -(db.general.barPadding), 0)
  self.hearthFrame:Show()
end

function TravelModule:FindFirstOption()
  local firstItem = {portId = 140192, text = GetMapNameByID(1014)}
  if self.portOptions then
    for k,v in pairs(self.portOptions) do
      if self:IsUsable(v.portId) then
        firstItem = v
        break
      end
    end
  end
  return firstItem
end

function TravelModule:IsUsable(id)
  return IsUsableItem(id) or IsPlayerSpell(id)
end

function TravelModule:GetDefaultOptions()
  local firstItem = self:FindFirstOption()
  xb.db.char.portItem = xb.db.char.portItem or firstItem
  return 'travel', {
    enabled = true
  }
end

function TravelModule:GetConfig()
  return {
    name = self:GetName(),
    type = "group",
    args = {
      enable = {
        name = ENABLE,
        order = 0,
        type = "toggle",
        get = function() return xb.db.profile.modules.travel.enabled; end,
        set = function(_, val)
          xb.db.profile.modules.travel.enabled = val
          if val then
            self:Enable()
          else
            self:Disable()
          end
        end,
        width = "full"
      }
    }
  }
end