-- Initialize pfUI core table for non-pfUI environments
if not pfUI then
  pfUI = {
    ["api"] = {},
    ["cache"] = {},
    ["backdrop"] = {
      bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
      edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
      tile = true, tileSize = 16, edgeSize = 16,
      insets = { left = 3, right = 3, top = 3, bottom = 3 }
    },
    ["backdrop_small"] = {
      bgFile = "Interface\\BUTTONS\\WHITE8X8", tile = false, tileSize = 0,
      edgeFile = "Interface\\BUTTONS\\WHITE8X8", edgeSize = 1,
      insets = {left = 0, right = 0, top = 0, bottom = 0},
    },
    ["font_default"] = STANDARD_TEXT_FONT,
   }

  pfUI_config = {
    ["appearance"] = {
      ["border"] = {
        ["default"] = "3",
      }
    },
    ["global"] = {
      ["font_size"] = 12
    },
    -- fix for old questie releases
    ["disabled"] = {
      ["minimap"] = "1"
    }
  }

  pfUI.api.emulated = true
end

pfUI.api.SetButtonFont = pfUI.api.SetButtonFont or function(button, font, size, flags)
  if button.SetFont then
    -- vanilla + tbc
    button:SetFont(font, size, flags)
  else
    -- wotlk
    local buttonText = button:GetFontString()
    if buttonText then
      buttonText:SetFont(font, size, flags)
    else
      -- create new font string if not existing
      buttonText = button:CreateFontString(nil, "OVERLAY")
      buttonText:SetFont(font, size, flags)
      buttonText:SetPoint("CENTER", button, "CENTER")
      button:SetFontString(buttonText)
    end
  end
end

pfUI.api.SetButtonFontColor = pfUI.api.SetButtonFontColor or function(button, r, g, b, a)
  if button.SetTextColor then
    -- vanilla + tbc
    button:SetTextColor(r, g, b, a)
  else
    -- wotlk
    local buttonText = button:GetFontString()
    if buttonText then buttonText:SetTextColor(r, g, b, a) end
  end
end


pfUI.api.strsplit = pfUI.api.strsplit or function(delimiter, subject)
  if not subject then return nil end
  local delimiter, fields = delimiter or ":", {}
  local pattern = string.format("([^%s]+)", delimiter)
  string.gsub(subject, pattern, function(c) fields[table.getn(fields)+1] = c end)
  return unpack(fields)
end

local sanitize_cache = {}
pfUI.api.SanitizePattern = pfUI.api.SanitizePattern or function(pattern, dbg)
  if not sanitize_cache[pattern] then
    local ret = pattern
    -- escape magic characters
    ret = gsub(ret, "([%+%-%*%(%)%?%[%]%^])", "%%%1")
    -- remove capture indexes
    ret = gsub(ret, "%d%$","")
    -- catch all characters
    ret = gsub(ret, "(%%%a)","%(%1+%)")
    -- convert all %s to .+
    ret = gsub(ret, "%%s%+",".+")
    -- set priority to numbers over strings
    ret = gsub(ret, "%(.%+%)%(%%d%+%)","%(.-%)%(%%d%+%)")
    -- cache it
    sanitize_cache[pattern] = ret
  end

  return sanitize_cache[pattern]
end

local er, eg, eb, ea = .4,.4,.4,1
local br, bg, bb, ba = 0,0,0,1
pfUI.api.CreateBackdrop = pfUI.api.CreateBackdrop or function(f, inset, legacy, transp)
  -- exit if now frame was given
  if not f then return end

  -- use default inset if nothing is given
  local border = inset
  if not border then
    border = tonumber(pfUI_config.appearance.border.default)
  end

  if transp then ba = transp end

  -- use legacy backdrop handling
  if legacy then
    f:SetBackdrop(pfUI.backdrop)
    f:SetBackdropColor(br, bg, bb, ba)
    f:SetBackdropBorderColor(er, eg, eb , ea)
    return
  end

  -- increase clickable area if available
  if f.SetHitRectInsets then
    f:SetHitRectInsets(-border,-border,-border,-border)
  end

  -- use new backdrop behaviour
  if not f.backdrop then
    f:SetBackdrop(nil)

    local border = tonumber(border) - 1
    local backdrop = pfUI.backdrop
    if border < 1 then backdrop = pfUI.backdrop_small end
  	local b = CreateFrame("Frame", nil, f)
  	b:SetPoint("TOPLEFT", f, "TOPLEFT", -border, border)
  	b:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", border, -border)

    local level = f:GetFrameLevel()
    if level < 1 then
  	  --f:SetFrameLevel(level + 1)
      b:SetFrameLevel(level)
    else
      b:SetFrameLevel(level - 1)
    end

    f.backdrop = b
    b:SetBackdrop(backdrop)
  end

  local b = f.backdrop
  b:SetBackdropColor(br, bg, bb, ba)
  b:SetBackdropBorderColor(er, eg, eb , ea)
end

pfUI.api.SkinButton = pfUI.api.SkinButton or function(button, cr, cg, cb)
  local b = getglobal(button)
  if not b then b = button end
  if not b then return end
  if not cr or not cg or not cb then
    _, class = UnitClass("player")
    local color = RAID_CLASS_COLORS[class]
    cr, cg, cb = color.r , color.g, color.b
  end
  pfUI.api.CreateBackdrop(b, nil, true)
  b:SetNormalTexture(nil)
  b:SetHighlightTexture(nil)
  b:SetPushedTexture(nil)
  b:SetDisabledTexture(nil)
  local funce = b:GetScript("OnEnter")
  local funcl = b:GetScript("OnLeave")
  b:SetScript("OnEnter", function()
    if funce then funce() end
    pfUI.api.CreateBackdrop(b, nil, true)
    b:SetBackdropBorderColor(cr,cg,cb,1)
  end)
  b:SetScript("OnLeave", function()
    if funcl then funcl() end
    pfUI.api.CreateBackdrop(b, nil, true)
  end)
  pfUI.api.SetButtonFont(b, pfUI.font_default, pfUI_config.global.font_size, "OUTLINE")
end

pfUI.api.CreateScrollFrame = pfUI.api.CreateScrollFrame or function(name, parent)
  local f = CreateFrame("ScrollFrame", name, parent)

  -- create slider
  f.slider = CreateFrame("Slider", nil, f)
  f.slider:SetOrientation('VERTICAL')
  f.slider:SetPoint("TOPLEFT", f, "TOPRIGHT", -7, 0)
  f.slider:SetPoint("BOTTOMRIGHT", 0, 0)
  f.slider:SetThumbTexture("Interface\\BUTTONS\\WHITE8X8")
  f.slider.thumb = f.slider:GetThumbTexture()
  f.slider.thumb:SetHeight(50)
  f.slider.thumb:SetTexture(.3,1,.8,.5)

  local selfevent = false
  f.slider:SetScript("OnValueChanged", function()
    if selfevent then return end
    selfevent = true
    f:SetVerticalScroll(this:GetValue())
    f.UpdateScrollState()
    selfevent = false
  end)

  f.UpdateScrollState = function()
    f.slider:SetMinMaxValues(0, f:GetVerticalScrollRange())
    f.slider:SetValue(f:GetVerticalScroll())

    local m = f:GetHeight()+f:GetVerticalScrollRange()
    local v = f:GetHeight()
    local ratio = v / m

    if ratio < 1 then
      local size = math.floor(v * ratio)
      f.slider.thumb:SetHeight(size)
      f.slider:Show()
    else
      f.slider:Hide()
    end
  end

  f.Scroll = function(self, step)
    local step = step or 0

    local current = f:GetVerticalScroll()
    local max = f:GetVerticalScrollRange()
    local new = current - step

    if new >= max then
      f:SetVerticalScroll(max)
    elseif new <= 0 then
      f:SetVerticalScroll(0)
    else
      f:SetVerticalScroll(new)
    end

    f:UpdateScrollState()
  end

  f:EnableMouseWheel(1)
  f:SetScript("OnMouseWheel", function()
    this:Scroll(arg1*10)
  end)

  return f
end

pfUI.api.CreateScrollChild = pfUI.api.CreateScrollChild or function(name, parent)
  local f = CreateFrame("Frame", name, parent)

  -- dummy values required
  f:SetWidth(1)
  f:SetHeight(1)
  f:SetAllPoints(parent)

  parent:SetScrollChild(f)

  f:SetScript("OnUpdate", function()
    this:GetParent():UpdateScrollState()
  end)

  return f
end

-- [ round ]
-- Rounds a float number into specified places after comma.
-- 'input'      [float]         the number that should be rounded.
-- 'places'     [int]           amount of places after the comma.
-- returns:     [float]         rounded number.
pfUI.api.round = pfUI.api.round or function(input, places)
  if not places then places = 0 end
  if type(input) == "number" and type(places) == "number" then
    local pow = 1
    for i = 1, places do pow = pow * 10 end
    return floor(input * pow + 0.5) / pow
  end
end

-- [ rgbhex ]
-- Returns color format from color info
-- 'r'          [table or number]  color table or r color component
-- 'g'          [number] optional g color component
-- 'b'          [number] optional b color component
-- 'a'          [number] optional alpha component
-- returns color string in the form of '|caaxxyyzz'
local hexcolor_cache = {}
pfUI.api.rgbhex = pfUI.api.rgbhex or function(r, g, b, a)
  local key
  if type(r)=="table" then
    local _r,_g,_b,_a
    if r.r then
      _r,_g,_b,_a = r.r, r.g, r.b, r.a or 1
    elseif table.getn(r) >= 3 then
      _r,_g,_b,_a = r[1], r[2], r[3], r[4] or 1
    end
    if _r and _g and _b and _a then
      key = string.format("%s%s%s%s",_r,_g,_b,_a)
      if hexcolor_cache[key] == nil then
        hexcolor_cache[key] = string.format("|c%02x%02x%02x%02x", _a*255, _r*255, _g*255, _b*255)
      end
    end
  elseif tonumber(r) and g and b then
    a = a or 1
    key = string.format("%s%s%s%s",r,g,b,a)
    if hexcolor_cache[key] == nil then
      hexcolor_cache[key] = string.format("|c%02x%02x%02x%02x", a*255, r*255, g*255, b*255)
    end
  end
  return hexcolor_cache[key] or ""
end

-- [ GetColorGradient ] --
-- 'perc'     percentage (0-1)
-- return r,g,b and hexcolor
local gradientcolors = {}
pfUI.api.GetColorGradient = pfUI.api.GetColorGradient or function(perc)
  perc = perc > 1 and 1 or perc
  perc = perc < 0 and 0 or perc
  perc = floor(perc*100)/100

  local index = perc
  if not gradientcolors[index] then
    local r1, g1, b1, r2, g2, b2

    if perc <= 0.5 then
      perc = perc * 2
      r1, g1, b1 = 1, 0, 0
      r2, g2, b2 = 1, 1, 0
    else
      perc = perc * 2 - 1
      r1, g1, b1 = 1, 1, 0
      r2, g2, b2 = 0, 1, 0
    end

    local r = pfUI.api.round(r1 + (r2 - r1) * perc, 4)
    local g = pfUI.api.round(g1 + (g2 - g1) * perc, 4)
    local b = pfUI.api.round(b1 + (b2 - b1) * perc, 4)
    local h = pfUI.api.rgbhex(r,g,b)

    gradientcolors[index] = {}
    gradientcolors[index].r = r
    gradientcolors[index].g = g
    gradientcolors[index].b = b
    gradientcolors[index].h = h
  end

  return gradientcolors[index].r,
    gradientcolors[index].g,
    gradientcolors[index].b,
    gradientcolors[index].h
end


-- [ GetBestAnchor ]
-- Returns the best anchor of a frame, based on its position
-- 'self'       [frame]        the frame that should be checked
-- returns:     [string]       the name of the best anchor
pfUI.api.GetBestAnchor = pfUI.api.GetBestAnchor or function(self)
  local scale = self:GetScale()
  local x, y = self:GetCenter()
  local a = GetScreenWidth()  / scale / 3
  local b = GetScreenWidth()  / scale / 3 * 2
  local c = GetScreenHeight() / scale / 3 * 2
  local d = GetScreenHeight() / scale / 3
  if not x or not y then return end

  if x < a and y > c then
    return "TOPLEFT"
  elseif x > a and x < b and y > c then
    return "TOP"
  elseif x > b and y > c then
    return "TOPRIGHT"
  elseif x < a and y > d and y < c then
    return "LEFT"
  elseif x > a and x < b and y > d and y < c then
    return "CENTER"
  elseif x > b and y > d and y < c then
    return "RIGHT"
  elseif x < a and y < d then
    return "BOTTOMLEFT"
  elseif x > a and x < b and y < d then
    return "BOTTOM"
  elseif x > b and y < d then
    return "BOTTOMRIGHT"
  end
end

-- [ ConvertFrameAnchor ]
-- Converts a frame anchor into another one while preserving the frame position
-- 'self'       [frame]        the frame that should get another anchor.
-- 'anchor'     [string]       the new anchor that shall be used
-- returns:     anchor, x, y   can directly be used in SetPoint()
pfUI.api.ConvertFrameAnchor = pfUI.api.ConvertFrameAnchor or function(self, anchor)
  local scale, x, y, _ = self:GetScale(), nil, nil, nil

  if anchor == "CENTER" then
    x, y = self:GetCenter()
    x, y = x - GetScreenWidth()/2/scale, y - GetScreenHeight()/2/scale
  elseif anchor == "TOPLEFT" then
    x, y = self:GetLeft(), self:GetTop() - GetScreenHeight()/scale
  elseif anchor == "TOP" then
    x, _ = self:GetCenter()
    x, y = x - GetScreenWidth()/2/scale, self:GetTop() - GetScreenHeight()/scale
  elseif anchor == "TOPRIGHT" then
    x, y = self:GetRight() - GetScreenWidth()/scale, self:GetTop() - GetScreenHeight()/scale
  elseif anchor == "RIGHT" then
    _, y = self:GetCenter()
    x, y = self:GetRight() - GetScreenWidth()/scale, y - GetScreenHeight()/2/scale
  elseif anchor == "BOTTOMRIGHT" then
    x, y = self:GetRight() - GetScreenWidth()/scale, self:GetBottom()
  elseif anchor == "BOTTOM" then
    x, _ = self:GetCenter()
    x, y = x - GetScreenWidth()/2/scale, self:GetBottom()
  elseif anchor == "BOTTOMLEFT" then
    x, y = self:GetLeft(), self:GetBottom()
  elseif anchor == "LEFT" then
    _, y = self:GetCenter()
    x, y = self:GetLeft(), y - GetScreenHeight()/2/scale
  end

  return anchor, pfUI.api.round(x, 2), pfUI.api.round(y, 2)
end
