
  --get the addon namespace
  local addon, ns = ...

  --get oUF namespace (just in case needed)
  local oUF = ns.oUF or oUF
  local LSM = LibStub("LibSharedMedia-3.0")

  --get the config
  local cfg = ns.cfg

  --object container
  local func = CreateFrame("Frame")
  ns.func = func
  RothUI = {}

  ---------------------------------------------
  -- VARIABLES
  ---------------------------------------------

  local tinsert, tremove, floor, mod, format = tinsert, tremove, floor, mod, format

  ---------------------------------------------
  -- FUNCTIONS
  ---------------------------------------------

local function GetSpecializationRole_MoPClassic()
    local _, class = UnitClass("player")
    local spec = GetPrimaryTalentTree()

    if class == "DRUID" then
        if spec == 1 then return "TANK"    -- Guardian
        elseif spec == 2 then return "HEALER" -- Restoration
        elseif spec == 3 then return "DAMAGER" -- Balance
        elseif spec == 4 then return "DAMAGER" -- Feral (Cat)
        end
    elseif class == "PALADIN" then
        if spec == 1 then return "HEALER"   -- Holy
        elseif spec == 2 then return "TANK" -- Protection
        elseif spec == 3 then return "DAMAGER" -- Retribution
        end
    elseif class == "PRIEST" then
        if spec == 1 or spec == 2 then return "HEALER" -- Disc/Holy
        elseif spec == 3 then return "DAMAGER" -- Shadow
        end
    elseif class == "WARRIOR" then
        if spec == 1 then return "DAMAGER" -- Arms
        elseif spec == 2 then return "DAMAGER" -- Fury
        elseif spec == 3 then return "TANK" -- Protection
        end
    end
    -- Add mappings for the other classes here

    return "DAMAGER" -- fallback
end

local function IsHealer()
    if rOrbs:CheckClientVersion(WOW_PROJECT_CLASSIC) then return true
    elseif rOrbs:CheckClientVersion(WOW_PROJECT_MISTS_CLASSIC) or rOrbs:CheckClientVersion(WOW_PROJECT_BURNING_CRUSADE_CLASSIC) then
        return GetSpecializationRole_MoPClassic(GetActiveTalentGroup()) == "HEALER"
    else
        return GetSpecializationRole(GetSpecialization()) == 'HEALER'
    end
end
        
    function rOrbs:canDispelDebuff(type)
  	local class, _, _ = UnitClass("player")
  	if (type == 'Magic' and IsHealer()) then
  		return true
  	elseif (type == 'Curse') then
  		return class == 'Druid' or class == 'Mage' or class == 'Shaman'
  	elseif (type == 'Disease') then
  		return class == 'Monk' or class == 'Paladin' or class == 'Priest'
  	elseif (type == 'Poison') then
  		return class == 'Druid' or class == 'Monk' or class == 'Paladin'
  	else
  		return false
  	end
    end
    
  --number format func
  func.numFormat = function(v)
    -- launder secret/tainted numbers through string conversion
    v = tonumber(format("%.0f", v or 0)) or 0
    if v > 1E10 then
      return (floor(v/1E9)).."b"
    elseif v > 1E9 then
      return (floor((v/1E9)*10)/10).."b"
    elseif v > 1E7 then
      return (floor(v/1E6)).."m"
    elseif v > 1E6 then
      return (floor((v/1E6)*10)/10).."m"
    elseif v > 1E4 then
      return (floor(v/1E3)).."k"
    elseif v > 1E3 then
      return (floor((v/1E3)*10)/10).."k"
    else
      return v
    end
  end

  --round number
  func.round = function(val)
    return floor(val*1000)/1000
  end

  --format time func
  func.GetFormattedTime = function(time)
    local hr, m, s, text
    if time <= 0 then text = ""
    elseif(time < 3600 and time > 60) then
      hr = floor(time / 3600)
      m = floor(mod(time, 3600) / 60 + 1)
      text = format("%dm", m)
    elseif time < 60 then
      m = floor(time / 60)
      s = mod(time, 60)
      text = (m == 0 and format("%ds", s))
    else
      hr = floor(time / 3600 + 1)
      text = format("%dh", hr)
    end
    return text
  end

  --backdrop func
  func.createBackdrop = function(f)
    f:SetBackdrop(cfg.backdrop)
    f:SetBackdropColor(0,0,0,0.7)
    f:SetBackdropBorderColor(0,0,0,1)
  end

local function NormalizeSpellList(list)
    if not list then return nil end

    local t = {}
    for _, spellId in ipairs(list) do
        t[spellId] = true
    end
    return t
end

  --create debuff func
  func.createDebuffs = function(self)
  if self.cfg.vertical then
	local f = CreateFrame("Frame", nil, self)
	f.disableCooldownText = self.cfg.auras.disableCooldownText
    f.size = self.cfg.auras.size
    if self.cfg.style == "targettarget" then
      f.num = 8
    else
      f.num = cfg.units.party.auras.number
    end
    f:SetHeight((f.size+5)*(f.num/4))
    f:SetWidth((f.size+5)*4)
	if self.cfg.style == "targettarget" then
		f:SetPoint("BOTTOM", self, "RIGHT", 0, 0)
	elseif self.cfg.style == "party" then
		f:SetPoint("BOTTOM", self.Health, "RIGHT", 22,-19)
	else
		f:SetPoint("TOP", self, "RIGHT", 50, -5)

	end
    f.initialAnchor = "TOPLEFT"
    f["growth-x"] = "RIGHT"
    f["growth-y"] = "DOWN"
    f.spacing = 5
    f.showDebuffType = self.cfg.auras.showDebuffType
    f.onlyShowPlayer = self.cfg.auras.onlyShowPlayerDebuffs
    f.onlyDispellable = self.cfg.auras.onlyDispellable
    f.whitelist = NormalizeSpellList(self.cfg.auras.whitelist or {})
    f.blacklist = NormalizeSpellList(self.cfg.auras.blacklist or {})
    self.Debuffs = f
  else
	local f = CreateFrame("Frame", nil, self)
	f.disableCooldownText = self.cfg.auras.disableCooldownText
    f.size = self.cfg.auras.size
    if self.cfg.style == "targettarget" then
      f.num = 8
    else
      f.num = cfg.units.party.auras.number
    end
    f:SetHeight((f.size+5)*(f.num/9))
    f:SetWidth((f.size+5)*4)
	if self.cfg.style == "targettarget" then
		f:SetPoint("BOTTOM", self, "RIGHT", -90, -40)
	else
		f:SetPoint("TOP", self, "RIGHT", -60, -67)
	end
    f.initialAnchor = "TOPLEFT"
    f["growth-x"] = "RIGHT"
    f["growth-y"] = "DOWN"
    f.spacing = 5
    f.showDebuffType = self.cfg.auras.showDebuffType
    f.onlyShowPlayer = self.cfg.auras.onlyShowPlayerDebuffs
    f.onlyDispellable = self.cfg.auras.onlyDispellable
    f.whitelist = NormalizeSpellList(self.cfg.auras.whitelist or {})
    f.blacklist = NormalizeSpellList(self.cfg.auras.blacklist or {})
    self.Debuffs = f
  end
  end

  --create buff func
  func.createBuffs = function(self)
  if self.cfg.auras.hideBuffs == true then return end
  if self.cfg.vertical == false then
    local f = CreateFrame("Frame", nil, self)
    f.size = self.cfg.auras.size
    if self.cfg.style == "targettarget" then
      f.num = 8
    else
      f.num = cfg.units.party.auras.number
    end
    f:SetHeight((f.size+5)*(f.num/9))
    f:SetWidth((f.size+5)*4)
    f:SetPoint("TOP", self, "RIGHT", -60, -27)
    f.initialAnchor = "TOPLEFT"
    f["growth-x"] = "RIGHT"
    f["growth-y"] = "DOWN"
    f.spacing = 5
    f.showBuffType = self.cfg.auras.showBuffType
    f.onlyShowPlayer = self.cfg.auras.onlyShowPlayerBuffs
    f.disableCooldownText = self.cfg.auras.disableCooldownText
    self.Buffs = f
  else
	local f = CreateFrame("Frame", nil, self)
    f.size = self.cfg.auras.size
    if self.cfg.style == "targettarget" then
      f.num = 8
    else
      f.num = cfg.units.party.auras.number
    end
    f:SetHeight((f.size+5)*(f.num/9))
    f:SetWidth((f.size+5)*9)
    f:SetPoint("TOP", self, "RIGHT", 117.5, 30)
    f.initialAnchor = "TOPLEFT"
    f["growth-x"] = "RIGHT"
    f["growth-y"] = "UP"
    f.spacing = 5
    f.showBuffType = self.cfg.auras.showBuffType
    f.onlyShowPlayer = self.cfg.auras.onlyShowPlayerBuffs
    f.disableCooldownText = self.cfg.auras.disableCooldownText
    self.Buffs = f
  end
  end
    
  func.checkColors = function(self, event, unit, full, auras)
      local hasDebuff = false
  
      local function handleAura(name, icon, _, debuffType, _, _, _, _, _, spellId, ...)
          if debuffType and rOrbs:canDispelDebuff(debuffType) then
              local color = DebuffTypeColor[debuffType] or DebuffTypeColor.none
              if color ~= DebuffTypeColor.none then
                  hasDebuff = true
                  self.Health.buffOverride = true
                  if self.Health.glow then
                      self.Health.glow:SetVertexColor(color.r, color.g, color.b)
                  end
              end
          end
      end
  
      if AuraUtil and AuraUtil.ForEachAura then
          -- Retail
          AuraUtil.ForEachAura(unit, "HARMFUL", nil, handleAura)
      else
          -- Classic fallback
          for i = 1, 40 do
              local name, icon, _, debuffType, _, _, _, _, _, spellId = UnitAura(unit, i, "HARMFUL")
              if not name then break end
              handleAura(name, icon, _, debuffType, _, _, _, _, _, spellId)
          end
      end

    -- reset if no debuff found
	if (not hasDebuff) then
		self.Health.buffOverride = false
		if (self.Health.glow) then
			self.Health.glow:SetVertexColor(0, 0, 0)
		end
	end
  end
  

	

  --Desaturated and Button CD
  func.postUpdateDebuff = function(element, unit, button, index, duration, expirationTime)
    if(UnitIsFriend("player", unit) or button.isPlayer) then
      button.icon:SetDesaturated(false)
      --button.cd:Show()
    else
      button.icon:SetDesaturated(true)
      --button.cd:Hide()
    end
    button.icon.duration = duration
    button.icon.timeLeft = expirationTime
    button.icon.first = true
  end

  --aura icon func
  func.createAuraIcon = function(icons, button)
    local font = LSM:Fetch("font", cfg.fonts.textFont)
    --button:SetSize(icons.size,icons.size)
    --button.cd:SetReverse()
    local size = icons.size or button:GetWidth()
	button.Cooldown:SetFrameStrata("MEDIUM")
    button.Cooldown:SetPoint("TOPLEFT", 1, -1)
    button.Cooldown:SetPoint("BOTTOMRIGHT", -1, 1)
    button.Icon:SetTexCoord(0.1, 0.9, 0.1, 0.9)
    --count helper frame, this push the count fontstring over the cooldown spiral
    button.countFrame = CreateFrame("Frame",nil,button)
    button.countFrame:SetAllPoints()
	button.countFrame:SetFrameStrata("MEDIUM")
    button.countFrame:SetFrameLevel(button.Cooldown:GetFrameLevel()+2)
    --button count
    button.Count:SetParent(button.countFrame)
    button.Count:ClearAllPoints()
    button.Count:SetPoint("TOPRIGHT", 4, 4)
    button.Count:SetTextColor(0.9,0.9,0.9)
    --fix fontsize to be based on button size
    button.Count:SetFont(font,size/1.8,"THINOUTLINE")
    button.Overlay:SetTexture(ns.mediapath.."gloss2")
    button.Overlay:SetTexCoord(0,1,0,1)
    button.Overlay:SetPoint("TOPLEFT", -1, 1)
    button.Overlay:SetPoint("BOTTOMRIGHT", 1, -1)
    button.Overlay:SetVertexColor(0.4,0.35,0.35,1)
    button.Overlay:Show()
    button.Overlay.Hide = function() end
    local back = button:CreateTexture(nil, "BACKGROUND", nil, 0)
    back:SetPoint("TOPLEFT",button.Icon,"TOPLEFT",-0.18*size,0.18*size)
    back:SetPoint("BOTTOMRIGHT",button.Icon,"BOTTOMRIGHT",0.18*size,-0.18*size)
    back:SetTexture(ns.mediapath.."simplesquare_glow")
    back:SetVertexColor(0, 0, 0, 1)
  end

    --create aura watch func
func.createAuraWatch = function(self)
    local auras = {}
    local spellIDs = {}
    
    if self.cfg.aurawatch.spellIds.ALL then
        for _, id in ipairs(self.cfg.aurawatch.spellIds.ALL) do
            table.insert(spellIDs, id)
        end
    end
    
    if self.cfg.aurawatch.spellIds[ns.player.class] then
        for _, id in ipairs(self.cfg.aurawatch.spellIds[ns.player.class]) do
            table.insert(spellIDs, id)
        end
    end
    
    auras.onlyShowPresent = self.cfg.aurawatch.onlyShowPresent
    auras.presentAlpha = self.cfg.aurawatch.alpha
        
    auras.PostCreateIcon = function(self, icon, sid)
        if icon.cd then
            icon.cd:SetPoint("TOPLEFT", 1, -1)
            icon.cd:SetPoint("BOTTOMRIGHT", -1, 1)
        end
        --count hack for lifebloom
        if sid == 33763 and icon.count then
            icon.count:SetFont("Interface\\AddOns\\rOrbs\\media\\visitor1.ttf",8,"THINOUTLINE, MONOCHROME")
            icon.count:ClearAllPoints()
            icon.count:SetPoint("CENTER", 3, 3)
            icon.count:SetParent(icon.cd)
        end
    end
        
    -- Set any other AuraWatch settings
    auras.icons = {}
    for i, sid in pairs(spellIDs) do
        local icon = CreateFrame("Frame", nil, self)
        icon:SetFrameStrata("TOOLTIP")
        icon.spellID = sid
                    
        local setup = {size = self.cfg.aurawatch.size, pos = self.cfg.aurawatch.pos.a1, x = self.cfg.aurawatch.pos.x * i, y = self.cfg.aurawatch.pos.y}
        -- set the dimensions and positions
        icon.size = setup.size
        icon:SetSize(setup.size,setup.size)
        --position icon
        icon:SetPoint("CENTER", self, self.cfg.aurawatch.pos.a1, self.cfg.aurawatch.pos.x + ((i-1) * self.cfg.aurawatch.size), self.cfg.aurawatch.pos.y)
        --make indicator
        --if dir[i].indicator then
        local tex = icon:CreateTexture(nil, "BACKGROUND", nil, 1)
            tex:SetAllPoints()
            tex:SetTexture(C_Spell.GetSpellTexture(sid))
            --tex:SetVertexColor(dir[i].color.r,dir[i].color.g,dir[i].color.b)
            icon.icon = tex
        
            auras.icons[sid] = icon
    -- Set any other AuraWatch icon settings
    end
    --call aurawatch
    self.AuraWatch = auras
end


  --update health func
  func.updateHealth = function(bar, unit, min, max)
  
    local d = floor(min/max*100)
    local color
    local dead
	local offline

    if unit and UnitIsTapDenied(unit) then
      color = {r = 0.65, g = 0.65, b = 0.65}
    elseif UnitIsDeadOrGhost(unit) then
      color = {r = 0.4, g = 0.4, b = 0.4}
      dead = 1
	elseif not UnitIsConnected(unit) then
		color = {r = 0.4, g = 0.4, b = 0.4}
		offline = 1
    elseif not cfg.colorswitcher.classcolored then
      color = cfg.colorswitcher.bright
    --elseif cfg.colorswitcher.threatColored and unit and UnitThreatSituation(unit) == 3 then
      --color = { r = 1, g = 0, b = 0, }
    elseif UnitIsPlayer(unit) then
      color = RAID_CLASS_COLORS[select(2, UnitClass(unit))]
    else
      color = FACTION_BAR_COLORS[UnitReaction(unit, "player")]
    end
    if not color then color = { r = 0.5, g = 0.5, b = 0.5, } end
    --dead
    if offline == 1 then
		bar:SetStatusBarColor(0.4, 0.4, 0.4, 0.4)
		bar.glow:SetVertexColor(0, 0, 0, 0)
    else
      --alive
      if cfg.colorswitcher.useBrightForeground then
        bar:SetStatusBarColor(color.r,color.g,color.b,color.a or 1)
        bar.bg:SetVertexColor(cfg.colorswitcher.dark.r,cfg.colorswitcher.dark.g,cfg.colorswitcher.dark.b,cfg.colorswitcher.dark.a)
      else
        bar:SetStatusBarColor(cfg.colorswitcher.dark.r,cfg.colorswitcher.dark.g,cfg.colorswitcher.dark.b,cfg.colorswitcher.dark.a)
        bar.bg:SetVertexColor(color.r,color.g,color.b,color.a or 1)
      end
    end
    --low hp
    if d <= 25 or dead == 1 then
      bar.highlight:SetAlpha(0)
      if cfg.colorswitcher.useBrightForeground then
        bar.glow:SetVertexColor(1,0,0,0.6)
        bar:SetStatusBarColor(1,0,0,1)
        bar.bg:SetVertexColor(0.2,0,0,0.9)
      else
        bar.glow:SetVertexColor(1,0,0,1)
      end
    elseif (not bar.buffOverride) then
      --inner shadow
        if (bar.glow) then
            bar.glow:SetVertexColor(0,0,0,0.7)
        end
        if (bar.highlight) then
            bar.highlight:SetAlpha(cfg.highlightMultiplier)
        end
    end
    --bar.highlight:SetAlpha((min/max)*cfg.highlightMultiplier)
  end

  --update power func
  func.updatePower = function(bar, unit, min, max)
    local color = cfg.powercolors[select(2, UnitPowerType(unit))]
    if not color then
      --prevent powertype from bugging out on certain encounters.
      color = {r=1,g=0.5,b=0.25}
    end
      bar:SetStatusBarColor(color.r, color.g, color.b,1) 
      bar.bg:SetVertexColor(color.r, color.g, color.b,0.2)
  end

  --debuffglow
  func.createDebuffGlow = function(self)
    local t = self:CreateTexture(nil,"BACKGROUND",nil,-5)
    if self.cfg.style == "target" then
      t:SetTexture(ns.mediapath.."target_debuffglow")
    else
      t:SetTexture(ns.mediapath.."targettarget_debuffglow")
    end
	if self.cfg.style == "party" then
		if cfg.units.party.vertical == true then
			t:SetPoint("TOP",0,29)
			t:SetPoint("LEFT",0,0)
			t:SetPoint("RIGHT",0,0)
			t:SetPoint("BOTTOM",0,-10)
		else
			t:SetPoint("TOP",0,19)
			t:SetPoint("LEFT",0,0)
			t:SetPoint("RIGHT",0,0)
			t:SetPoint("BOTTOM",0,-15)
		end
	elseif self.cfg.style == "target" then
		t:SetPoint("TOP",0,25)
		t:SetPoint("LEFT",-60,0)
		t:SetPoint("RIGHT",60,0)
		t:SetPoint("BOTTOM",0,-15)
	else
		t:SetAllPoints()
	end
    t:SetBlendMode("BLEND")
    t:SetVertexColor(0, 1, 1, 0) -- set alpha to 0 to hide the texture
    self.DebuffHighlight = t
    self.DebuffHighlightAlpha = 1
    self.DebuffHighlightFilter = false
  end

  --check threat
  func.checkThreat = function(self,event,unit)
    if unit then
      if self.unit ~= unit then return end
      local threat = UnitThreatSituation(unit)
      if(threat and threat > 0) then
        local r, g, b = GetThreatStatusColor(threat)
        if self.Border then
          self.Border:SetVertexColor(r,g,b)
          self.PortraitBack:SetVertexColor(r,g,b,1)
        end
      else
        if self.Border then
          self.Border:SetVertexColor(0.6,0.5,0.5)
          self.PortraitBack:SetVertexColor(0.1,0.1,0.1,0.9)
        end
      end
    end
  end

  --create portrait func
  func.createPortrait = function(self)
  

    local back = CreateFrame("Frame",nil,self)
    back:SetSize(self.cfg.width,self.cfg.width)
	
	if self.cfg.style == "party" then
		if cfg.units.party.vertical == false then
			back:SetPoint("BOTTOM", self, "TOP", 0, -35)
		else
			back:SetPoint("BOTTOM", self, "LEFT", 10, -38)
		end
	else 
		back:SetPoint("BOTTOM", self, "TOP", 0, -35)
	end
    self.PortraitHolder = back

    local t = back:CreateTexture(nil,"BACKGROUND",nil,-8)
    t:SetAllPoints(back)
    t:SetTexture(ns.mediapath.."portrait_back")
    t:SetVertexColor(0.1,0.1,0.1,0.9)
    self.PortraitBack = t

	if self.cfg.style == "party" then
		if self.cfg.portrait.use3D then
			self.Portrait = CreateFrame("PlayerModel", nil, back)
			self.Portrait:SetPoint("TOPLEFT",back,"TOPLEFT",22,-20)
			self.Portrait:SetPoint("BOTTOMRIGHT",back,"BOTTOMRIGHT",-19,21)

			local borderholder = CreateFrame("Frame", nil, self.Portrait)
				borderholder:SetAllPoints(back)
				self.BorderHolder = borderholder

			local border = borderholder:CreateTexture(nil,"BACKGROUND",nil,-6)
				border:SetAllPoints(borderholder)
				border:SetTexture(ns.mediapath.."portrait_border")
				border:SetVertexColor(0.6,0.5,0.5)
				--border:SetVertexColor(1,0,0,1) --threat test
				self.Border = border

			local gloss = borderholder:CreateTexture(nil,"BACKGROUND",nil,-5)
				gloss:SetAllPoints(borderholder)
				gloss:SetTexture(ns.mediapath.."portrait_gloss")
				gloss:SetVertexColor(0.9,0.95,1,0.6)

		else
			self.Portrait = back:CreateTexture(nil,"BACKGROUND",nil,-7)
			self.Portrait:SetPoint("TOPLEFT",back,"TOPLEFT",21,-21)
			self.Portrait:SetPoint("BOTTOMRIGHT",back,"BOTTOMRIGHT",-21,21)
			self.Portrait:SetTexCoord(0.15,0.85,0.15,0.85)

			local border = back:CreateTexture(nil,"BACKGROUND",nil,-6)
				border:SetAllPoints(back)
				border:SetTexture(ns.mediapath.."portrait_border")
				border:SetVertexColor(0.6,0.5,0.5)
				self.Border = border

			local gloss = back:CreateTexture(nil,"BACKGROUND",nil,-5)
				gloss:SetAllPoints(back)
				gloss:SetTexture(ns.mediapath.."portrait_gloss")
				gloss:SetVertexColor(0.9,0.95,1,0.6)

		end
	else
		if self.cfg.portrait.use3D then
			self.Portrait = CreateFrame("PlayerModel", nil, back)
			self.Portrait:SetPoint("TOPLEFT",back,"TOPLEFT",27,-27)
			self.Portrait:SetPoint("BOTTOMRIGHT",back,"BOTTOMRIGHT",-27,27)

			local borderholder = CreateFrame("Frame", nil, self.Portrait)
				borderholder:SetAllPoints(back)
				self.BorderHolder = borderholder

			local border = borderholder:CreateTexture(nil,"BACKGROUND",nil,-6)
				border:SetAllPoints(borderholder)
				border:SetTexture(ns.mediapath.."portrait_border")
				border:SetVertexColor(0.6,0.5,0.5)
				--border:SetVertexColor(1,0,0,1) --threat test
				self.Border = border

			local gloss = borderholder:CreateTexture(nil,"BACKGROUND",nil,-5)
				gloss:SetAllPoints(borderholder)
				gloss:SetTexture(ns.mediapath.."portrait_gloss")
				gloss:SetVertexColor(0.9,0.95,1,0.6)

		else
			self.Portrait = back:CreateTexture(nil,"BACKGROUND",nil,-7)
			self.Portrait:SetPoint("TOPLEFT",back,"TOPLEFT",27,-27)
			self.Portrait:SetPoint("BOTTOMRIGHT",back,"BOTTOMRIGHT",-27,27)
			self.Portrait:SetTexCoord(0.15,0.85,0.15,0.85)

			local border = back:CreateTexture(nil,"BACKGROUND",nil,-6)
				border:SetAllPoints(back)
				border:SetTexture(ns.mediapath.."portrait_border")
				border:SetVertexColor(0.6,0.5,0.5)
				self.Border = border

			local gloss = back:CreateTexture(nil,"BACKGROUND",nil,-5)
				gloss:SetAllPoints(back)
				gloss:SetTexture(ns.mediapath.."portrait_gloss")
				gloss:SetVertexColor(0.9,0.95,1,0.6)

		end	
	end
		
	if self.cfg.vertical == true then
    self.Name:SetPoint("CENTER", 0, 0)
	else
	self.Name:SetPoint("BOTTOM", self, "TOP", 0, self.cfg.width-53)
	end

  end

  --create standalone portrait func
  func.createStandAlonePortrait = function(self)

    local fname
    if self.cfg.style == "player" then
      fname = "rOrbsPlayerPortrait"
    elseif self.cfg.style == "target" then
      fname = "rOrbsTargetPortrait"
    end

    local pcfg = self.cfg.portrait

    local back = CreateFrame("Frame",fname,self)
    back:SetSize(pcfg.size,pcfg.size)
    back:SetPoint(pcfg.pos.a1,pcfg.pos.af,pcfg.pos.a2,pcfg.pos.x,pcfg.pos.y)

    func.applyDragFunctionality(back)

    local t = back:CreateTexture(nil,"BACKGROUND",nil,-8)
    t:SetAllPoints(back)
    t:SetTexture(ns.mediapath.."portrait_back")
    t:SetVertexColor(0.1,0.1,0.1,0.9)

    if pcfg.use3D then
      self.Portrait = CreateFrame("PlayerModel", nil, back)
      self.Portrait:SetPoint("TOPLEFT",back,"TOPLEFT",pcfg.size*27/128,-pcfg.size*27/128)
      self.Portrait:SetPoint("BOTTOMRIGHT",back,"BOTTOMRIGHT",-pcfg.size*27/128,pcfg.size*27/128)

      local borderholder = CreateFrame("Frame", nil, self.Portrait)
      borderholder:SetAllPoints(back)

      local border = borderholder:CreateTexture(nil,"BACKGROUND",nil,-6)
      border:SetAllPoints(borderholder)
      border:SetTexture(ns.mediapath.."portrait_border")
      border:SetVertexColor(0.6,0.5,0.5)

      local gloss = borderholder:CreateTexture(nil,"BACKGROUND",nil,-5)
      gloss:SetAllPoints(borderholder)
      gloss:SetTexture(ns.mediapath.."portrait_gloss")
      gloss:SetVertexColor(0.9,0.95,1,0.6)

    else
      self.Portrait = back:CreateTexture(nil,"BACKGROUND",nil,-7)
      self.Portrait:SetPoint("TOPLEFT",back,"TOPLEFT",pcfg.size*27/128,-pcfg.size*27/128)
      self.Portrait:SetPoint("BOTTOMRIGHT",back,"BOTTOMRIGHT",-pcfg.size*27/128,pcfg.size*27/128)
      self.Portrait:SetTexCoord(0.15,0.85,0.15,0.85)

      local border = back:CreateTexture(nil,"BACKGROUND",nil,-6)
      border:SetAllPoints(back)
      border:SetTexture(ns.mediapath.."portrait_border")
      border:SetVertexColor(0.6,0.5,0.5)

      local gloss = back:CreateTexture(nil,"BACKGROUND",nil,-5)
      gloss:SetAllPoints(back)
      gloss:SetTexture(ns.mediapath.."portrait_gloss")
      gloss:SetVertexColor(0.9,0.95,1,0.6)

    end

  end

  --create castbar func
  func.createCastbar = function(f)
	local frame = CreateFrame("Frame", "$parentCastBarDragFrame", f)
	frame:SetPoint(f.cfg.castbar.pos.a1, f.cfg.castbar.pos.af, f.cfg.castbar.pos.a2, f.cfg.castbar.pos.x+8, f.cfg.castbar.pos.y)
	frame:SetSize(100,10)
    local c = CreateFrame("StatusBar", "$parentCastbar", frame)
    --wow is this outdated...man I really need to rewrite how the drag stuff is handled
    tinsert(rOrbs_Bars,frame:GetName())
    c:SetSize(265,15)
    c:SetStatusBarTexture(f.cfg.castbar.texture)
    c:SetScale(f.cfg.castbar.scale)
    c:SetPoint("CENTER", frame, 0,0)
	c:SetFrameStrata("HIGH")
    c:SetStatusBarColor(f.cfg.castbar.color.bar.r,f.cfg.castbar.color.bar.g,f.cfg.castbar.color.bar.b,f.cfg.castbar.color.bar.a)
    --c:SetStatusBarColor(0,0,0,1)

    c.background = c:CreateTexture(nil,"OVERLAY",nil,-8)
    c.background:SetTexture(ns.mediapath.."castbar")
    c.background:SetPoint("TOP",0,24.9)
    c.background:SetPoint("LEFT",-170,0)
    c.background:SetPoint("RIGHT",170,0)
    c.background:SetPoint("BOTTOM",0,-24.2)

    c.bg = c:CreateTexture(nil,"BACKGROUND",nil,-6)
    c.bg:SetTexture(f.cfg.castbar.texture)
    c.bg:SetAllPoints(c)
    c.bg:SetVertexColor(f.cfg.castbar.color.bg.r,f.cfg.castbar.color.bg.g,f.cfg.castbar.color.bg.b,f.cfg.castbar.color.bg.a)

    c.Text =  func.createFontString(c, cfg.fonts.textFont, f.cfg.castbar.fontSize, "THINOUTLINE")
    c.Text:SetPoint("LEFT", 5, 0)
    c.Text:SetJustifyH("LEFT")

    c.Time =  func.createFontString(c, cfg.fonts.textFont, f.cfg.castbar.fontSize, "THINOUTLINE")
    c.Time:SetPoint("RIGHT", -2, 0)

    c.Text:SetPoint("RIGHT", -50, 0)
    --c.Text:SetPoint("RIGHT", c.Time, "LEFT", -10, 0) --right point of text will anchor left point of time


    c.Spark = c:CreateTexture(nil,"OVERLAY",nil,-7)
    c.Spark:SetBlendMode("ADD")
    c.Spark:SetVertexColor(0.8,0.6,0,1)

    c.glow = c:CreateTexture(nil, "OVERLAY",nil,-4)
    c.glow:SetTexture(ns.mediapath.."castbar_glow")
    c.glow:SetPoint("TOP",0,26.5)
    c.glow:SetPoint("LEFT",-35,0)
    c.glow:SetPoint("RIGHT",35,0)
    c.glow:SetPoint("BOTTOM",0,-24.2)
    c.glow:SetVertexColor(0,0,0,1)

    c.highlight = c:CreateTexture(nil,"OVERLAY",nil,-3)
    c.highlight:SetTexture(ns.mediapath.."castbar_highlight")
    c.highlight:SetPoint("TOP",0,26.5)
    c.highlight:SetPoint("LEFT",-35,0)
    c.highlight:SetPoint("RIGHT",35,0)
    c.highlight:SetPoint("BOTTOM",0,-24.2)
	

    if f.cfg.style == "target" then
      c.Shield = c:CreateTexture(nil,"BACKGROUND",nil,-8)
      c.Shield:SetColorTexture(0,0,0,0)
    end

    --safezone
    if f.cfg.style == "player" and f.cfg.castbar.latency then
      c.SafeZone = c:CreateTexture(nil,"OVERLAY")
      c.SafeZone:SetTexture(f.cfg.castbar.texture)
      c.SafeZone:SetVertexColor(0.6,0,0,0.6)
      c.SafeZone:SetPoint("TOPRIGHT")
      c.SafeZone:SetPoint("BOTTOMRIGHT")
    end

    if f.cfg.style == "player" then
      c.PostUpdateStage = func.PostUpdateStage
      c.cfg = f.cfg
    end
    func.applyDragFunctionality(frame)

    f.Castbar = c

  end

  func.PostUpdateStage = function(self, stage)
    if (stage == 1) then
      self:SetStatusBarColor(1, 0, 0)
    elseif (stage == 2) then
      self:SetStatusBarColor(0, 0, 1)
    elseif (stage == 3) then
      self:SetStatusBarColor(self.cfg.castbar.color.bar.r,self.cfg.castbar.color.bar.g,self.cfg.castbar.color.bar.b,self.cfg.castbar.color.bar.a)
    end
  end
  
  --fontstring func
  func.createFontString = function(f, font, size, outline,layer)
    local fs = f:CreateFontString(nil, layer or "OVERLAY")
    local fontPath = LSM:Fetch("font",font)
    fs:SetFont(fontPath, size, outline)
    fs:SetShadowColor(0,0,0,1)
    return fs
  end

local function CheckFont()
	if (rOrbs.db == nil) then 
		return false
	elseif (LSM:Fetch("font", rOrbs.db.profile.chatFont) == nil) then
		return false
	else
		return true
	end
end

  --allows frames to become movable but frames can be locked or set to default positions
  func.applyDragFunctionality = function(f,special)
    --save the default position
    local getPoint = function(self)
      local pos = {}
      pos.a1, pos.af, pos.a2, pos.x, pos.y = self:GetPoint()
      if pos.af and pos.af:GetName() then pos.af = pos.af:GetName() end
      return pos
    end
    f.defaultPosition = getPoint(f)
    
    local savedScale = nil
    if rOrbsDB and rOrbsDB.frameScales then
      local frameName = f:GetName()
      if frameName and rOrbsDB.frameScales[frameName] then
        savedScale = rOrbsDB.frameScales[frameName]
      end
    end
    
    --new form of dragframe
    local df = CreateFrame("Frame",nil,f)
    df:SetAllPoints(f)
    df:SetFrameStrata("HIGH")
    df:EnableMouse(false)
    
    local isScaling = false
    local isMoving = false
    local startScale = 1
    local startX, startY = 0, 0

    -- ----------------------------------------------------------------
    -- Scale label: zeigt aktuellen Scale-Wert in der Mitte des Orbs
    -- Erstellt auf df (HIGH strata) damit es über allem sichtbar ist
    -- ----------------------------------------------------------------
    local scaleLabel = df:CreateFontString(nil, "OVERLAY")
    scaleLabel:SetFont("Fonts\\FRIZQT__.TTF", 18, "OUTLINE")
    scaleLabel:SetPoint("CENTER", df, "CENTER", 0, 0)
    scaleLabel:SetTextColor(1, 1, 0.3, 1)
    scaleLabel:SetText("")
    scaleLabel:SetAlpha(0)
    df:EnableMouse(false)  -- reset after CreateFontString

    local function ShowScaleLabel(scaleVal)
      scaleLabel:SetText(string.format("%.2fx", scaleVal))
      scaleLabel:SetAlpha(1)
    end

    local function HideScaleLabel()
      local elapsed = 0
      local totalTime = 0.6
      local fadeFrame = CreateFrame("Frame")
      fadeFrame:SetScript("OnUpdate", function(self, dt)
        elapsed = elapsed + dt
        local alpha = math.max(0, 1 - (elapsed / totalTime))
        scaleLabel:SetAlpha(alpha)
        if elapsed >= totalTime then
          scaleLabel:SetAlpha(0)
          scaleLabel:SetText("")
          self:SetScript("OnUpdate", nil)
          self:Hide()
        end
      end)
      fadeFrame:Show()
    end

    -- ----------------------------------------------------------------
    -- Hilfsfunktion: Model nach Scale/Move neu berechnen
    -- f._orbType wird von engine:Apply() gesetzt (nach frame creation)
    -- ----------------------------------------------------------------
    local function RefreshOrbModel()
      if f._orbType and ns and ns.OrbTemplateEngine then
        ns.OrbTemplateEngine:Refresh(f._orbType)
      end
    end

    -- ----------------------------------------------------------------
    -- Grid: zeigt Alignment-Grid während des Verschiebens
    -- ----------------------------------------------------------------
    local dragGrid = nil

    local function ShowGrid()
      if dragGrid then return end
      dragGrid = CreateFrame("Frame", nil, UIParent)
      dragGrid:SetAllPoints(UIParent)
      dragGrid:SetFrameStrata("BACKGROUND")
      local scale = 64
      local screenW = GetScreenWidth()
      local screenH = GetScreenHeight()
      local numV = scale
      local numH = math.floor(screenH / screenW * scale)
      local offV = screenW / numV
      local offH = screenH / numH
      for i = 1, numV - 1 do
        local line = dragGrid:CreateLine()
        line:SetThickness(1)
        line:SetColorTexture(1, 1, 1, 0.3)
        line:SetStartPoint("TOPLEFT",   i * offV, 0)
        line:SetEndPoint("BOTTOMLEFT",  i * offV, 0)
      end
      for i = 1, numH - 1 do
        local line = dragGrid:CreateLine()
        line:SetThickness(1)
        line:SetColorTexture(1, 1, 1, 0.3)
        line:SetStartPoint("BOTTOMLEFT", 0, i * offH)
        line:SetEndPoint("BOTTOMRIGHT",  0, i * offH)
      end
      dragGrid:Show()
    end

    local function HideGrid()
      if dragGrid then
        dragGrid:Hide()
        dragGrid = nil
      end
    end

    -- Enable mouse only when modifier keys are pressed (for dragging/scaling)
    df:SetScript("OnUpdate", function(self)
      local shouldBeActive = IsShiftKeyDown() or IsAltKeyDown()
      if shouldBeActive and not self:IsMouseEnabled() then
        self:EnableMouse(true)
      elseif not shouldBeActive and self:IsMouseEnabled() and not isScaling and not isMoving then
        self:EnableMouse(false)
      end
      
      -- Handle scaling: update scale + model + label live
      if isScaling then
        local x, y = GetCursorPosition()
        local diffY = (y - startY) / 100
        local newScale = math.max(0.5, math.min(3.0, startScale + diffY))
        f:SetScale(newScale)
        -- Modell-Positionen neu berechnen (ApplyParams nutzt orb:GetScale())
        RefreshOrbModel()
        ShowScaleLabel(newScale)
      end
    end)
    
    df:SetScript("OnMouseDown", function(self, button)
      if button == "LeftButton" then
        if IsShiftKeyDown() then
          isMoving = true
          ShowGrid()
          f:StartMoving()
        elseif IsAltKeyDown() then
          isScaling = true
          startScale = f:GetScale()
          startX, startY = GetCursorPosition()
          ShowScaleLabel(startScale)
        end
      end
    end)
    
    df:SetScript("OnMouseUp", function(self, button)
      if button == "LeftButton" then
        f:StopMovingOrSizing()
        
        if isScaling then
          if not rOrbsDB then rOrbsDB = {} end
          if not rOrbsDB.frameScales then rOrbsDB.frameScales = {} end
          local frameName = f:GetName()
          if frameName then
            rOrbsDB.frameScales[frameName] = f:GetScale()
          end
          -- Finales Model-Refresh nach Skalierung
          RefreshOrbModel()
          isScaling = false
          HideScaleLabel()
        elseif isMoving then
          HideGrid()
          -- Model-Refresh nach Verschieben (frame-relative Positionen ändern sich)
          RefreshOrbModel()
          if not rOrbsDB then rOrbsDB = {} end
          if not rOrbsDB.framePositions then rOrbsDB.framePositions = {} end
          local frameName = f:GetName()
          if frameName then
            local point, relativeTo, relativePoint, x, y = f:GetPoint()
            rOrbsDB.framePositions[frameName] = {
              point = point,
              relativeTo = relativeTo and relativeTo:GetName() or "UIParent",
              relativePoint = relativePoint,
              x = x,
              y = y
            }
          end
          isMoving = false
        end
      end
    end)
    
    local t = df:CreateTexture(nil,"OVERLAY",nil,6)
    t:SetAllPoints(df)
    t:SetColorTexture(0,1,0)
    t:SetAlpha(0.2)
    df.texture = t
    f.dragframe = df
    f.dragframe:Hide()
    if not special then
      f:SetClampedToScreen(true)
    end
    
    local config = ns.cfg or cfg
    
    if not config.framesUserplaced then
      f:SetMovable(false)
    else
      f:SetMovable(true)
      f:SetUserPlaced(true)
      
      f.dragframe:Show()
      f.dragframe:SetScript("OnEnter", function(s)
        local parent = s:GetParent()
        local name = parent:GetName()
        
        if name == "rOrbsHealthOrb" then
          UnitFrame_OnEnter(parent)
        elseif name == "rOrbsTargetHealthOrb" and UnitExists("target") then
          GameTooltip:SetOwner(parent, "ANCHOR_RIGHT")
          GameTooltip:SetUnit("target")
          GameTooltip:Show()
        elseif name == "rOrbsTargetFrame" and UnitExists("target") then
          GameTooltip:SetOwner(parent, "ANCHOR_RIGHT")
          GameTooltip:SetUnit("target")
          GameTooltip:Show()
        end
        
        if IsShiftKeyDown() or IsAltKeyDown() then
          GameTooltip:SetOwner(s, "ANCHOR_TOP")
          GameTooltip:AddLine(parent:GetName(), 0, 1, 0.5, 1, 1, 1)
          GameTooltip:AddLine("SHIFT + Click & Drag = Move", 1, 1, 1, 1, 1, 1)
          GameTooltip:AddLine("ALT + Click & Drag = Scale", 1, 1, 1, 1, 1, 1)
          GameTooltip:Show()
        end
      end)
      f.dragframe:SetScript("OnLeave", function(s) 
        local parent = s:GetParent()
        local name = parent:GetName()
        if name == "rOrbsHealthOrb" then
          UnitFrame_OnLeave(parent)
        end
        GameTooltip:Hide() 
      end)
      
      if config.framesLocked then
        df.texture:Hide()
      else
        df.texture:Show()
      end
    end
  end

  --simple frame movement
  func.simpleDragFunc = function(f)

    f:SetHitRectInsets(-15,-15,-15,-15)
    f:SetClampedToScreen(true)
    f:SetMovable(true)
    f:SetUserPlaced(true)

    f:EnableMouse(true)

    f:RegisterForDrag("LeftButton")
    --[[
    f:SetScript("OnEnter", function(s)
      GameTooltip:SetOwner(s, "ANCHOR_CURSOR")
      GameTooltip:AddLine(s:GetName(), 0, 1, 0.5, 1, 1, 1)
      GameTooltip:AddLine("Hold down ALT+SHIFT to drag!", 1, 1, 1, 1, 1, 1)
      GameTooltip:Show()
    end)
    f:SetScript("OnLeave", function(s) GameTooltip:Hide() end)
    ]]--
    --f:SetScript("OnDragStart", function(s) if IsAltKeyDown() and IsShiftKeyDown() then s:StartMoving() end end)
    f:SetScript("OnDragStart", function(s) s:StartMoving() end)
    f:SetScript("OnDragStop", function(s) s:StopMovingOrSizing() end)

  end

  --create icon func
  func.createIcon = function(f,layer,size,anchorframe,anchorpoint1,anchorpoint2,posx,posy,sublevel)
    local icon = f:CreateTexture(nil,layer,nil,sublevel)
    icon:SetSize(size,size)
    icon:SetPoint(anchorpoint1,anchorframe,anchorpoint2,posx,posy)
    return icon
  end

  --heal prediction
  func.healPrediction = function(self)
    if not self.cfg.healprediction or (self.cfg.healprediction and not self.cfg.healprediction.show) or rOrbs:CheckClientVersion(WOW_PROJECT_CLASSIC) then return end
    local w = self.Health:GetWidth()
    if w == 0 then
      w = self:GetWidth()-24.5-24.5 --raids and party have no width on the health frame for whatever reason, thus use self and subtract the setpoint values
    end
    -- my heals
    local mhpb = CreateFrame("StatusBar", nil, self.Health)
    mhpb:SetFrameLevel(self.Health:GetFrameLevel())
    mhpb:SetPoint("TOPLEFT", self.Health, "TOPRIGHT", 0, 0)
    mhpb:SetPoint("BOTTOMLEFT", self.Health, "BOTTOMRIGHT", 0, 0)
    mhpb:SetWidth(w)
    mhpb:SetStatusBarTexture(self.cfg.healprediction.texture)
    mhpb:SetStatusBarColor(self.cfg.healprediction.color.myself.r,self.cfg.healprediction.color.myself.g,self.cfg.healprediction.color.myself.b,self.cfg.healprediction.color.myself.a)
    -- other heals
    local ohpb = CreateFrame("StatusBar", nil, self.Health)
    ohpb:SetFrameLevel(self.Health:GetFrameLevel())
    ohpb:SetPoint("TOPLEFT", mhpb:GetStatusBarTexture(), "TOPRIGHT", 0, 0)
    ohpb:SetPoint("BOTTOMLEFT", mhpb:GetStatusBarTexture(), "BOTTOMRIGHT", 0, 0)
    ohpb:SetWidth(w)
    ohpb:SetStatusBarTexture(self.cfg.healprediction.texture)
    ohpb:SetStatusBarColor(self.cfg.healprediction.color.other.r,self.cfg.healprediction.color.other.g,self.cfg.healprediction.color.other.b,self.cfg.healprediction.color.other.a)
    -- Register it with oUF
    self.HealthPrediction = {
      myBar = mhpb,
      otherBar = ohpb,
      maxOverflow = self.cfg.healprediction.maxoverflow,
    }
  end

  --total absorb
  func.totalAbsorb = function(self)
    if not self.cfg.totalabsorb or (self.cfg.totalabsorb and not self.cfg.totalabsorb.show) or rOrbs:CheckClientVersion(WOW_PROJECT_CLASSIC) then return end
	
    local w = self.Health:GetWidth()
	
	if self.cfg.style == "party" then
		if cfg.units.party.verical == false then
			if w == 0 then
				w = self:GetWidth()-24.5-24.5 --raids and party have no width on the health frame for whatever reason, thus use self and subtract the setpoint values
			end
		else
			if w == 0 then
				w = self:GetWidth()-60-24.5
			end
		end
	else
		if w == 0 then
			w = self:GetWidth()-24.5-24.5 --raids and party have no width on the health frame for whatever reason, thus use self and subtract the setpoint values
		end
	end
    local absorbBar = CreateFrame("StatusBar", nil, self.Health)
    --new anchorpoint, absorb will now overlay the healthbar from right to left
    absorbBar:SetFrameLevel(self.Health:GetFrameLevel()+1)
    absorbBar:SetPoint("TOPRIGHT", self.Health, 0, 0)
    absorbBar:SetPoint("BOTTOMRIGHT", self.Health, 0, 0)
    absorbBar:SetWidth(w)
    absorbBar:SetStatusBarTexture(self.cfg.totalabsorb.texture)
    absorbBar:SetStatusBarColor(self.cfg.totalabsorb.color.bar.r,self.cfg.totalabsorb.color.bar.g,self.cfg.totalabsorb.color.bar.b,self.cfg.totalabsorb.color.bar.a)    
    absorbBar:SetReverseFill(true)
    -- Register with oUF
    self.TotalAbsorb = absorbBar    
  end
  
--Register LSM media
LSM:Register("border", "RB border", "Interface\\AddOns\\rOrbs\\media\\5.tga")
LSM:Register("statusbar", "Solid", "Interface\\AddOns\\rOrbs\\media\\Solid.tga")
LSM:Register("statusbar", "Roth_Statusbar1", "Interface\\AddOns\\rOrbs\\media\\statusbar")
LSM:Register("statusbar", "Roth_Statusbar2", "Interface\\AddOns\\rOrbs\\media\\statusbar2")
LSM:Register("statusbar", "Roth_Statusbar3", "Interface\\AddOns\\rOrbs\\media\\statusbar3")
LSM:Register("statusbar", "Roth_Statusbar4", "Interface\\AddOns\\rOrbs\\media\\statusbar4")
LSM:Register("statusbar", "Roth_Statusbar5", "Interface\\AddOns\\rOrbs\\media\\statusbar5")
LSM:Register("statusbar", "Roth_Statusbar6", "Interface\\AddOns\\rOrbs\\media\\statusbar128")
LSM:Register("statusbar", "Roth_Statusbar7", "Interface\\AddOns\\rOrbs\\media\\statusbar128_3")
LSM:Register("statusbar", "Roth_Statusbar8", "Interface\\AddOns\\rOrbs\\media\\statusbar256")
LSM:Register("statusbar", "Roth_Statusbar9", "Interface\\AddOns\\rOrbs\\media\\statusbar256_2")
LSM:Register("statusbar", "Roth_Statusbar10", "Interface\\AddOns\\rOrbs\\media\\statusbar256_3")
LSM:Register("background", "Solid", "Interface\\AddOns\\rOrbs\\media\\Solid.tga")
LSM:Register("font", "Cracked", "Interface\\AddOns\\rOrbs\\media\\Cracked-Narrow.ttf")
LSM:Register("font", "Expressway", "Interface\\AddOns\\rOrbs\\media\\Expressway.ttf")
