--if not Cartographer and not Cartographer3 then
--	ChatFrame1:AddMessage("|cffff0000Zygor Guides Viewer requires Cartographer or Cartographer3")
--end

--local Rock = nil  -- keep the partial Rock compatibility for now, but don't actually use it.
--local Cartographer_Notes = nil
--local Cartographer = nil
--local Cartographer3 = nil

local me = LibStub('AceAddon-3.0'):NewAddon(
  'ZygorGuidesViewer',
  'AceConsole-3.0',
  'AceEvent-3.0',
  'AceTimer-3.0'
)

--global export
ZygorGuidesViewer = me

ZGV = me
local ZGV = me

me.L = ZygorGuidesViewer_L('Main')
me.LS = ZygorGuidesViewer_L('G_string')

local linecount = 50
local guidebuttoncount = 10

local L = me.L
local LI = me.LI
local LC = me.LC
local LQ = me.LQ
local LS = me.LS

local Gratuity = LibStub('LibGratuity-3.0')

me.registeredguides = {}
me.registeredmapspotsets = {}
me.guidebuttons = {}

local DIR = 'Interface\\AddOns\\ZygorGuidesViewer'
ZGV.DIR = DIR
local SKINDIR = ''

ZYGORGUIDESVIEWER_COMMAND = 'zygor'

ZYGORGUIDESVIEWERFRAME_TITLE = 'ZygorGuidesViewer'

BINDING_HEADER_ZYGORGUIDES = L['name_plain']
BINDING_NAME_ZYGORGUIDES_OPENGUIDE = L['binding_togglewindow']
BINDING_NAME_ZYGORGUIDES_PREV = L['binding_prev']
BINDING_NAME_ZYGORGUIDES_NEXT = L['binding_next']

_G['BINDING_NAME_CLICK ZygorGuideButton1:LeftButton'] = 'Magic Button #1 (Target, UseItem)'
_G['BINDING_NAME_CLICK ZygorGuideButton2:LeftButton'] = 'Magic Button #2 (Target, UseItem)'
_G['BINDING_NAME_CLICK ZygorGuideButton3:LeftButton'] = 'Magic Button #3 (Target, UseItem)'
_G['BINDING_NAME_CLICK ZygorGuideButton4:LeftButton'] = 'Magic Button #4 (Target, UseItem)'
_G['BINDING_NAME_CLICK ZygorGuideButton5:LeftButton'] = 'Magic Button #5 (Target, UseItem)'

local ver = select(4, GetBuildInfo())
ZGV.WotLK = (ver >= 30000)
ZGV.Cata = (ver >= 40000)

local BZ = LibStub('LibBabble-Zone-3.0')
local BZL = BZ:GetUnstrictLookupTable()
local BZR = BZ:GetReverseLookupTable()
me.BZL = BZL
me.BZR = BZR
local BF = LibStub('LibBabble-Faction-3.0')
local BFL = BF:GetUnstrictLookupTable()
local BFR = BF:GetReverseLookupTable()
me.BFL = BFL
me.BFR = BFR

me.guidesets = {}

local _G, assert, table, string, tinsert, tonumber, tostring, type, ipairs, pairs, setmetatable, math =
  _G, assert, table, string, tinsert, tonumber, tostring, type, ipairs, pairs, setmetatable, math

--local Dewdrop = AceLibrary("Dewdrop-2.0")

me.LibTaxi = LibStub('LibTaxi-1.0')

me.icons = {
  ['hilite'] = {
    text = L['map_highlight'],
    path = DIR .. '\\Skin\\highlightmap',
    width = 32,
    height = 32,
    alpha = 1,
  },
  ['hilitesquare'] = {
    text = L['map_highlight'],
    path = DIR .. '\\Skin\\highlightmap_square',
    width = 32,
    height = 32,
    alpha = 1,
  },
}

me.CartographerDatabase = {}

me.startups = {}

me.StepLimit = linecount
me.stepframes = {}
me.spotframes = {}

local STEP_LINE_SPACING = 2
local MIN_HEIGHT = 100
local ICON_INDENT = 15
ZGV.ICON_INDENT = ICON_INDENT
local STEP_SPACING = 2
ZGV.STEP_SPACING = STEP_SPACING
ZGV.STEPMARGIN_X = 3
ZGV.STEPMARGIN_Y = 4

ZGV.MIN_STEP_HEIGHT = 15

local FONT = STANDARD_TEXT_FONT
--ZGV.BUTTONS_INLINE=true

-- NOTE : To create a skin, create an entry in both SKINS and SKIN_HOOKS
Z_SKINS = {
  Default = {
    skinbacktopoff = 0,
    disableGuideAnim = false,
    Boarder_Close_topright_offsetX = 5,
    Boarder_Close_topright_offsetY = -2,
    Boarder_Mini_topright_offsetX = -40,
    Boarder_Mini_topright_offsetY = -5,
    Boarder_Guide_topright_offsetX = -58,
    Boarder_Guide_topright_offsetY = -19,
    Boarder_Settings_anchor_offsetX = 40,
    Boarder_Settings_anchor_offsetY = -5,
    Boarder_Lock_offsetX = 8,
    Boarder_Lock_offsetY = -13,
    Boarder_Settings_anchor = 'TOPLEFT',
    Boarder_topleft_offsetX = 28,
    Boarder_topleft_offsetY = 11,
    Boarder_topright_offsetX = 0,
    Boarder_topright_offsetY = 11,
    disableBackDrop = false,
    bgfileback = 'Interface\\ChatFrame\\ChatFrameBackground',
    Boarder_Left_TopLeft_X = -1,
    Boarder_Left_TopLeft_Y = -85,
    Boarder_Left_BottomRight_X = 9,
    Boarder_Left_BottomRight_Y = 10,
    Boarder_Right_TopRight_X = 1,
    Boarder_Right_TopRight_Y = -35,
    Boarder_Right_BottomLeft_X = -9,
    Boarder_Right_BottomLeft_Y = 10,
    Border_Title = true,
    Skipper_anchor = not LibStub('AceDB-3.0'):New('ZygorGuidesViewerSettings').profile.resizeup
        and 'TOPLEFT'
      or 'BOTTOMLEFT', -- This feels evil - Kevinf100
    Skipper_relativeTo = ZygorGuidesViewerFrame, -- Broken?
    Skipper_xOffset = -23,
    Skipper_yOffset = -27,
  },
  Modern = {
    disableBackDrop = true,
    -- ZygorGuidesViewerFrame_Border_Close Button
    Boarder_Close_topright_offsetX = -15,
    Boarder_Close_topright_offsetY = -13,
    -- ZygorGuidesViewerFrame_Border_MiniButton Button
    Boarder_Mini_topright_offsetX = -35,
    Boarder_Mini_topright_offsetY = -13,
    -- ZygorGuidesViewerFrame_Border_GuideButton
    Boarder_Guide_topright_offsetX = -55,
    Boarder_Guide_topright_offsetY = -27,
    disableGuideAnim = true,
    -- ZygorGuidesViewerFrame_Border_SettingsButton
    Boarder_Settings_anchor_offsetX = -75,
    Boarder_Settings_anchor_offsetY = -13,
    Boarder_Settings_anchor = 'TOPRIGHT',
    -- ZygorGuidesViewerFrame_Border_LockButton
    Boarder_Lock_offsetX = 10,
    Boarder_Lock_offsetY = -13,
    -- ZygorGuidesViewerFrame_Border
    --backopacity = 1,
    -- Minus goes up, postive goes down
    skinbacktopoff = 0,
    bgfileback = 'Interface\\ChatFrame\\ChatFrameBackground',
    --End ZygorGuidesViewerFrame_Border
    Boarder_topleft_offsetX = 0,
    Boarder_topleft_offsetY = 5,
    Boarder_topright_offsetX = 0,
    Boarder_topright_offsetY = 0,
    Border_Title = false,
    -- ZygorGuidesViewerFrame_Border_Left
    Boarder_Left_TopLeft_X = -1,
    Boarder_Left_TopLeft_Y = -85,
    Boarder_Left_BottomRight_X = 9,
    Boarder_Left_BottomRight_Y = 10,
    -- ZygorGuidesViewerFrame_Border_Right
    Boarder_Right_TopRight_X = 1,
    Boarder_Right_TopRight_Y = -35,
    Boarder_Right_BottomLeft_X = -9,
    Boarder_Right_BottomLeft_Y = 10,
    -- ZygorGuidesViewerFrame_Skipper
    Skipper_anchor = 'CENTER',
    Skipper_relativeTo = ZygorGuidesViewerFrame_Border_Top, -- Unused atm?
    Skipper_xOffset = 0,
    Skipper_yOffset = 0,
  },
  Classic = {
    disableBackDrop = false,
    -- ZygorGuidesViewerFrame_Border_Close Button
    Boarder_Close_topright_offsetX = -12,
    Boarder_Close_topright_offsetY = -3,
    -- ZygorGuidesViewerFrame_Border_MiniButton Button
    Boarder_Mini_topright_offsetX = -30,
    Boarder_Mini_topright_offsetY = -3,
    -- ZygorGuidesViewerFrame_Border_GuideButton
    Boarder_Guide_topright_offsetX = -51,
    Boarder_Guide_topright_offsetY = -18,
    Boarder_Guide_anchor = 'CENTER',
    disableGuideAnim = true,
    -- ZygorGuidesViewerFrame_Border_SettingsButton
    Boarder_Settings_anchor_offsetX = -70,
    Boarder_Settings_anchor_offsetY = -3,
    Boarder_Settings_anchor = 'TOPRIGHT',
    -- ZygorGuidesViewerFrame_Border_LockButton
    Boarder_Lock_offsetX = 12,
    Boarder_Lock_offsetY = -3,
    Boarder_Lock_anchor = 'TOPRLEFT',
    -- ZygorGuidesViewerFrame_Border
    --backopacity = 1,
    -- Minus goes up, postive goes down
    skinbacktopoff = 0,
    bgfileback = 'Interface\\AddOns\\ZygorGuidesViewer\\Skin\\Classic\\UI-Background-Marble.tga',
    --End ZygorGuidesViewerFrame_Border
    Boarder_topleft_offsetX = 0,
    Boarder_topleft_offsetY = 10,
    Boarder_topright_offsetX = 1,
    Boarder_topright_offsetY = 6,
    Border_Title = false,
    -- ZygorGuidesViewerFrame_Border_Left
    Boarder_Left_TopLeft_X = -3,
    Boarder_Left_TopLeft_Y = 10,
    Boarder_Left_BottomRight_X = 6,
    Boarder_Left_BottomRight_Y = 2,
    -- ZygorGuidesViewerFrame_Border_Right
    Boarder_Right_TopRight_X = 2,
    Boarder_Right_TopRight_Y = 10,
    Boarder_Right_BottomLeft_X = -7,
    Boarder_Right_BottomLeft_Y = 2,
    -- ZygorGuidesViewerFrame_Skipper
    Skipper_anchor = 'CENTER',
    Skipper_relativeTo = ZygorGuidesViewerFrame_Border_Top, -- Unused atm
    Skipper_xOffset = 0,
    Skipper_yOffset = 0,
  },
}
Z_SKIN_HOOKS = {
  Default = function()
    ZygorGuidesViewerFrame_Border_Left:SetTexture(
      'Interface\\AddOns\\ZygorGuidesViewer\\Skin\\LeavesOfSteel_border'
    )
    ZygorGuidesViewerFrame_Skipper_Step:ClearAllPoints()
    ZygorGuidesViewerFrame_Skipper_Step:SetPoint(
      'TOP',
      ZygorGuidesViewerFrame_Skipper,
      'TOP',
      0,
      -19
    )
    ZygorGuidesViewerFrame_Skipper_PrevButton:ClearAllPoints()
    ZygorGuidesViewerFrame_Skipper_PrevButton:SetPoint(
      'TOP',
      ZygorGuidesViewerFrame_Skipper,
      'TOP',
      0,
      0
    )
    ZygorGuidesViewerFrame_Skipper_NextButton:ClearAllPoints()
    ZygorGuidesViewerFrame_Skipper_NextButton:SetPoint(
      'TOP',
      ZygorGuidesViewerFrame_Skipper_PrevButton,
      'BOTTOM',
      0,
      -13
    )
    ZygorGuidesViewerFrame_Border_Gear1:SetTexture(
      'Interface\\AddOns\\ZygorGuidesViewer\\Skin\\leavesofsteel_gear1'
    )
    ZygorGuidesViewerFrame_Border_Gear2:SetTexture(
      'Interface\\AddOns\\ZygorGuidesViewer\\Skin\\leavesofsteel_gear2'
    )
    ZygorGuidesViewerFrame_Border_Gear3:SetTexture(
      'Interface\\AddOns\\ZygorGuidesViewer\\Skin\\leavesofsteel_gear3'
    )
    ZygorGuidesViewerFrame_Border_Logo:SetTexture(SKINDIR .. '\\zglogo')
  end,
  Modern = function()
    ZygorGuidesViewerFrame_Border_Left:SetTexture(nil)
    -- New horizontal layout for skipper buttons
    ZygorGuidesViewerFrame_Skipper_PrevButton:ClearAllPoints()
    ZygorGuidesViewerFrame_Skipper_PrevButton:SetPoint(
      'LEFT',
      ZygorGuidesViewerFrame_Border_LockButton,
      'CENTER',
      20,
      0
    )
    ZygorGuidesViewerFrame_Skipper_Step:ClearAllPoints()
    ZygorGuidesViewerFrame_Skipper_Step:SetPoint(
      'CENTER',
      ZygorGuidesViewerFrame_Skipper_PrevButton,
      'CENTER',
      17,
      0
    )
    ZygorGuidesViewerFrame_Skipper_NextButton:ClearAllPoints()
    ZygorGuidesViewerFrame_Skipper_NextButton:SetPoint(
      'RIGHT',
      ZygorGuidesViewerFrame_Skipper_PrevButton,
      'CENTER',
      44,
      0
    )
    ZygorGuidesViewerFrame_Border_Gear1:SetTexture(nil)
    ZygorGuidesViewerFrame_Border_Gear2:SetTexture(nil)
    ZygorGuidesViewerFrame_Border_Gear3:SetTexture(nil)
    ZygorGuidesViewerFrame_Border_Logo:SetTexture(nil)

    -- Check which skin is active to set the correct scrollbar button textures
    -- Textures for the MODERN skin scrollbar
    local modernScrollAtlas = SKINDIR .. '\\modern_scroll_buttons.tga' -- Make sure this is your filename

    -- Updated to use your 256px icon height
    local icon_height_ratio = 256 / 1024 -- (256px Icon Height / 1024px Image Height)

    -- UP ARROWS (Top row, arranged right-to-left on the image)
    ZygorGuidesViewerFrameScrollScrollBarScrollUpButton:SetNormalTexture(
      CreateTextureWithCoords(
        ZygorGuidesViewerFrameScrollScrollBarScrollUpButton,
        modernScrollAtlas,
        0.75,
        1.0,
        0.0,
        icon_height_ratio
      )
    ) -- Normal (Far right)
    ZygorGuidesViewerFrameScrollScrollBarScrollUpButton:SetPushedTexture(
      CreateTextureWithCoords(
        ZygorGuidesViewerFrameScrollScrollBarScrollUpButton,
        modernScrollAtlas,
        0.5,
        0.75,
        0.0,
        icon_height_ratio
      )
    ) -- Pushed
    ZygorGuidesViewerFrameScrollScrollBarScrollUpButton:SetDisabledTexture(
      CreateTextureWithCoords(
        ZygorGuidesViewerFrameScrollScrollBarScrollUpButton,
        modernScrollAtlas,
        0.25,
        0.5,
        0.0,
        icon_height_ratio
      )
    ) -- Disabled
    ZygorGuidesViewerFrameScrollScrollBarScrollUpButton:SetHighlightTexture(
      CreateTextureWithCoords(
        ZygorGuidesViewerFrameScrollScrollBarScrollUpButton,
        modernScrollAtlas,
        0.0,
        0.25,
        0.0,
        icon_height_ratio
      )
    ) -- Highlight (Far left)

    -- DOWN ARROWS (Bottom row, arranged right-to-left on the image)
    ZygorGuidesViewerFrameScrollScrollBarScrollDownButton:SetNormalTexture(
      CreateTextureWithCoords(
        ZygorGuidesViewerFrameScrollScrollBarScrollDownButton,
        modernScrollAtlas,
        0.75,
        1.0,
        1.0 - icon_height_ratio,
        1.0
      )
    ) -- Normal (Far right)
    ZygorGuidesViewerFrameScrollScrollBarScrollDownButton:SetPushedTexture(
      CreateTextureWithCoords(
        ZygorGuidesViewerFrameScrollScrollBarScrollDownButton,
        modernScrollAtlas,
        0.5,
        0.75,
        1.0 - icon_height_ratio,
        1.0
      )
    ) -- Pushed
    ZygorGuidesViewerFrameScrollScrollBarScrollDownButton:SetDisabledTexture(
      CreateTextureWithCoords(
        ZygorGuidesViewerFrameScrollScrollBarScrollDownButton,
        modernScrollAtlas,
        0.25,
        0.5,
        1.0 - icon_height_ratio,
        1.0
      )
    ) -- Disabled
    ZygorGuidesViewerFrameScrollScrollBarScrollDownButton:SetHighlightTexture(
      CreateTextureWithCoords(
        ZygorGuidesViewerFrameScrollScrollBarScrollDownButton,
        modernScrollAtlas,
        0.0,
        0.25,
        1.0 - icon_height_ratio,
        1.0
      )
    ) -- Highlight (Far left)
  end,
  Classic = function()
    SKINDIR = DIR .. '\\Skin\\Classic' -- Force the path to your Classic folder

    -- Load all textures for the Classic skin
    ZygorGuidesViewerFrame_Skipper_PrevButton:ClearAllPoints()
    -- ZygorGuidesViewerFrame_Border_LockButton:ClearAllPoints()
    -- ZygorGuidesViewerFrame_Border_LockButton:SetPoint("TOP", ZygorGuidesViewerFrame_Skipper, "TOP", 0, 0)
    ZygorGuidesViewerFrame_Skipper_PrevButton:SetPoint(
      'LEFT',
      ZygorGuidesViewerFrame_Border_LockButton,
      'CENTER',
      20,
      -2
    )
    ZygorGuidesViewerFrame_Skipper_Step:ClearAllPoints()
    ZygorGuidesViewerFrame_Skipper_Step:SetPoint(
      'CENTER',
      ZygorGuidesViewerFrame_Skipper_PrevButton,
      'CENTER',
      17,
      1
    )
    ZygorGuidesViewerFrame_Skipper_NextButton:ClearAllPoints()
    ZygorGuidesViewerFrame_Skipper_NextButton:SetPoint(
      'RIGHT',
      ZygorGuidesViewerFrame_Skipper_PrevButton,
      'CENTER',
      44,
      0
    )
    ZygorGuidesViewerFrame_Skipper_PrevButton.ntx:SetTexture(SKINDIR .. '\\titlebuttons2.tga')
    ZygorGuidesViewerFrame_Skipper_PrevButton.ptx:SetTexture(SKINDIR .. '\\titlebuttons2')
    ZygorGuidesViewerFrame_Skipper_PrevButton.htx:SetTexture(SKINDIR .. '\\titlebuttons2')
    ZygorGuidesViewerFrame_Skipper_NextButton.ntx:SetTexture(SKINDIR .. '\\titlebuttons2')
    ZygorGuidesViewerFrame_Skipper_NextButton.ptx:SetTexture(SKINDIR .. '\\titlebuttons2')
    ZygorGuidesViewerFrame_Skipper_NextButton.htx:SetTexture(SKINDIR .. '\\titlebuttons2')
    ZygorGuidesViewerFrame_Border_GuideButton.ntx:SetTexture(
      SKINDIR .. '\\leavesofsteel_dropdown_up'
    )
    ZygorGuidesViewerFrame_Border_GuideButton.ptx:SetTexture(
      SKINDIR .. '\\leavesofsteel_dropdown_down'
    )
    ZygorGuidesViewerFrame_Border_GuideButton.htx:SetTexture(
      SKINDIR .. '\\leavesofsteel_dropdown_hi'
    )
    ZygorGuidesViewerFrame_Border_TopLeft:SetTexture(SKINDIR .. '\\leavesofsteel')
    ZygorGuidesViewerFrame_Border_TopRight:SetTexture(SKINDIR .. '\\leavesofsteel')
    ZygorGuidesViewerFrame_Border_BottomLeft:SetTexture(SKINDIR .. '\\leavesofsteel')
    ZygorGuidesViewerFrame_Border_BottomRight:SetTexture(SKINDIR .. '\\leavesofsteel')
    ZygorGuidesViewerFrame_Border_Left:SetTexture(SKINDIR .. '\\leavesofsteel_border')
    ZygorGuidesViewerFrame_Border_Right:SetTexture(SKINDIR .. '\\leavesofsteel_border')
    ZygorGuidesViewerFrame_Border_Bottom:SetTexture(SKINDIR .. '\\leavesofsteel_border')
    ZygorGuidesViewerFrame_Border_Left:SetDrawLayer('OVERLAY')
    ZygorGuidesViewerFrame_Border_Right:SetDrawLayer('OVERLAY')
    ZygorGuidesViewerFrame_Border_Top:SetTexture(SKINDIR .. '\\leavesofsteel_top')
    ZygorGuidesViewerFrame_Border_Gear1:SetTexture(nil)
    ZygorGuidesViewerFrame_Border_Gear2:SetTexture(nil)
    ZygorGuidesViewerFrame_Border_Gear3:SetTexture(nil)
    ZygorGuidesViewerFrame_Border_Logo:SetTexture(nil)
    ZygorGuidesViewerFrame_Skipper_PrevButton.ntx:SetTexture(SKINDIR .. '\\titlebuttons2')
    ZygorGuidesViewerFrame_Skipper_PrevButton.ptx:SetTexture(SKINDIR .. '\\titlebuttons2')
    ZygorGuidesViewerFrame_Skipper_PrevButton.htx:SetTexture(SKINDIR .. '\\titlebuttons2')
    ZygorGuidesViewerFrame_Skipper_NextButton.ntx:SetTexture(SKINDIR .. '\\titlebuttons2')
    ZygorGuidesViewerFrame_Skipper_NextButton.ptx:SetTexture(SKINDIR .. '\\titlebuttons2')
    ZygorGuidesViewerFrame_Skipper_NextButton.htx:SetTexture(SKINDIR .. '\\titlebuttons2')
  end,
}

local math_modf = math.modf
math.round = function(n)
  local x, y = math_modf(n)
  return n > 0 and (y >= 0.5 and x + 1 or x) or (y <= -0.5 and x - 1 or x)
end
local round = math.round

function me:OnInitialize()
  --	if not ZygorGuidesViewerMiniFrame then error("Zygor Guide Viewer step frame not loaded.") end
  if not ZygorGuidesViewerFrame then
    error('Zygor Guide Viewer frame not loaded.')
  end

  self.db = LibStub('AceDB-3.0'):New('ZygorGuidesViewerSettings')

  self:Debug('Initializing...')

  self:Options_RegisterDefaults()

  --self.db:SetProfile("char/"..UnitName("player").." - "..GetRealmName())

  self:Options_DefineOptions()

  self.optionsprofile = LibStub('AceDBOptions-3.0'):GetOptionsTable(self.db)

  local function ApplySkin(skinName)
    local skin = Z_SKINS[skinName]
    if skin then
      for k, v in pairs(skin) do
        if type(v) == 'table' then
          self.db.profile[k] = CopyTable(v)
        else
          self.db.profile[k] = v
        end
      end
    end
  end

  function LoadSkin(name)
    ApplySkin(name)
    if Z_SKIN_HOOKS[name] then
      Z_SKIN_HOOKS[name]()
    end
  end

  self.skin_choice = {
    ['modern'] = 'Modern',
    ['classic'] = 'Classic',
    ['violet'] = 'Default',
    ['green'] = 'Default',
    ['blue'] = 'Default',
    ['orange'] = 'Default',
  }

  if IsShiftKeyDown() then
    self.db.char.maint_startguides = false
    self.db.char.maint_queryquests = false
    self.db.char.maint_fetchquestdata = false
    self.db.char.maint_fetchitemdata = false
    ZygorGuidesViewerMaintenanceFrame_StartGuides:SetChecked(self.db.char.maint_startguides)
    ZygorGuidesViewerMaintenanceFrame_QueryQuests:SetChecked(self.db.char.maint_queryquests)
    ZygorGuidesViewerMaintenanceFrame_FetchQuestData:SetChecked(self.db.char.maint_fetchquestdata)
    ZygorGuidesViewerMaintenanceFrame_FetchItemData:SetChecked(self.db.char.maint_fetchitemdata)

    ZygorGuidesViewerMaintenanceFrame:Show()
  else
    self.db.char.maint_startguides = true
    self.db.char.maint_queryquests = true
    self.db.char.maint_fetchquestdata = true
    self.db.char.maint_fetchitemdata = true
  end

  self.db.char.completedQuests = nil --wipe and flush

  self.CurrentStepNum = self.db.char.step
  self.CurrentGuideName = self.db.char.guidename

  self.QuestCacheTime = 0
  self.QuestCacheUndertimeRepeats = 0
  self.StepCompletion = {}
  self.recentlyAcceptedQuests = {}
  --self.recentlyCompletedQuests = {}
  self.LastSkip = 1

  self.instantQuests = {}
  self.dailyQuests = self.dailyQuests or {}

  self.completionelapsed = 0
  self.completionintervallong = 1.0
  self.completionintervalmin = 0.01
  self.completioninterval = self.completionintervallong

  self:ClearRecentActivities() -- just to make sure they're not nils

  --self.AutoskipTemp = true

  self.Frame = ZygorGuidesViewerFrame

  self.frameNeedsResizing = 0

  self.Frame:SetScale(self.db.profile.framescale)
  self:UpdateLocking()
  self:ReanchorFrame()

  self.TomTomWaypoints = {}

  self.quests = {}
  self.questsbyid = {}
  self.reputations = {}

  self.bandwidth = 0

  --LibSimpleOptions.AddOptionsPanel("Zygor's Guide",function(self) MakeOptionsControls(self,ZGV.options,ZGV) end)
  --LibSimpleOptions.AddSuboptionsPanel("Zygor's Guide",ZGV.options.args.map.name, function(self) MakeOptionsControls(self,ZGV.options.args.map,ZGV) end)
  --LibSimpleOptions.AddSuboptionsPanel("Zygor's Guide",ZGV.options.args.addons.name, function(self) MakeOptionsControls(self,ZGV.options.args.addons,ZGV) end)
  --LibSimpleOptions.AddSlashCommand("Zygor's Guide","/zygoropt")

  self:Options_SetupConfig()
  self:Options_SetupBlizConfig()

  --	self:Echo(L["initialized"])
  self:Debug('Initialized.')

  if self.LibTaxi then
    if not self.db.char.taxis then
      self.db.char.taxis = {}
    end
    self.LibTaxi:Startup(self.db.char.taxis)
  end

  if self.Pointer then
    self.Pointer:Startup()
  end
  if self.Foglight then
    self.Foglight:Startup()
  end

  if ZygorTalentAdvisor and ZygorTalentAdvisor.revision > self.revision then
    self.revision = ZygorTalentAdvisor.revision
    self.version = ZygorTalentAdvisor.version
    self.date = ZygorTalentAdvisor.date
  end

  for i = 1, guidebuttoncount do
    local name = 'ZygorGuideButton' .. i
    local btn = CreateFrame('Button', name, UIParent, 'SecureActionButtonTemplate')
    btn:SetPoint('TOPLEFT', UIParent, 'BOTTOMLEFT', -100, -100) -- put offscreen
    btn:SetSize(36, 36)
    btn:Hide()

    btn:SetAttribute('type', 'macro')
    btn:SetAttribute('macrotext', '/say yo' .. i)

    self.guidebuttons[i] = btn
  end

  if self.LocaleFont then
    FONT = self.LocaleFont
  end

  -- home detection, fire-and-forget style.
  hooksecurefunc('ConfirmBinder', function()
    ZygorGuidesViewer.recentlyHomeChanged = true
  end)
end

local macroIndex = 1

function me:SetMacroButton(slot, text)
  if InCombatLockdown() then
    return
  end

  local btn = self.guidebuttons[slot]
  if btn then
    btn:SetAttribute('macrotext', text or '')
  end

  if true and slot <= ZGV.db.profile.macrocount then
    local macroname = 'ZGV:' .. slot
    local macro = GetMacroIndexByName(macroname)
    if macro == 0 then
      macro = CreateMacro(macroname, 1, text, 1)
    end

    macro = EditMacro(macroname, 'ZGV:' .. slot, 1, text, 1)
  end
end

function me:SetNextMacroButton(text)
  me:SetMacroButton(macroIndex, text)
  macroIndex = macroIndex + 1
end

function me:ResetMacroButtons()
  for i = 1, guidebuttoncount do
    ZGV:SetMacroButton(i, '')
  end
  macroIndex = 1
end

function me:OnEnable()
  self:Debug('enabling')

  if self.db.profile['visible'] then
    self:ToggleFrame()
  end

  ZygorGuidesViewerMapIcon:Show()

  self:UpdateMapButton()
  self:UpdateSkin()

  self:Debug('enabled')

  self:AddEvent('UNIT_INVENTORY_CHANGED')

  -- combat detection for hiding in combat
  self:AddEvent('PLAYER_REGEN_DISABLED')
  self:AddEvent('PLAYER_REGEN_ENABLED')

  self:AddEvent('SPELL_UPDATE_COOLDOWN')

  self:AddEvent('PLAYER_CONTROL_GAINED') -- try to force current zone updates; should prevent GoTo lines from locking up after a taxi flight

  --self.startuptimer = self:ScheduleRepeatingTimer("StartupTimer", 0.1)

  -- startup 'modules'
  for i, startup in ipairs(self.startups) do
    startup(self)
  end

  self:SetWaypointAddon(self.db.profile.waypointaddon)

  self:PruneNPCs()

  self.Log.entries = self.db.char.debuglog
  self.Log:Add('Viewer started. ---------------------------')

  self.ConditionEnv:_Setup()

  -- waiting for QUEST_LOG_UPDATE for true initialization...
  --self:QueryQuests()

  if ZGV_DEV then
    ZGV_DEV()
  end
end

function me:OnDisable()
  --	self:UnregisterAllEvents()
  UnsetWaypointAddon()

  ZygorGuidesViewerMapIcon:Hide()
  self.Frame:Hide()
end

-- my event handling. Multiple handlers allowed, just for the heck of it.

local meta_newtables = {
  __index = function(tbl, key)
    tbl[key] = {}
    return tbl[key]
  end,
}
me.Events = setmetatable({}, meta_newtables)
function me:AddEvent(event, func)
  tinsert(self.Events[event], func or true)
  if #self.Events[event] == 1 then
    self:RegisterEvent(event, 'EventHandler')
  end
end
function me:EventHandler(event, ...)
  for i, hand in ipairs(self.Events[event]) do
    local func
    if type(hand) == 'function' then
      func = hand
    elseif type(hand) == 'string' then
      func = self[hand]
      assert(func, 'No function ' .. hand .. ' in event handler!')
    elseif hand == true then
      func = self[event]
      assert(func, 'No function ' .. event .. ' in event handler!')
    end
    func(self, event, ...)
  end
end

function me:OnFirstQuestLogUpdate()
  if not self.guidesloaded then
    return
  end -- let the OnGuidesLoaded func call us.
  if self.questLogInitialized then
    return
  end

  if self.db.char['starting'] then
    self:Print('First start! Finding proper starter section.')
    local i = self:FindDefaultGuide()
    if i then
      self.db.char.guidename = self.registeredguides[i].title
      self.db.char.step = 1
      self.Frame:Show()
    end
    self.db.char['starting'] = false
  end

  if ZGV.db.char.maint_startguides then
    self:SetGuide(self.db.char.guidename, self.db.char.step)
  end

  self.frameNeedsResizing = 1
  self:AlignFrame()
  self:UpdateFrame(true)
  self.questLogInitialized = true
end

function me:GetGuideByTitle(title)
  for i, v in ipairs(self.registeredguides) do
    if v.title == title then
      return v
    end
  end
end

function me:SetGuide(name, step, temp)
  if not name then
    return
  end
  if not step then
    step = 1
  end
  self:Debug('SetGuide ' .. name .. ' (' .. tostring(step))

  local guide
  if type(name) == 'number' then
    local num = name
    if self.registeredguides[num] then
      guide = self.registeredguides[num]
    else
      self:Print('Cannot find guide number: ' .. num)
      --return false
    end
  else
    guide = self:GetGuideByTitle(name)
    if not guide then
      self:Print('Cannot find guide: ' .. name)
      self:Debug('Cannot find guide: ' .. name)
      return false
    end
  end

  --if guide.is_stored then guide = self.db.global.storedguides[name] end

  if guide and guide.steps then
    --self.MapNotes = _G["ZygorGuides_"..faction.."Mapnotes"]
    local name = guide.title

    self.CurrentGuide = guide

    self:Print(L['message_loadedguide']:format(name))

    self.CurrentGuideIsTemporary = temp

    self.CurrentGuideName = name
    if not temp then
      self.db.char.guidename = name
    end

    if #self.CurrentGuide.steps < step then
      step = 1
    end

    self:QuestTracking_ResetDailies(true)

    self:Debug('Guide loaded: ' .. name)

    self:FocusStep(step)

    ZygorGuidesViewerFrame_Border_GuideButton:UnlockHighlight()
  else
    self:Print(L['message_missingguide']:format(name))
    self.db.char['guide'] = nil
    self.db.char['step'] = nil
    self.CurrentGuide = nil
  end

  self:UpdateFrame(true)
end

function me:FindDefaultGuide()
  for i, guide in ipairs(self.registeredguides) do
    if guide.defaultfor and self:RaceClassMatch(guide.defaultfor, true) then
      return i
    end
  end
  return nil
end

--Some nonsense

function ZGV.BETASTART()
  ZGV.BETAguides = true
end
function ZGV.BETAEND()
  ZGV.BETAguides = false
end

function me:DoMutex(m)
  ZygorGuidesViewer.GuideMenuTier = nil
  if self.guidesets[m] then
    return true
  else
    self.guidesets[m] = true
  end
end

-- function me:SearchForCompleteableGoal() --removed

function me:ClearRecentActivities()
  self.recentlyVisitedCoords = {}
  self.recentlyCompletedGoals = {}
  self.recentlyAcceptedQuests = {}
  self.recentlyStickiedGoals = {}
  self.recentGoalProgress = {}
  self.recentCooldownsPulsing = {}
  self.recentCooldownsStarted = {}
  self.recentlyHomeChanged = false
  self.recentlyDiscoveredFlightpath = false
  self.recentlyLearnedRecipes = {}
  self.recentKills = {}
  self.completedQuestTitles = {}
end

function me:FocusStep(num, quiet)
  if not num or num <= 0 then
    return
  end
  if not self.CurrentGuide then
    return
  end
  if not self.CurrentGuide.steps then
    return
  end
  if num > #self.CurrentGuide.steps then
    return
  end

  self:Debug('FocusStep ' .. num .. (quiet and ' (quiet)' or ''))

  self.CurrentStepNum = num
  if not self.CurrentGuideIsTemporary then
    self.db.char.step = num
  end
  self.CurrentStep = self.CurrentGuide['steps'][num]

  self:ClearRecentActivities()

  self.CurrentStep:PrepareCompletion()

  self.stepchanged = true

  for i, goal in ipairs(self.CurrentStep.goals) do
    if goal:IsComplete() then
      self.recentlyCompletedGoals[goal] = true
    end
  end

  if not quiet then
    --self:HighlightCurrentStep()

    self:StopFlashAnimation()
    self.frameNeedsResizing = self.frameNeedsResizing + 1
    self:UpdateFrame(true)
    self:ScrollToCurrentStep()
    self:UpdateCooldowns()

    self:UpdateCartographerExport()
    self:SetWaypoint()
  end
  --self:UpdateMinimapArrow(true)

  local stepcomplete, steppossible, stepmanual = self.CurrentStep:IsComplete()
  if self.pause then
    if
      (self.db.profile.skipimpossible and not steppossible and not stepmanual)
      or (self.db.profile.skipobsolete and self.CurrentStep:IsObsolete())
      or (self.db.profile.skipauxsteps and self.CurrentStep:IsAuxiliarySkippable())
    then
      stepcomplete = true
      --self.pause=nil
    end
    self.LastSkip = 1
    if not stepcomplete then
      self:Debug('unpausing')
      self.pause = nil
    end
  end
  --and self.LastSkip~=0) then self.AutoskipTemp=false else self.AutoskipTemp=true end

  -- add to last-guides history
  local history = self.db.char.guides_history
  local found
  for i, g in ipairs(history) do
    if g.full == self.CurrentGuideName then
      -- update
      g.step = num
      found = true
      break
    end
  end
  if not found then
    tinsert(
      history,
      { full = self.CurrentGuideName, short = self.CurrentGuide.title_short, step = step }
    )
    if #history > self.db.profile.guidesinhistory then
      tremove(history, 1)
    end
  end

  self:AnimateGears()
end

function me:FocusStepQuiet(num)
  return self:FocusStep(num, true)
end

--- A quest is 'interesting' if any follow-ups to it appear anywhere in the guides and they're not gray.
function me:GetMentionedFollowups(questid)
  local q, f
  local live = { questid }
  local fups = {}
  local lev
  while #live > 0 do
    q = tremove(live, 1)
    lev = self.mentionedQuests[q]
    if lev then
      tinsert(fups, { q, lev })
    end

    f = self.RevChains[q]
    if f then
      for i = 1, #f do
        tinsert(live, f[i])
      end
    end
  end
  return fups
end

-- A quest's "maximum chained level" can be safely cached, I guess.
function me:CacheMentionedFollowups()
  local f, maxlev
  self.maxQuestLevels = {}
  for qid = 1, 30000 do
    if self.mentionedQuests[qid] then
      f = ZGV:GetMentionedFollowups(qid)
      maxlev = 0
      for i = 1, #f do
        if f[i][2] > maxlev then
          maxlev = f[i][2]
        end
      end
      self.maxQuestLevels[qid] = maxlev
    end
  end
end

function me:ListMentionedQuests()
  self.mentionedQuests = {}
  local guide = self:FindDefaultGuide()
  if guide then
    guide = self.registeredguides[guide]
  else
    return
  end
  while guide do
    if guide.quests then
      for qid, lev in pairs(guide.quests) do
        self.mentionedQuests[qid] = lev
      end
    end
    guide.quests = nil

    guide = self:GetGuideByTitle(guide.next)
  end
end

--- Attempt to complete current step.
-- 09-09-24:
function me:TryToCompleteStep(force)
  if not self.CurrentStep then
    return
  end

  if self.BUTTONS_INLINE then
    if self.actionsvisible and InCombatLockdown() then
      return
    end
  end

  -- prevent overtime checks
  if self.completionelapsed <= self.completioninterval and not force then
    self.completionelapsed = self.completionelapsed + 0.1
    return
  end
  self.completionelapsed = 0

  -- frame hidden? bail.
  if not self.Frame:IsVisible() or self.Frame:GetAlpha() < 0.1 then
    return
  end
  --if InCombatLockdown() then return end

  --local skipped=0
  --local updated

  local stepcomplete, steppossible, stepmanual = self.CurrentStep:IsComplete()

  local completing = stepcomplete

  -- smart skipping: treat impossible or skippable as completed
  if
    (self.db.profile.skipimpossible and not steppossible and not stepmanual)
    or (self.db.profile.skipobsolete and self.CurrentStep:IsObsolete())
    or (self.db.profile.skipauxsteps and self.CurrentStep:IsAuxiliarySkippable())
  then
    completing = true
    --self.pause=nil
  end

  if not completing then
    self.pause = nil
    self.completioninterval = self.completionintervallong
  end

  if self.pause then
    self.completioninterval = self.completionintervallong
    self.LastSkip = 1
  else
    if completing then
      --self.recentlyCompletedQuests = {} -- forget it! We're skipping the step, already.
      self:Debug(
        'Skipping step: '
          .. self.CurrentStepNum
          .. ' ('
          .. (stepcomplete and 'complete' or (steppossible and 'possible?' or 'impossible'))
          .. ')'
      )

      if
        self.lasttriedstep
        and self.lasttriedstep == self.CurrentStep
        and not self.lastwascompleted
      then
        --newly completed!
        PlaySound(self.db.profile.completesound)
        if self.db.profile.flashborder then
          self.delayFlash = 1
        end
      end

      self:SkipStep(self.LastSkip, true)
      self.fastforward = true

      self.completioninterval = self.completioninterval * 0.9
      if self.completioninterval < self.completionintervalmin then
        self.completioninterval = self.completionintervalmin
      end
      --skipped=skipped+1
      --if skipped>100 then break end

      --self:UpdateFrame()
      --updated=true

      --self.completioninterval = self.completionshortinterval

      --ZygorGuidesViewerFrame_CoverFlash_blink:Play()

      --stepcomplete = self.CurrentStep:IsComplete()
    else
      self.completioninterval = self.completionintervallong
      self.pause = nil
      self.fastforward = nil
      self.LastSkip = 1
      --self.completioninterval = self.completionlonginterval
    end

    --[[
		if updated and not self.db.profile.showallsteps then
			self.stepframes[1].slideup:Play()
		end
		--]]

    --if not stepcomplete then self.AutoskipTemp=true end

    --if not updated then self:UpdateFrame() end
  end

  self:UpdateFrame()

  self.lasttriedstep = self.CurrentStep
  self.lastwascompleted = stepcomplete
end

function me:InitializeDropDown(frame)
  if not self.guidesloaded then
    return
  end

  local guides = ZygorGuidesViewer.registeredguides

  if not guides then
    return
  end

  for i, guide in ipairs(guides) do
    --		ChatFrame1:AddMessage(section)
    local info = {}
    info.text = guide.title
    info.value = guide.title
    info.func = ZGVFSectionDropDown_Func
    if self.CurrentGuideName == guide.title then
      info.checked = 1
    else
      info.checked = nil
    end
    info.button = 1
    --		if (i == 1) then
    --			info.isTitle = 1
    --		end
    UIDropDownMenu_AddButton(info)
  end
  UIDropDownMenu_SetText(frame, self.CurrentGuideName)
end

function me:UpdateLocking()
  -- remove mouse activity in lock mode
  local locked = self.db.profile['windowlocked']
  --self:Debug("lock mode: "..tostring(locked))

  ZygorGuidesViewerFrame_Border_TitleBar:EnableMouse(not locked)
  ZygorGuidesViewerFrame_ResizerLeft:EnableMouse(not locked)
  ZygorGuidesViewerFrame_ResizerRight:EnableMouse(not locked)
  ZygorGuidesViewerFrame_ResizerBottomLeft:EnableMouse(not locked)
  ZygorGuidesViewerFrame_ResizerBottomRight:EnableMouse(not locked)
  ZygorGuidesViewerFrame_ResizerBottom:EnableMouse(not locked)

  ZygorGuidesViewerFrameScroll:EnableMouseWheel(not locked)

  if self.stepframes then
    for s, st in ipairs(self.stepframes) do
      st:EnableMouse(not locked)
      --[[
			for l,ln in ipairs(st.lines) do
				ln.clicker:EnableMouse(not locked)
			end
		]]
    end
  end

  -- lock button
  if self.db.profile['windowlocked'] then
    ZygorGuidesViewerFrame_Border_LockButton.ntx:SetTexCoord(0.375, 0.500, 0.00, 0.25)
    ZygorGuidesViewerFrame_Border_LockButton.ptx:SetTexCoord(0.375, 0.500, 0.25, 0.50)
    ZygorGuidesViewerFrame_Border_LockButton.htx:SetTexCoord(0.375, 0.500, 0.50, 0.75)
  else
    ZygorGuidesViewerFrame_Border_LockButton.ntx:SetTexCoord(0.250, 0.375, 0.00, 0.25)
    ZygorGuidesViewerFrame_Border_LockButton.ptx:SetTexCoord(0.250, 0.375, 0.25, 0.50)
    ZygorGuidesViewerFrame_Border_LockButton.htx:SetTexCoord(0.250, 0.375, 0.50, 0.75)
  end

  if self.db.profile['showallsteps'] then
    ZygorGuidesViewerFrame_Border_MiniButton.ntx:SetTexCoord(0.000, 0.125, 0.0, 0.25)
    ZygorGuidesViewerFrame_Border_MiniButton.ptx:SetTexCoord(0.000, 0.125, 0.25, 0.5)
    ZygorGuidesViewerFrame_Border_MiniButton.htx:SetTexCoord(0.000, 0.125, 0.50, 0.75)
  else
    ZygorGuidesViewerFrame_Border_MiniButton.ntx:SetTexCoord(0.125, 0.250, 0.00, 0.25)
    ZygorGuidesViewerFrame_Border_MiniButton.ptx:SetTexCoord(0.125, 0.250, 0.25, 0.50)
    ZygorGuidesViewerFrame_Border_MiniButton.htx:SetTexCoord(0.125, 0.250, 0.50, 0.75)
  end
end

function me:StopFlashAnimation()
  if not self.stepframes[1] then
    return
  end
  for s = 1, linecount do
    for i = 1, linecount, 1 do
      local anim_w2g = self.stepframes[s].lines[i].anim_w2g
      if not anim_w2g then
        break
      end
      anim_w2g:Stop()
    end
  end
end

--[[
function me:HideCooldown(arg)
	arg.cooldown:Hide()
	self.recentCooldownsPulsing[goal] = 2
end
--]]

function me:UpdateCooldowns()
  --self:Debug("UpdateCooldowns")
  if not self.CurrentStep then
    return
  end
  local stepframe = self.stepframes[self.CurrentStepframeNum]
  if not stepframe then
    return
  end
  for i = 1, linecount, 1 do
    local cooldown = _G['ZygorGuidesViewerFrame_Act' .. i .. 'ActionCooldown']
    if not cooldown then
      return
    end
    local goal = stepframe.lines[i].goal
    if goal and goal:IsActionable() then
      --cooldown:Show()
      --self:Debug("goal "..i.." actionable")
      if goal.castspell or goal.castspellid then
        local start, dur, en = GetSpellCooldown(goal.castspellid or goal.castspell)
        CooldownFrame_SetTimer(cooldown, start, dur, en)
        if start > 0 then
          cooldown:Show()
        else
          cooldown:Hide()
        end
        --self:Debug(("spell: %d,%d,%d"):format(start,dur,en))
      elseif goal.useitem or goal.useitemid then
        local start, dur, en = GetItemCooldown(goal.useitemid or goal.useitem)
        CooldownFrame_SetTimer(cooldown, start, dur, en)
        if start > 0 then
          cooldown:Show()
        else
          cooldown:Hide()
        end
        --self:Debug(("item: %d,%d,%d"):format(start,dur,en))
      elseif goal.petaction then
        local num, name, x, tex
        if type(goal.petaction) == 'number' then
          num = goal.petaction
        else
          num, name, x, tex = FindPetActionInfo(goal.petaction)
        end
        local start, dur, en = GetPetActionCooldown(num)
        CooldownFrame_SetTimer(cooldown, start, dur, en)
        if start > 0 then
          cooldown:Show()
        else
          cooldown:Hide()
        end
      end
    else
      cooldown:Hide()
    end
  end
end

local function gradient(a, b, p)
  return a + (b - a) * p
end

local function fromRGBA(ob)
  return ob.r, ob.g, ob.b, ob.a
end

local function fromRGB_a(ob, a)
  return ob.r, ob.g, ob.b, a
end

local function fromRGBmul_a(ob, mul, a)
  return ob.r * mul, ob.g * mul, ob.b * mul, a
end

local function fromRGB(ob)
  return ob.r, ob.g, ob.b
end

--local function gradientRGBA(f,t,p)  --removed

--function me:

function me:SetDisplayMode(mode)
  self.db.profile.displaymode = mode
  self:UpdateFrame(true)
end

local Tpi = 6.2832
local cardinals = { 'N', 'NW', 'W', 'SW', 'S', 'SE', 'E', 'NE', 'N' }
local function GetCardinalDirName(angle)
  for i = 1, 9 do
    if Tpi * ((i * 2) - 1) / 16 > angle then
      return cardinals[i]
    end
  end
end
function GetCardinalDirNum(angle)
  while angle < 0 do
    angle = angle + Tpi
  end
  while angle > Tpi do
    angle = angle - Tpi
  end
  local ret = 1
  for i = 1, 16 do
    if Tpi * ((i * 2) - 1) / 32 > angle then
      ret = i
      break
    end
  end
  return ret
end

local itemsources = { 'vendor', 'drop', 'ore', 'herb', 'skin' }

local gold_ox, gold_oy = 0, 0

function me:UpdateFrame(full, onupdate)
  if full then
    self.stepchanged = true
  end

  if not self.Frame or not self.Frame:IsVisible() then
    return
  end

  --if InCombatLockdown() then return end
  --[[
	--		self.Frame:SetAlpha(0.5)
		return
	else
	--		self.Frame:SetAlpha(1.0)
	end
	--]]

  --self:Debug("updatemini")

  --if ZygorGuidesViewerMiniFrame_bdflash:IsPlaying() and not ZygorGuidesViewerMiniFrame_bdflash:IsDone() then return end

  local minh = 0

  if self.loading then
    ZygorGuidesViewerFrame_Border_SectionTitle:SetText()
    ZygorGuidesViewerFrame_MissingText:Show()
    ZygorGuidesViewerFrame_MissingText:SetText(
      L['miniframe_loading']:format((self.loadprogress or 0) * 100)
    )
  elseif self.db.profile.displaymode == 'guide' then
    if self.CurrentGuide and self.CurrentGuide.steps then
      -- hide spot frames, if visible
      if self.spotframes[1] and self.spotframes[1]:IsVisible() then
        for i, spotframe in ipairs(self.spotframes) do
          spotframe:Hide()
        end
      end

      if self.db.profile.showallsteps then
        if ZygorGuidesViewerFrameScrollScrollBar:GetValue() < 1 then
          ZygorGuidesViewerFrameScrollScrollBar:SetValue(self.CurrentStepNum)
        end
        ZygorGuidesViewerFrameScrollScrollBar:Show()
      else
        ZygorGuidesViewerFrameScrollScrollBar:Hide()
      end

      if full then
        ZygorGuidesViewerFrameScrollScrollBar:SetMinMaxValues(
          1,
          #self.CurrentGuide.steps > 0 and #self.CurrentGuide.steps or 1
        )
        ZygorGuidesViewerFrame_Skipper_Step:SetText(self.CurrentStepNum)
        ZygorGuidesViewerFrame_Border_SectionTitle:SetText(self.CurrentGuide.title_short)
      end

      --ZygorGuidesViewerFrame_Border_TitleBar_PrevButton:Show()
      --ZygorGuidesViewerFrame_Border_TitleBar_NextButton:Show()
      --ZygorGuidesViewerFrame_Border_TitleBar_Step:Show()
      --ZygorGuidesViewerFrame_Border_TitleBar_StepText:SetText(self.CurrentStepNum)
      --ZygorGuidesViewerFrame_Border_TitleBar_StepText:Show()

      ZygorGuidesViewerFrameScroll:Show()
      ZygorGuidesViewerFrame_MissingText:Hide()

      local totalheight = 0

      local frame
      local stepnum, stepdata

      local firststep = self.db.profile.showallsteps
          and math.floor(ZygorGuidesViewerFrameScrollScrollBar:GetValue())
        or self.CurrentStepNum
      if firststep < 1 then
        firststep = 1
      end
      local laststep = self.db.profile.showallsteps and #self.CurrentGuide.steps
        or self.CurrentStepNum + self.db.profile.showcountsteps - 1

      --self:Debug("first step "..firststep..", last step "..laststep)
      -- run through buttons and assign steps for them

      local nomoredisplayed = false

      for stepbuttonnum = 1, self.StepLimit do
        repeat
          --frame = _G['ZygorGuidesViewerFrame_Step'..stepbuttonnum]
          frame = self.stepframes[stepbuttonnum]

          stepnum = firststep + stepbuttonnum - 1

          -- show this button at all?
          if
            stepnum >= firststep
            and stepnum <= laststep
            and stepnum <= #self.CurrentGuide.steps
          then
            local stepdata = self.CurrentGuide.steps[stepnum]
            assert(stepdata, 'UpdateFrame: No data for step ' .. stepnum)

            if nomoredisplayed then
              frame:Hide()
              break --continue
            end

            --[[
					if not self.stepchanged and not stepdata:NeedsUpdating() or (nomoredisplayed and not frame:IsVisible()) then
						break --continue
					end
					--]]
            --print("Displaying step "..stepnum)

            frame.stepnum = stepnum
            frame.step = stepdata

            --#### position step frame

            frame:SetWidth(
              self.db.profile.showallsteps and ZygorGuidesViewerFrameScrollChild:GetWidth()
                or ZygorGuidesViewerFrameScroll:GetWidth()
            ) -- this is needed so the text lines below can access proper widths

            -- out of screen space? bail.
            -- but only in all steps mode!
            local top = frame:GetTop()
            local bottom = ZygorGuidesViewerFrameScroll:GetBottom()
            if self.db.profile.showallsteps and top and bottom and top < bottom then
              frame:Hide()
              nomoredisplayed = true
              break --continue!
            end

            --#### fill it with text

            local changed, dirty = stepdata:Translate()
            if dirty then
              self.frameNeedsUpdating = true
              self:SetWaypoint()
            end

            local line = 1

            if stepdata.requirement or self.db.profile.stepnumbers then
              local numbertext = self.db.profile.stepnumbers and L['step_num']:format(stepnum)
              local reqtext = stepdata.requirement
                and (
                  (stepdata:AreRequirementsMet() and '|cff44aa44' or '|cffbb0000')
                  .. '('
                  .. (table.concat(stepdata.requirement, L['stepreqor'])):gsub(
                    '!([a-zA-Z ]+)',
                    L['req_not']:format('%1')
                  )
                  .. ')'
                )
              local leveltext = (
                stepdata.level
                and stepdata.level > 0
                and self.db.profile.stepnumbers
              ) and L['step_level']:format(stepdata.level or '?')

              frame.lines[line].label:SetPoint('TOPLEFT')
              frame.lines[line].label:SetPoint('TOPRIGHT')
              frame.lines[line].label:SetText(
                (numbertext or '') .. (leveltext or '') .. (reqtext or '')
              )
              --frame.lines[line].label:SetMultilineIndent(1)
              frame.lines[line].goal = nil
              frame.lines[line].label:SetFont(FONT, round(self.db.profile.fontsecsize))
              line = line + 1
            else
              frame.lines[line].label:SetPoint('TOPLEFT', ZGV.ICON_INDENT, 0)
              frame.lines[line].label:SetPoint('TOPRIGHT')
              frame.lines[line].label:SetFont(FONT, self.db.profile.fontsize)
            end

            if stepdata:AreRequirementsMet() or self.db.profile.showwrongsteps then
              --#### insert goals

              for i, goal in ipairs(stepdata.goals) do
                if goal:GetStatus() ~= 'hidden' then
                  --steptext = steptext .. ("  "):rep(goal.indent or 0) .. goal:GetText() .. "|n"
                  local indent = ('  '):rep(goal.indent or 0)
                  --local goaltxt = goal:GetText(stepnum>=self.CurrentStepNum)
                  local goaltxt = goal:GetText(true)
                  if goaltxt ~= '?' or (goal.action == 'info') then
                    if goal.action == 'info' then
                      frame.lines[line].label:SetFont(FONT, round(self.db.profile.fontsecsize))
                      frame.lines[line].label:SetText(indent .. '|cffeeeecc' .. goal.info .. '|r')
                      frame.lines[line].goal = nil
                    else
                      local link = (
                        (goal.tooltip and not self.db.profile.tooltipsbelow)
                        or (goal.x and not self.db.profile.windowlocked)
                        or goal.image
                      )
                          and ' |cffdd44ff*|r'
                        or ''

                      frame.lines[line].label:SetFont(FONT, self.db.profile.fontsize)
                      frame.lines[line].label:SetText(indent .. goaltxt .. link)
                      frame.lines[line].goal = goal
                    end
                    line = line + 1
                    --frame.lines[line].label:SetMultilineIndent(1)

                    if self.db.profile.tooltipsbelow and goal.tooltip then
                      frame.lines[line].label:SetFont(FONT, round(self.db.profile.fontsecsize))
                      frame.lines[line].label:SetText(
                        indent .. '|cffeeeecc' .. goal.tooltip .. '|r'
                      )
                      --frame.lines[line].label:SetMultilineIndent(1)
                      frame.lines[line].goal = nil
                      line = line + 1
                    end
                  end
                end
              end

              if self.CurrentGuide then
                local stepnum, stepdata

                local firststep = (showallsteps or self.CurrentStepNum) or 1
                firststep = max(1, firststep)
                local laststep = showallsteps and #self.CurrentGuide.steps
                  or self.CurrentStepNum + self.db.profile.showcountsteps - 1
                laststep = min(laststep, #self.CurrentGuide.steps)
                local diff = #self.CurrentGuide.steps - laststep
                if ZGV.db.profile.showcountsteps > 1 then
                  if laststep == #self.CurrentGuide.steps then
                    laststep = min(laststep, #self.CurrentGuide.steps) - diff - 1
                  end
                end

                local stickies
                if not self.db.profile.showallsteps then
                  stickies, changed = self:GetStickiesAt(firststep, laststep)

                  if changed then
                    self:SendMessage('ZGV_STEP_CHANGED', num)
                  end
                end
                self.CurrentStickies = stickies
              end

              local stickySep = nil

              if stepdata.stickies then
                for i, sticky in ipairs(stepdata.stickies) do
                  if
                    sticky:AreRequirementsMet()
                    and sticky:CanBeSticky()
                    and not sticky:IsComplete()
                  then
                    for i, goal in ipairs(sticky.goals) do
                      if goal:GetStatus() ~= 'hidden' then
                        if stickySep == nil then
                          stickySep = true
                          frame.lines[line].label:SetFont(FONT, self.db.profile.fontsize)
                          frame.lines[line].label:SetText('|cffeeeecc' .. '- Stickies - ' .. '|r')
                          frame.lines[line].goal = nil
                          line = line + 1
                        end
                        --steptext = steptext .. ("  "):rep(goal.indent or 0) .. goal:GetText() .. "|n"
                        local indent = ('  '):rep(goal.indent or 0)
                        --local goaltxt = goal:GetText(stepnum>=self.CurrentStepNum)
                        local goaltxt = goal:GetText(true)
                        if goaltxt ~= '?' or (goal.action == 'info') then
                          if goal.action == 'info' then
                            frame.lines[line].label:SetFont(
                              FONT,
                              round(self.db.profile.fontsecsize)
                            )
                            frame.lines[line].label:SetText(
                              indent .. '|cffeeeecc' .. goal.info .. '|r'
                            )
                            frame.lines[line].goal = nil
                          else
                            local link = (
                              (goal.tooltip and not self.db.profile.tooltipsbelow)
                              or (goal.x and not self.db.profile.windowlocked)
                              or goal.image
                            )
                                and ' |cffdd44ff*|r'
                              or ''

                            frame.lines[line].label:SetFont(FONT, self.db.profile.fontsize)
                            frame.lines[line].label:SetText(indent .. goaltxt .. link)
                            frame.lines[line].goal = goal
                          end
                          line = line + 1
                          --frame.lines[line].label:SetMultilineIndent(1)

                          if self.db.profile.tooltipsbelow and goal.tooltip then
                            frame.lines[line].label:SetFont(
                              FONT,
                              round(self.db.profile.fontsecsize)
                            )
                            frame.lines[line].label:SetText(
                              indent .. '|cffeeeecc' .. goal.tooltip .. '|r'
                            )
                            --frame.lines[line].label:SetMultilineIndent(1)
                            frame.lines[line].goal = nil
                            line = line + 1
                          end
                        end
                      end
                    end
                  end
                end
              end

              --[[ -- no more
						-- info line
						if stepdata.info then
							frame.lines[line].label:SetText("|cffeeeecc"..stepdata.info.."|r")
							--frame.lines[line].label:SetMultilineIndent(0)
							frame.lines[line].label:SetFont(FONT,self.db.profile.fontsize)
							frame.lines[line].goal = nil
							line=line+1
						end
						--]]

              -- (level #)
              --[[
						if self.db.profile.showsteplevels then
							frame.lines[line].label:SetText()
							frame.lines[line].goal = nil
							line=line+1
						end
						--]]
            end

            local TMP_TRUNCATE = true
            local heightleft = 400
            if self.db.profile.showallsteps and TMP_TRUNCATE then
              if stepbuttonnum > 1 then
                local stepbottom = self.stepframes[stepbuttonnum - 1]:GetBottom()
                local scrollbottom = ZygorGuidesViewerFrameScroll:GetBottom()
                if stepbottom and scrollbottom then
                  heightleft = stepbottom - scrollbottom - 2 * self.STEPMARGIN_Y - 5
                else
                  heightleft = 0
                  self:Debug(
                    'Error in step height calculation! step '
                      .. stepbuttonnum
                      .. ' stepbottom='
                      .. tostring(stepbottom)
                      .. ' scrollbottom='
                      .. tostring(scrollbottom)
                      .. ', forcing update'
                  )
                  self.frameNeedsUpdating = true
                end
              end

              if heightleft < self.MIN_STEP_HEIGHT then
                frame:Hide()
                nomoredisplayed = true
                break --continue
              end
            end

            local height = 0
            --frame.goallines={}
            local textheight
            frame.truncated = nil
            local abort
            for l = 1, linecount do
              local lineframe = frame.lines[l]
              local text = lineframe.label
              if l < line and not frame.truncated then
                text:SetWidth(frame:GetWidth() - ICON_INDENT - 2 * ZGV.STEPMARGIN_X)
                textheight = text:GetHeight()
                height = height + (height > 0 and STEP_LINE_SPACING or 0) + textheight
                --text:SetWidth(ZygorGuidesViewerFrameScroll:GetWidth()-30)

                if TMP_TRUNCATE and self.db.profile.showallsteps and height > heightleft then
                  lineframe.goal = nil
                  if l <= 2 then
                    abort = true
                    break
                  else
                    frame.truncated = true
                    frame.lines[l - 1].label:SetText('   . . .')
                    frame.lines[l - 1].goal = nil
                    lineframe:Hide()
                    height = height - textheight - STEP_LINE_SPACING
                  end
                else
                  lineframe:Show()
                  --if lineframe.goal then frame.goallines[lineframe.goal.num]=lineframe end
                  lineframe:SetHeight(textheight + STEP_LINE_SPACING)
                end
              else
                lineframe:Hide()
                lineframe.goal = nil
              end
            end

            if abort then
              frame:Hide()
              nomoredisplayed = true
              break --continue
            end

            --#### display it properly

            if height < self.MIN_STEP_HEIGHT then
              frame.lines[1]:SetPoint(
                'TOPLEFT',
                ZGV.STEPMARGIN_X,
                -(self.MIN_STEP_HEIGHT - height) / 2 - 0.6
              )
              frame.lines[1]:SetPoint(
                'TOPRIGHT',
                -ZGV.STEPMARGIN_X,
                -(self.MIN_STEP_HEIGHT - height) / 2 - 0.6
              )
              height = self.MIN_STEP_HEIGHT
            else
              frame.lines[1]:SetPoint('TOPLEFT', frame, ZGV.STEPMARGIN_X, -ZGV.STEPMARGIN_Y)
              frame.lines[1]:SetPoint('TOPRIGHT', frame, -ZGV.STEPMARGIN_X, -ZGV.STEPMARGIN_Y)
            end

            if not frame.truncated or not TMP_TRUNCATE then
              frame:SetHeight(height + 3 * self.STEPMARGIN_Y)
            else
              frame:SetHeight(heightleft + 2 * self.STEPMARGIN_Y)
            end

            --end

            -- current step stuff

            if stepbuttonnum > 1 then
              totalheight = totalheight + STEP_SPACING
            end
            totalheight = totalheight + frame:GetHeight()

            --[[
					if self.db.profile.showallsteps and totalheight>ZygorGuidesViewerFrameScroll:GetHeight() then
						nomoredisplayed=true
						frame:Hide()
						break --continue
					end
					--]]

            if self.db.profile.showallsteps and frame.truncated then
              nomoredisplayed = true
            end

            --oookay, frame is visible, let's fill it for real
            frame:Show()

            if stepdata ~= self.CurrentStep then
              for l = 1, linecount do
                frame.lines[l].back:Hide()
                frame.lines[l].icon:Hide()
              end
            end

            if stepnum == self.CurrentStepNum then
            --frame:EnableMouse(0)
            --frame:SetScript("OnClick",nil)
            else
              --frame:EnableMouse(1)
            end

            if self.db.profile.showallsteps then
              frame:SetAlpha(stepnum < self.CurrentStepNum and 0.4 or 1.0)
            else
              if stepbuttonnum == 1 then
                frame:SetAlpha(1.0)
              else
                frame:SetAlpha(
                  0.8 - 0.4 * ((stepbuttonnum - 1) / (self.db.profile.showcountsteps - 1))
                )
              end
            end

            if self.db.profile.disableBackDrop then
              frame.border:SetBackdrop(nil)
            elseif stepnum == self.CurrentStepNum then
              frame.border:SetBackdrop({
                edgeFile = 'Interface\\Addons\\ZygorGuidesViewer\\skin\\popup_border_active',
                edgeSize = 16,
              })
            else
              frame.border:SetBackdrop({
                edgeFile = 'Interface\\Addons\\ZygorGuidesViewer\\skin\\popup_border',
                edgeSize = 16,
              })
            end
            if stepdata:AreRequirementsMet() then
              if stepdata:IsComplete() then
                frame:SetBackdropColor(
                  fromRGBmul_a(self.db.profile.goalbackcomplete, 0.5, self.db.profile.stepbackalpha)
                )
                --frame:SetBackdropColor(0,0.7,0,0.5)
                frame.border:SetBackdropBorderColor(1, 1, 1, 1)
              elseif self.db.profile.showobsolete and stepdata:IsObsolete() then
                frame:SetBackdropColor(
                  fromRGBmul_a(self.db.profile.goalbackobsolete, 0.5, self.db.profile.stepbackalpha)
                )
                frame.border:SetBackdropBorderColor(1, 1, 1, 1)
              elseif self.db.profile.skipauxsteps and stepdata:IsAuxiliarySkippable() then
                frame:SetBackdropColor(
                  fromRGBmul_a(self.db.profile.goalbackaux, 0.5, self.db.profile.stepbackalpha)
                )
                frame.border:SetBackdropBorderColor(1, 1, 1, 1)
              else
                frame:SetBackdropColor(0.0, 0.0, 0.0, self.db.profile.stepbackalpha)
                frame.border:SetBackdropBorderColor(1, 1, 1, 1)
              end
            else
              frame:SetBackdropColor(0.5, 0.0, 0.0, self.db.profile.stepbackalpha)
              frame.border:SetBackdropBorderColor(0.5, 0.0, 0.0, 0.5)
            end

            if self.db.profile.hidestepborders then
              frame.border:Hide()
            else
              frame.border:Show()
            end

          --text:Show()
          else -- not showing this one
            if frame then
              frame:Hide()
              --[[
						local prename = "ZygorGuidesViewerFrame_Step"..stepnum.."_Text"
						for line=1,10 do
							local text = _G[prename..line]
							text:SetHeight(0.1)
						end
						--]]
              --[[
						frame:SetHeight(0)
						frame:ClearAllPoints()
						frame:SetPoint("TOPLEFT")
						--]]
            end
          end
        until true
      end

      self.stepchanged = false

      -- set minimum frame size to one step
      minh = self.stepframes[1]:GetHeight() + 40

      self:UpdateFrameCurrent()

      ZygorGuidesViewerFrame_Skipper:Show()
      ZygorGuidesViewerFrame_Skipper.mustbevisible = true

      --self:HighlightCurrentStep()

      -- steps displayed, clear the remaining slots
    else -- no current guide?
      ZygorGuidesViewerFrame_Border_SectionTitle:SetText()

      --ZygorGuidesViewerFrame_LocationLabel:Hide()
      --ZygorGuidesViewerFrame_LevelLabel:Hide()
      ZygorGuidesViewerFrame_MissingText:Show()

      --ZygorGuidesViewerFrame_Divider2:Hide()

      local guides = self:GetGuides()
      if #guides > 0 then
        ZygorGuidesViewerFrame_MissingText:SetText(L['miniframe_notselected'])
      else
        ZygorGuidesViewerFrame_MissingText:SetText(L['miniframe_notloaded'])
      end
    end
  elseif self.db.profile.displaymode == 'gold' then
    local x, y = GetPlayerMapPosition('player')
    local d = GetPlayerFacing()
    if x == gold_ox and y == gold_oy and d == gold_od and not full then
      return
    end
    gold_ox, gold_oy, gold_od = x, y, d

    -- get rid of tooltips, before they get messed up
    if ZGV.hasTooltipOverSpotLink then
      GameTooltip:Hide()
      ZGV.hasTooltipOverSpotLink = nil
    end

    -- hide step frames, if visible
    if self.stepframes[1]:IsVisible() then
      for i, stepframe in ipairs(self.stepframes) do
        stepframe:Hide()
      end
    end

    local spots
    if self.db.profile.golddistmode == 1 then
      spots = ZGV:GetMapSpotsInRange()
    elseif self.db.profile.golddistmode == 2 then
      spots = ZGV:GetMapSpotsInZone()
    else
      spots = ZGV:GetAllMapSpots()
    end

    if #spots > 0 then
      if full then
        ZygorGuidesViewerFrameScroll:Show()
        ZygorGuidesViewerFrame_MissingText:Hide()
        if ZygorGuidesViewerFrameScrollScrollBar:GetValue() < 1 then
          ZygorGuidesViewerFrameScrollScrollBar:SetValue(1)
        end
        ZygorGuidesViewerFrameScrollScrollBar:Show()
        ZygorGuidesViewerFrameScrollScrollBar:SetMinMaxValues(1, #spots)
        if ZygorGuidesViewerFrame_Skipper then
          ZygorGuidesViewerFrame_Skipper:Hide()
        end
        ZygorGuidesViewerFrame_Border_SectionTitle:SetText('Gold Spots')
      end
    else -- no gold guides or no spots in range
      ZygorGuidesViewerFrameScroll:Hide()
      ZygorGuidesViewerFrame_MissingText:Show()

      if #self.registeredmapspotsets > 0 then
        ZygorGuidesViewerFrame_MissingText:SetText(L['gold_missing_nospotsinrange'])
      else
        ZygorGuidesViewerFrame_MissingText:SetText(L['gold_missing_noguidesloaded'])
      end
    end

    local totalheight = 0

    local frame
    local spotnum

    local firstspot = math.floor(ZygorGuidesViewerFrameScrollScrollBar:GetValue())
    if firstspot < 1 then
      firstspot = 1
    end
    local lastspot = #spots

    --self:Debug("first step "..firststep..", last step "..laststep)
    -- run through buttons and assign steps for them

    local nomoredisplayed = false

    for spotbuttonnum = 1, self.StepLimit do
      repeat
        --frame = _G['ZygorGuidesViewerFrame_Step'..stepbuttonnum]
        frame = self.spotframes[spotbuttonnum]
        assert(frame, 'Out of spot frames at ' .. spotbuttonnum)

        spotnum = firstspot + spotbuttonnum - 1

        -- show this button at all?
        if spotnum >= firstspot and spotnum <= lastspot and spotnum <= #spots then
          local spotdata = spots[spotnum]
          assert(spotdata, 'UpdateFrame: No data for spot ' .. spotnum)

          if nomoredisplayed then
            frame:Hide()
            break --continue
          end

          frame.spotnum = spotnum
          frame.spot = spotdata

          --#### position step frame

          frame:SetWidth(ZygorGuidesViewerFrameScrollChild:GetWidth()) -- this is needed so the text lines below can access proper widths

          -- out of screen space? bail.
          -- but only in all steps mode!
          local top = frame:GetTop()
          local bottom = ZygorGuidesViewerFrameScroll:GetBottom()
          if top and bottom and top < bottom then
            frame:Hide()
            nomoredisplayed = true
            break --continue!
          end

          --#### fill it with text

          -- no translation here
          --[[
				local changed,dirty = stepdata:Translate()
				if dirty then self.frameNeedsUpdating=true end
				--]]

          local line = 1

          assert(frame.lines[line], 'Out of lines (' .. line .. ') in spot frame ' .. spotbuttonnum)

          frame.lines[line].label:SetFont(FONT, round(self.db.profile.fontsize))

          -- cardinal names
          --frame.lines[line].label:SetText(("|cffffbb00%s|r (%s %s)"):format(spotdata.title or "?",ZGV.FormatDistance(spotdata.waypoint.minimapFrame.dist),GetCardinalDirName(Astrolabe:GetDirectionToIcon(spotdata.waypoint.minimapFrame))))

          -- icons
          local dirnum = GetCardinalDirNum(
            -Astrolabe:GetDirectionToIcon(spotdata.waypoint.minimapFrame) + GetPlayerFacing()
          ) - 1 --:30:30:0:0:32:32:0:0:0:0
          local dirnum2 = dirnum > 8 and 16 - dirnum or dirnum
          local arrow = ('|Tinterface\\addons\\ZygorGuidesViewer\\skin\\arrow-mini-multi:20:20:0:0:32:512:%d:%d:%d:%d|t'):format(
            dirnum > 8 and 32 or 0,
            dirnum > 8 and 0 or 32,
            dirnum2 * 32,
            (dirnum2 + 1) * 32
          )
          frame.lines[line].label:SetText(
            ('%s |cffffbb00%s|r (%s)'):format(
              arrow,
              spotdata.title or '?',
              ZGV.FormatDistance(spotdata.waypoint.minimapFrame.dist)
            )
          )

          line = line + 1

          --[[
				frame.lines[line].label:SetFont(FONT,round(self.db.profile.fontsize))
				frame.lines[line].label:SetText(("|cffffff00%s %s,%s|r"):format(spotdata.map,spotdata.x,spotdata.y))
				line=line+1
				--]]

          if spotdata.desc then
            frame.lines[line].label:SetFont(FONT, round(self.db.profile.fontsize))
            frame.lines[line].label:SetText(('%s'):format(spotdata.desc))
            line = line + 1
          end

          if spotdata.objects then
            for s, source in ipairs(itemsources) do
              local objs = spotdata:GetObjectsOfType(source, true)
              if objs then
                local mobs = source == 'drop' and spotdata.mobs
                local mobtext
                if mobs then
                  mobtext = ''
                  for i, mob in ipairs(spotdata.mobs) do
                    if #mobtext > 0 then
                      mobtext = mobtext .. ', '
                    end
                    mobtext = mobtext .. mob.name
                  end
                elseif spotdata.vendorid then
                  mobtext = spotdata.vendor
                end

                --[[
							-- all in one line; tidy but impractical
							local header = L['gold_header_'..source]:format(mobtext or "mob")
							local str=""
							for o,obj in ipairs(objs) do
								if not obj.hidden then
									if obj.item.id then
										str = str .. "|Hitem:"..obj.item.id.."|h"..(obj.icon or "item").."|h "
									else
										str = str .. " ["..obj.item.name.."]"
									end
								end
							end

							if #str>0 then
								frame.lines[line].label:SetFont(FONT,round(self.db.profile.fontsize))
								--frame.lines[line].label:SetText("<html><body><p>"..("|cffdddd66%s |r%s"):format(header,str).."</p></body></html>")
								frame.lines[line].label:SetText(("|cffdddd66%s |r%s"):format(header,str))
								line=line+1
							end
							--]]

                local goodobjs = {}
                for o, obj in ipairs(objs) do
                  if not obj.hidden then
                    tinsert(goodobjs, obj)
                  end
                end

                if #goodobjs then
                  frame.lines[line].label:SetFont(FONT, round(self.db.profile.fontsize))
                  --frame.lines[line].label:SetText("<html><body><p>"..("|cffdddd66%s |r%s"):format(header,str).."</p></body></html>")
                  frame.lines[line].label:SetText(
                    ('|cffdddd66%s|r'):format(L['gold_header_' .. source]:format(mobtext or 'mob'))
                  )
                  line = line + 1

                  for o, obj in ipairs(goodobjs) do
                    local str
                    if obj.item.id then
                      str = '|Hitem:'
                        .. obj.item.id
                        .. '|h'
                        .. (obj.icon or 'item')
                        .. ' '
                        .. (obj.string or '?')
                        .. '|h '
                    else
                      str = obj.item.name
                    end

                    if obj.toohard then
                      str = '|cffff0000' .. str .. '|r'
                    end

                    frame.lines[line].label:SetFont(FONT, round(self.db.profile.fontsize * 1.0))
                    frame.lines[line].label:SetText(str)
                    frame.lines[line].label:SetHyperlinksEnabled(false)
                    frame.lines[line].label.reenableHyperlinks = true
                    line = line + 1
                  end
                end
              end
            end
          end

          local TMP_TRUNCATE = true
          local heightleft = 400
          if TMP_TRUNCATE then
            if spotbuttonnum > 1 then
              local spotbottom = self.spotframes[spotbuttonnum - 1]:GetBottom()
              local scrollbottom = ZygorGuidesViewerFrameScroll:GetBottom()
              if spotbottom and scrollbottom then
                heightleft = spotbottom - scrollbottom - 2 * self.STEPMARGIN_Y - 5
              else
                heightleft = 0
                self:Debug(
                  'Error in spot height calculation! spot '
                    .. spotbuttonnum
                    .. ' spotbottom='
                    .. tostring(spotbottom)
                    .. ' scrollbottom='
                    .. tostring(scrollbottom)
                    .. ', forcing update'
                )
                --self.frameNeedsUpdating=true
              end
            end

            if heightleft < self.MIN_STEP_HEIGHT then
              frame:Hide()
              nomoredisplayed = true
              break --continue
            end
          end

          local height = 0
          --frame.goallines={}
          local textheight
          frame.truncated = nil
          local abort

          for l = 1, linecount do
            local lineframe = frame.lines[l]
            local text = lineframe.label
            if l < line and not frame.truncated then
              text:SetWidth(frame:GetWidth() - ICON_INDENT - 2 * ZGV.STEPMARGIN_X)

              -- old non-HTML stuff
              --textheight = text:GetHeight()
              textheight = text:GetRegions():GetHeight()
              text:SetHeight(textheight)

              height = height + (height > 0 and STEP_LINE_SPACING or 0) + textheight
              --text:SetWidth(ZygorGuidesViewerFrameScroll:GetWidth()-30)

              if TMP_TRUNCATE and height > heightleft then
                if l <= 2 then
                  abort = true
                  break
                else
                  frame.truncated = true
                  frame.lines[l - 1].label:SetText('   . . .')
                  lineframe:Hide()
                  height = height - textheight - STEP_LINE_SPACING
                end
              else
                lineframe:Show()
                --if lineframe.goal then frame.goallines[lineframe.goal.num]=lineframe end
                lineframe:SetHeight(textheight + STEP_LINE_SPACING)
              end
            else
              lineframe:Hide()
            end
          end

          if abort then
            frame:Hide()
            nomoredisplayed = true
            break --continue
          end

          --self:Print(("spot %d, height %s"):format(spotbuttonnum,height))

          --#### display it properly

          if height < self.MIN_STEP_HEIGHT then
            frame.lines[1]:SetPoint(
              'TOPLEFT',
              ZGV.STEPMARGIN_X,
              -(self.MIN_STEP_HEIGHT - height) / 2 - 0.6
            )
            frame.lines[1]:SetPoint(
              'TOPRIGHT',
              -ZGV.STEPMARGIN_X,
              -(self.MIN_STEP_HEIGHT - height) / 2 - 0.6
            )
            height = self.MIN_STEP_HEIGHT
          else
            frame.lines[1]:SetPoint('TOPLEFT', frame, ZGV.STEPMARGIN_X, -ZGV.STEPMARGIN_Y)
            frame.lines[1]:SetPoint('TOPRIGHT', frame, -ZGV.STEPMARGIN_X, -ZGV.STEPMARGIN_Y)
          end

          if not frame.truncated or not TMP_TRUNCATE then
            frame:SetHeight(height + 3 * self.STEPMARGIN_Y)
          else
            frame:SetHeight(heightleft + 2 * self.STEPMARGIN_Y)
          end

          --end

          if spotbuttonnum > 1 then
            totalheight = totalheight + STEP_SPACING
          end
          totalheight = totalheight + frame:GetHeight()

          if frame.truncated then
            nomoredisplayed = true
          end

          --oookay, frame is visible, let's fill it for real
          frame:Show()

          frame:SetBackdropColor(0.0, 0.0, 0.0, self.db.profile.stepbackalpha)

          if self.db.profile.hidestepborders then
            frame.border:Hide()
          else
            frame.border:Show()
            if self.db.profile.disableBackDrop then
              frame.border:SetBackdrop(nil)
            else
              frame.border:SetBackdrop({
                edgeFile = 'Interface\\Addons\\ZygorGuidesViewer\\skin\\popup_border_active',
                edgeSize = 16,
              })
            end
            frame.border:SetBackdropBorderColor(1, 1, 1, 1)
          end

          ZygorGuidesViewerFrame_Skipper:Hide()
          ZygorGuidesViewerFrame_Skipper.mustbevisible = false

        --text:Show()
        else -- not showing this one
          if frame then
            frame:Hide()
            --[[
					local prename = "ZygorGuidesViewerFrame_Step"..stepnum.."_Text"
					for line=1,10 do
						local text = _G[prename..line]
						text:SetHeight(0.1)
					end
					--]]
            --[[
					frame:SetHeight(0)
					frame:ClearAllPoints()
					frame:SetPoint("TOPLEFT")
					--]]
          end
        end
      until true
    end

    self.stepchanged = false

    -- set minimum frame size to one step
    minh = self.spotframes[1]:GetHeight() + 40

    --self:HighlightCurrentStep()

    -- steps displayed, clear the remaining slots

    --ZygorGuidesViewerFrame_Border_TitleBar_PrevButton:Show()
    --ZygorGuidesViewerFrame_Border_TitleBar_NextButton:Show()
    --ZygorGuidesViewerFrame_Border_TitleBar_Step:Show()
    --ZygorGuidesViewerFrame_Border_TitleBar_StepText:SetText(self.CurrentStepNum)
    --ZygorGuidesViewerFrame_Border_TitleBar_StepText:Show()
  end

  if minh < 100 then
    minh = 100
  end
  self.Frame:SetMinResize(260, minh)
  if self.Frame:GetHeight() < minh - 0.01 then
    self.Frame:SetHeight(minh)
  end

  self:ResizeFrame()

  if self.delayFlash and self.delayFlash > 0 then
    self.delayFlash = 2 --ready to flash!
    --ZygorGuidesViewerFrame_bdflash:StartRGB(1,1,1,1,0,1,0,1)
  end
end

function me:ClearFrameCurrent()
  if InCombatLockdown() then
    return
  end
  for i = 1, linecount do
    local actname = 'ZygorGuidesViewerFrame_Act' .. i
    local action = _G[actname .. 'Action']
    local petaction = _G[actname .. 'PetAction']
    local cooldown = _G[actname .. 'ActionCooldown']

    action:Hide()
    petaction:Hide()
    cooldown:Hide()
  end
end

local actionicon = {
  ['accept'] = 5,
  ['turnin'] = 6,
  ['kill'] = 7,
  ['get'] = 8,
  ['collect'] = 8,
  ['trash'] = 8,
  ['buy'] = 8,
  ['goal'] = 9,
  ['home'] = 10,
  ['fpath'] = 11,
  ['goto'] = 12,
  ['talk'] = 13,
}
setmetatable(actionicon, {
  __index = function()
    return 2
  end,
})

function me:UpdateFrameCurrent()
  -- current step!

  if self.CurrentStep then -- hey, it may be missing, if the whole guide is for another class
    --local mapped = self.CurrentStep.x

    --[[
		if ZGV.db.profile.colorborder then
			local done,possible = ZGV.CurrentStep:IsComplete()
			if done then		ZygorGuidesViewerFrame_Border:SetBackdropBorderColorRGB(ZGV.db.profile.goalbackcomplete)
			elseif possible then	ZygorGuidesViewerFrame_Border:SetBackdropBorderColorRGB(ZGV.db.profile.goalbackincomplete)
			else			ZygorGuidesViewerFrame_Border:SetBackdropBorderColor(0.7,0.7,0.7,1)
			end
		else ZygorGuidesViewerFrame_Border:SetBackdropBorderColor(0.7,0.7,0.7,1)
		end
		--]]

    --[[
		ZygorGuidesViewerFrame_ActiveStep:SetBackdrop({bgFile = "Interface/Tooltips/UI-Tooltip-Background", 
							    edgeFile = "Interface/Tooltips/UI-Tooltip-Border", 
							    tile = true, tileSize = 16, edgeSize = 16, 
							    insets = { left = 4, right = 4, top = 4, bottom = 4 }})
		--]]

    --[[
		if self.CurrentStep.requirement then
			ZygorGuidesViewerFrame_ActiveStep_Line0:SetText((self.CurrentStep:AreRequirementsMet() and "|cff88cc88" or "|cffbb0000") .. "(" .. table.concat(self.CurrentStep.requirement,L["stepreqor"]) .. ")")
			height = height + ZygorGuidesViewerFrame_ActiveStep_Line0:GetHeight()+STEP_LINE_SPACING
			ZygorGuidesViewerFrame_ActiveStep_Line1:ClearAllPoints()
			ZygorGuidesViewerFrame_ActiveStep_Line1:SetPoint("TOPLEFT",ZygorGuidesViewerFrame_ActiveStep_Line0,"BOTTOMLEFT",-ICON_INDENT,-STEP_LINE_SPACING)
			ZygorGuidesViewerFrame_ActiveStep_Line1:SetPoint("TOPRIGHT",ZygorGuidesViewerFrame_ActiveStep_Line0,"BOTTOMRIGHT",0,-STEP_LINE_SPACING)
			ZygorGuidesViewerFrame_ActiveStep_Line0:Show()
		else
			ZygorGuidesViewerFrame_ActiveStep_Line1:ClearAllPoints()
			ZygorGuidesViewerFrame_ActiveStep_Line1:SetPoint("TOPLEFT",ZygorGuidesViewerFrame_ActiveStep)
			ZygorGuidesViewerFrame_ActiveStep_Line1:SetPoint("TOPRIGHT",ZygorGuidesViewerFrame_ActiveStep)
			ZygorGuidesViewerFrame_ActiveStep_Line0:Hide()
		end
		--]]

    local name, line, label, icon, back, clicker, anim_w2g, anim_w2r, action, petaction, cooldown, lastlabel
    local height = 0

    if not self.stepframes[1].stepnum then
      return
    end

    local framenum = (self.CurrentStepNum - self.stepframes[1].stepnum + 1)
    if framenum < 1 or framenum > self.StepLimit then
      self.CurrentStepframeNum = nil
      return self:ClearFrameCurrent()
    else
      self.CurrentStepframeNum = framenum
    end

    local stepframe = self.stepframes[framenum]
    if not stepframe.lines[1].icon then
      ZygorGuidesViewerFrame_Step_Setup(framenum)
    end

    if not stepframe:IsVisible() then
      return self:ClearFrameCurrent()
    end

    --textline(1):ClearAllPoints()
    --textline(1):SetPoint("TOPLEFT",stepframe,"TOPLEFT",0,self.CurrentStep.requirement and -textline(1):GetHeight()-STEP_LINE_SPACING or 0)
    --textline(1):SetPoint("TOPRIGHT",stepframe,"TOPRIGHT",0,self.CurrentStep.requirement and -textline(1):GetHeight()-STEP_LINE_SPACING or 0)

    if self.BUTTONS_INLINE then
      if not InCombatLockdown() then
        self.actionsvisible = false
      end
    end

    for i = 1, linecount, 1 do -- update all lines
      --local linenum = (self.CurrentStep.requirement and i+1 or i)

      line = stepframe.lines[i]
      if not line then
        break
      end
      label = line.label
      icon = line.icon
      back = line.back
      clicker = line.clicker
      anim_w2g = line.anim_w2g
      anim_w2r = line.anim_w2r

      -- don't even touch this in combat.
      local actname = 'ZygorGuidesViewerFrame_Act' .. i
      action = _G[actname .. 'Action']
      petaction = _G[actname .. 'PetAction']
      cooldown = _G[actname .. 'ActionCooldown']

      if line.goal then
        local goal = line.goal

        lastlabel = label

        --steptext = ("  "):rep(goal.indent or 0)
        --if i==1 then steptext = steptext .. self.CurrentStepNum .. ". " end
        --steptext = steptext .. goal:GetText(true)

        --steptext = string.gsub(steptext,"\t([a-z]+\. )","\t|cffffff88%1|r")
        --steptext = string.gsub(steptext,"\t",">")

        if not InCombatLockdown() and not stepframe.slideup:IsPlaying() then
          if goal:IsActionable() then
            --	cooldown:Show()
            --self:Debug("showing cooldown "..i)
            local vis

            if not self.BUTTONS_INLINE then
              action:Raise() --SetFrameLevel(ZygorGuidesViewerFrame:GetFrameLevel()+10)
              petaction:Raise() --petaction:Raise()--SetFrameLevel(ZygorGuidesViewerFrame:GetFrameLevel()+10)
            end

            if goal.castspell and goal.castspellid then
              action:SetAttribute('type1', 'spell')
              action:SetAttribute('spell1', goal.castspell)
              action.spellid = goal.castspellid
              _G[actname .. 'ActionIcon']:SetTexture(
                select(3, GetSpellInfo(goal.castspellid or goal.castspell))
                  or 'Interface\\Icons\\Spell_Nature_FaerieFire'
              )
              --action:SetScript("OnClick",function(self) PetActionButton_OnClick(self,"LeftButton") end)
              vis = true
              --	local start,dur,en = GetSpellCooldown(goal.castspellid or goal.castspell)
              --DoCooldown(cooldown,start,dur,en)
            elseif goal.useitem or goal.useitemid then
              action:SetAttribute('type1', 'item')
              action:SetAttribute(
                'item1',
                goal.useitemid and 'item:' .. goal.useitemid or goal.useitem
              )
              _G[actname .. 'ActionIcon']:SetTexture(
                select(10, GetItemInfo(goal.useitemid or goal.useitem))
                  or 'Interface\\Icons\\INV_Misc_Bag_08'
              )
              vis = true
              --local start,dur,en = GetItemCooldown(goal.useitemid or goal.useitem)
              --DoCooldown(cooldown,start,dur,en)
            elseif goal.script then
              action:SetAttribute('type1', 'macro')
              action:SetAttribute('macro', 'ZygorGuidesMacro' .. goal.num)
              _G[actname .. 'ActionIcon']:SetTexture(select(2, GetMacroInfo(goal.macro)))
              vis = true
            elseif goal.petaction then
              local num, name, subtext, tex = FindPetActionInfo(goal.petaction)
              if num then
                petaction:SetID(num)
                petaction.tooltipName = name
                petaction.tooltipSubtext = subtext
                --action:SetScript("OnClick",function(self) PetActionButton_OnClick(self,"LeftButton") end)
                _G[actname .. 'PetActionIcon']:SetTexture(tex)
                petaction:Show()
                petaction:ClearAllPoints()
                petaction:SetPoint(
                  'CENTER',
                  UIParent,
                  'BOTTOMLEFT',
                  icon:GetLeft() + 8,
                  icon:GetBottom() + 8
                )
                petaction:SetScale(self.db.profile.framescale)
              else
                petaction:Hide()
              end
            else
              error('IsActionable but no item/spell!')
              --[[
							if not InCombatLockdown() then
								action:Hide()
							end
							cooldown:Hide()
							--]]
            end

            if vis then
              action:Show()
              action:SetScale(self.db.profile.framescale)
              if self.BUTTONS_INLINE then
                action:SetParent(clicker)
                action:ClearAllPoints()
                action:SetPoint('CENTER', clicker, 'LEFT', 8, 0)
                self.actionsvisible = true
              else
                action:ClearAllPoints()
                action:SetPoint(
                  'CENTER',
                  UIParent,
                  'BOTTOMLEFT',
                  (icon:GetLeft() or 0) + 8,
                  (icon:GetBottom() or 0) + 8
                )
              end
            end

            --cooldown:Show()
          else
            action:Hide()
            petaction:Hide()
            cooldown:Hide()
          end

          -- cooldown flasher
          local DoCooldown = function(cooldown, start, dur, en)
            CooldownFrame_SetTimer(cooldown, start, dur, en)

            -- is this useless or what
            if not InCombatLockdown() then
              if dur > 0 then
                --cooldown:Show()
                --self.recentCooldownsPulsing[goal] = nil
                --self.recentCooldownsStarted[goal] = 1
                --self:Debug("pulse: showing")
              else
                --[[
								if not self.recentCooldownsPulsed[goal] and not self.recentCooldownsPulsed[goal] then
									self.recentCooldownPulses[goal] = self:ScheduleTimer("HideCooldown",1.0,{goal=goal,cooldown=cooldown})
									self:Debug("pulse: not pulsed, pulsing now and delaying")
								end

								if self.recentCooldownsStarted[goal] and self.recentCooldownsPulsing[goal] and self.recentCooldownsPulsing[goal]==1 then
									cooldown:Show()
									self:Debug("pulse: showing, awaiting delayed hiding")
								else
									cooldown:Hide()
								end
								--]]
              end
            else
              --cooldown:Show()
            end
          end
        end

        local status, detail = goal:GetStatus()

        if status == 'passive' then
          if goal.action == 'talk' then
            icon:SetIcon(actionicon[goal.action])
          else
            icon:SetIcon(1)
          end
          icon:SetDesaturated(false)
          back:SetVertexColor(0.0, 0.0, 0.0, 0)
        elseif status == 'incomplete' then
          local progress = type(detail) == 'number' and detail or 0

          local inc = self.db.profile.goalbackincomplete
          local pro = self.db.profile.goalbackprogressing
          local com = self.db.profile.goalbackcomplete
          local a = self.db.profile.goalbackincomplete.a
          local r, g, b = self.gradient3(
            self.db.profile.goalbackprogress and progress * 0.7 or 0,
            inc.r,
            inc.g,
            inc.b,
            pro.r,
            pro.g,
            pro.b,
            com.r,
            com.g,
            com.b,
            0.5
          )

          --local r,g,b,a = gradientRGBA(self.db.profile.goalbackincomplete,self.db.profile.goalbackcomplete,self.db.profile.goalbackprogress and progress*0.7 or 0)

          if goal.action ~= 'goto' and progress > (self.recentGoalProgress[goal] or 1) then
            if self.db.profile.goalupdateflash and self.frameNeedsResizing == 0 then
              anim_w2r.r, anim_w2r.g, anim_w2r.b, anim_w2r.a = r, g, b, a
              anim_w2r:Play()
              self:Debug('Progress: ' .. goal:GetText())
            end
          end
          icon:SetIcon(actionicon[goal.action])
          icon:SetDesaturated(false)
          if anim_w2r:IsDone() or not anim_w2r:IsPlaying() then
            back:SetVertexColor(r, g, b, a)
          end
          self.recentGoalProgress[goal] = progress
        elseif status == 'complete' then
          if not self.recentlyCompletedGoals[goal] then
            self.recentlyCompletedGoals[goal] = true
            self:SetWaypoint()
            if
              self.db.profile.goalcompletionflash
              or self.db.profile.goalupdateflash and self.frameNeedsResizing == 0
            then
              anim_w2g:Play()
              self:Debug('Completion: ' .. goal:GetText())
            end

            -- if a goal just completed, unpause.
            self.pause = nil
          end
          icon:SetIcon(3)
          icon:SetDesaturated(false)
          if anim_w2g:IsDone() or not anim_w2g:IsPlaying() then
            back:SetVertexColor(fromRGBA(self.db.profile.goalbackcomplete))
          end
        elseif status == 'impossible' then
          --impossible!
          icon:SetIcon(actionicon[goal.action])
          icon:SetDesaturated(true)
          back:SetVertexColor(fromRGBA(self.db.profile.goalbackimpossible))
        elseif status == 'obsolete' then
          --icon:SetIcon(actionicon[goal.action])
          --icon:SetDesaturated(false)
          back:SetVertexColor(fromRGBA(self.db.profile.goalbackobsolete))
        end

        icon:SetWidth(self.db.profile.fontsize * 1.4)
        icon:SetHeight(self.db.profile.fontsize * 1.4)
        if self.db.profile.goalbackgrounds then
          back:Show()
        else
          back:Hide()
        end
        if self.db.profile.goalicons then
          icon:Show()
          icon:SetAlpha(1.0)
        else
          icon:Hide()
        end

        if self.BUTTONS_INLINE then
          if action:IsShown() then
            icon:Hide()
          end
        end

        --clicker:Show()

        --height = height + line:GetHeight()
      else
        icon:Hide()
        back:Hide()
        if not InCombatLockdown() then
          action:Hide()
          petaction:Hide()
        end
        cooldown:Hide()
        --label:SetText("")
        --label:SetHeight(0)

        --line:SetHeight(0)  -- NO. This breaks stuff.
        -- but... it's necessary..!

        --line:SetHeight(0)
        --cooldown:Hide()
      end
    end

    if lastlabel then
      --ZygorGuidesViewerFrame_Divider2:SetPoint("TOPLEFT",lastlabel,"BOTTOMLEFT",-15,-4)
    end

    --ZygorGuidesViewerFrame_TextTitle:SetText(self.CurrentStep.title or "")
    --if ZygorGuidesViewerFrame_TextTitle:GetRight() then ZygorGuidesViewerFrame_TextTitle:SetWidth(ZygorGuidesViewerFrame_TextTitle:GetRight()-ZygorGuidesViewerFrame_TextTitle:GetLeft()) end

    --[[
		ZygorGuidesViewerFrame_TextInfo:SetText(self.CurrentStep.info or "")
		if ZygorGuidesViewerFrame_TextInfo:GetRight() then ZygorGuidesViewerFrame_TextInfo:SetWidth(ZygorGuidesViewerFrame_TextInfo:GetRight()-ZygorGuidesViewerFrame_TextInfo:GetLeft()) end
		--ZygorGuidesViewerFrame_TextInfo:SetPoint("TOPLEFT",self.CurrentStep.title and ZygorGuidesViewerFrame_TextTitle or ZygorGuidesViewerFrame_Divider2,"BOTTOMLEFT",0,-2)
		ZygorGuidesViewerFrame_TextInfo:SetPoint("TOPLEFT",ZygorGuidesViewerFrame_Divider2,"BOTTOMLEFT",0,-2)

		ZygorGuidesViewerFrame_TextInfo2:SetText(self.CurrentStep.info2 or "")
		if ZygorGuidesViewerFrame_TextInfo2:GetRight() then ZygorGuidesViewerFrame_TextInfo2:SetWidth(ZygorGuidesViewerFrame_TextInfo2:GetRight()-ZygorGuidesViewerFrame_TextInfo2:GetLeft()) end
		--ZygorGuidesViewerFrame_TextInfo2:SetPoint("TOPLEFT",self.CurrentStep.info and ZygorGuidesViewerFrame_TextInfo or (self.CurrentStep.title and ZygorGuidesViewerFrame_TextTitle or ZygorGuidesViewerFrame_Divider2),"BOTTOMLEFT",0,-2)
		ZygorGuidesViewerFrame_TextInfo2:SetPoint("TOPLEFT",self.CurrentStep.info and ZygorGuidesViewerFrame_TextInfo or ZygorGuidesViewerFrame_Divider2,"BOTTOMLEFT",0,-2)

		height = height + ZygorGuidesViewerFrame_TextInfo:GetHeight() + ZygorGuidesViewerFrame_TextInfo2:GetHeight()
		--]]

    -- aaand anchor it.

    --ZygorGuidesViewerFrame_ActiveStep:SetHeight(height)

    --ZygorGuidesViewerFrame_ActiveStep:ClearAllPoints()

    --local t = getglobal("ZygorGuidesViewerFrame_Step"..(self.CurrentStepNum))
    --ZygorGuidesViewerFrame_ActiveStep:SetPoint("TOPLEFT",t,"TOPLEFT")
    --ZygorGuidesViewerFrame_ActiveStep:SetPoint("BOTTOMRIGHT",t,"BOTTOMRIGHT")

    --[[
		if self.db.profile.showallsteps then
			if self.CurrentStepNum==1 then
				ZygorGuidesViewerFrame_ActiveStep:SetPoint("TOPLEFT",ZygorGuidesViewerFrameScrollChild,"TOPLEFT",0,-STEP_SPACING)
				ZygorGuidesViewerFrame_ActiveStep:SetPoint("TOPRIGHT",ZygorGuidesViewerFrameScrollChild,"TOPRIGHT",0,-STEP_SPACING)
			else
				local t = getglobal("ZygorGuidesViewerFrame_Step"..(self.CurrentStepNum-1))
				ZygorGuidesViewerFrame_ActiveStep:SetPoint("TOPLEFT",t,"BOTTOMLEFT",0,-STEP_SPACING)
				ZygorGuidesViewerFrame_ActiveStep:SetPoint("TOPRIGHT",t,"BOTTOMRIGHT",0,-STEP_SPACING)
			end
		else
			-- it's all alone
			ZygorGuidesViewerFrame_ActiveStep:SetPoint("TOPLEFT",ZygorGuidesViewerFrameScrollChild,"TOPLEFT",0,-STEP_SPACING)
			ZygorGuidesViewerFrame_ActiveStep:SetPoint("TOPRIGHT",ZygorGuidesViewerFrameScrollChild,"TOPRIGHT",0,-STEP_SPACING)
		end
		--]]
  end
end

function me:SetFrameScale(scale)
  scale = self.db.profile.framescale
  frame:SetScale(scale)
end

function me:ReanchorFrame()
  local frame = self.Frame
  local framemaster = frame:GetParent()
  local upsideup = not self.db.profile.resizeup

  frame:ClearAllPoints()
  if upsideup then
    --frame:SetPoint("TOP",nil,"TOP",(left+right)/2-(uiwidth/2/scale),top-uiheight/scale)
    --frame:SetPoint("TOP",frame:GetParent(),"BOTTOMLEFT",left+width/2,top)
    frame:SetPoint('TOPLEFT', framemaster, 'TOPLEFT', 0, 0)
    frame:SetClampRectInsets(0, 0, -25, 0)
  else
    --frame:SetPoint("BOTTOM",nil,"BOTTOM",(left+right)/2-(uiwidth/2/scale),bottom)
    --frame:SetPoint("BOTTOM",frame:GetParent(),"BOTTOMLEFT",left+width/2,bottom)
    frame:SetPoint('BOTTOMLEFT', framemaster, 'BOTTOMLEFT', 0, 0)
    frame:SetClampRectInsets(0, 0, 0, 25)
  end
end

function me:AlignFrame()
  --self:Debug("aligning frame")
  --print("align")
  local frame = self.Frame
  local framemaster = frame:GetParent()

  --[[
	if not frame.aligned then return end
	--if ZGV.stepframes[1].slideup:IsPlaying() then self.delayedalign=true return end

	local scale = frame:GetScale()

	local left,top,bottom,right = frame:GetLeft(),frame:GetTop(),frame:GetBottom(),frame:GetRight()
	--self:Debug(table.concat({math.floor(left),math.floor(right),math.floor(top),math.floor(bottom)},","))
	local width = frame:GetWidth()

	self:Debug(("%.2f scale: left %.2f, top %.2f, bottom %.2f, right %.2f"):format(scale,left,top,bottom,right))

	-- regain 100% scale
	left=left*scale  right=right*scale  bottom=bottom*scale  top=top*scale  width=width*scale

	self:Debug(("Scaled: left %.2f, top %.2f, bottom %.2f, right %.2f"):format(left,top,bottom,right))

	--]]
  local scale = self.db.profile.framescale

  local width = frame:GetWidth()
  local height = frame:GetHeight()

  -- enter local scale
  --left=left/scale  right=right/scale  bottom=bottom/scale  top=top/scale  width=width/scale

  --self:Debug(("Now %.2f scale: left %.2f, top %.2f, bottom %.2f, right %.2f"):format(scale,left,top,bottom,right))

  --self.temp_scansize=true

  --[[
	if not self.temp_aligncounter then self.temp_aligncounter=0 end
	self.temp_aligncounter=self.temp_aligncounter+1
	if self.temp_aligncounter==1 then a=1/nil end
	--]]

  frame:SetAlpha(self.db.profile.opacitymain)

  local upsideup = not self.db.profile.resizeup

  local UP_TOPLEFT = upsideup and 'TOPLEFT' or 'BOTTOMLEFT'
  local UP_BOTTOMLEFT = upsideup and 'BOTTOMLEFT' or 'TOPLEFT'
  local UP_BOTTOM = upsideup and 'BOTTOM' or 'TOP'
  local UP_TOPRIGHT = upsideup and 'TOPRIGHT' or 'BOTTOMRIGHT'
  local UP_BOTTOMRIGHT = upsideup and 'BOTTOMRIGHT' or 'TOPRIGHT'
  local UP = upsideup and 1 or -1

  local UPcoords = function(x1, x2, y1, y2)
    if upsideup then
      return x1, x2, y1, y2
    else
      return x1, x2, y2, y1
    end
  end

  local minimized = self.db.profile.hideborder and self.borderfadedout

  if upsideup then
    framemaster:SetClampRectInsets(0, (width - 40) * scale, -45 * scale, (-height + 55) * scale)
  else
    framemaster:SetClampRectInsets(0, (width - 40) * scale, -height * scale, 40 * scale)
  end

  ZygorGuidesViewerFrame_Border:SetBackdrop({
    --bgFile="Interface\\AddOns\\ZygorGuidesViewer\\Skin\\leavesofsteel_bgr",  -- 3.3.3 BLIZZARD TEXTURE FAIL
    bgFile = self.db.profile.bgfileback, --instead
    tileSize = 128,
    tile = self.db.profile.Border_Title,
    insets = {
      top = (upsideup and 20 or 0) + self.db.profile.skinbacktopoff,
      right = 0,
      left = 0,
      bottom = upsideup and 0 or 0,
    },
  })

  -- fix for evil background... wtf.
  ZygorGuidesViewerFrame_Border:SetBackdropColor(
    self.db.profile.skincolors.back[1],
    self.db.profile.skincolors.back[2],
    self.db.profile.skincolors.back[3],
    self.db.profile.backopacity
  )

  ZygorGuidesViewerFrame_Skipper:ClearAllPoints()
  --ZygorGuidesViewerFrame_Skipper:SetPoint(UP_TOPLEFT,self.Frame,-23,-27*UP)
  ZygorGuidesViewerFrame_Skipper:SetPoint(
    self.db.profile.Skipper_anchor,
    self.Frame,
    self.db.profile.Skipper_xOffset,
    self.db.profile.Skipper_yOffset * UP
  )

  ZygorGuidesViewerFrame_Border_SectionTitle:ClearAllPoints()
  ZygorGuidesViewerFrame_Border_SectionTitle:SetPoint(
    UP_TOPLEFT,
    ZygorGuidesViewerFrame_Border_Top,
    UP_TOPLEFT,
    30,
    -5 * UP + 1
  )
  ZygorGuidesViewerFrame_Border_SectionTitle:SetPoint(
    UP_BOTTOMRIGHT,
    ZygorGuidesViewerFrame_Border_Top,
    UP_BOTTOMRIGHT,
    -30,
    10 * UP + 1
  )

  ZygorGuidesViewerFrame_Border_TitleBar:ClearAllPoints()
  ZygorGuidesViewerFrame_Border_TitleBar:SetPoint(
    UP_TOPLEFT,
    ZygorGuidesViewerFrame_Border,
    UP_TOPLEFT,
    0,
    11 * UP
  )
  ZygorGuidesViewerFrame_Border_TitleBar:SetPoint(
    UP_BOTTOMRIGHT,
    ZygorGuidesViewerFrame_Border,
    UP_TOPRIGHT,
    0,
    -25 * UP
  )

  ZygorGuidesViewerFrame_Border_LockButton:SetPoint(
    'CENTER',
    ZygorGuidesViewerFrame_Border,
    UP_TOPLEFT,
    self.db.profile.Boarder_Lock_offsetX,
    self.db.profile.Boarder_Lock_offsetY * UP
  )
  ZygorGuidesViewerFrame_Border_MiniButton:SetPoint(
    'CENTER',
    ZygorGuidesViewerFrame_Border,
    UP_TOPRIGHT,
    self.db.profile.Boarder_Mini_topright_offsetX,
    self.db.profile.Boarder_Mini_topright_offsetY * UP
  )
  local anchor = UP_TOPLEFT -- Default for not invalid text
  local dbanchor = self.db.profile.Boarder_Settings_anchor
  -- TODO : Put this in a map
  if dbanchor == 'TOPRIGHT' then
    anchor = UP_TOPRIGHT
  end
  ZygorGuidesViewerFrame_Border_SettingsButton:SetPoint(
    'CENTER',
    ZygorGuidesViewerFrame_Border,
    anchor,
    self.db.profile.Boarder_Settings_anchor_offsetX,
    self.db.profile.Boarder_Settings_anchor_offsetY * UP
  )
  ZygorGuidesViewerFrame_Border_CloseButton:SetPoint(
    'CENTER',
    ZygorGuidesViewerFrame_Border,
    UP_TOPRIGHT,
    self.db.profile.Boarder_Close_topright_offsetX,
    self.db.profile.Boarder_Close_topright_offsetY * UP
  )

  --ntx:SetTexCoord(731/1024,850/1024,76/512,145/512)
  --ptx:SetTexCoord(731/1024,850/1024,211/512,280/512)
  --htx:SetTexCoord(731/1024,850/1024,346/512,415/512)
  ZygorGuidesViewerFrame_Border_GuideButton.upsideup = upsideup
  ZygorGuidesViewerFrame_Border_GuideButton:ClearAllPoints()
  ZygorGuidesViewerFrame_Border_GuideButton:SetPoint(
    UP_BOTTOM,
    ZygorGuidesViewerFrame_Border,
    UP_TOPRIGHT,
    self.db.profile.Boarder_Guide_topright_offsetX,
    self.db.profile.Boarder_Guide_topright_offsetY * UP
  )

  if minimized then
    ZygorGuidesViewerFrame_Skipper:Hide()
    ZygorGuidesViewerFrame_Border:Hide()
  else
    if self.db.profile.displaymode == 'guide' then
      ZygorGuidesViewerFrame_Skipper:Show()
    else
      ZygorGuidesViewerFrame_Skipper:Hide()
    end
    ZygorGuidesViewerFrame_Border:Show()
  end

  --ZygorGuidesViewerFrame_TitleBar_SectionTitle:SetPoint(TOPLEFT,60,-4*UP)
  --ZygorGuidesViewerFrame_TitleBar_SectionTitle:SetPoint(BOTTOMRIGHT,-60,0)

  -- first line according to up/down orientation, the rest follows
  ZygorGuidesViewerFrameScroll:ClearAllPoints()
  ZygorGuidesViewerFrameScroll:SetPoint(UP_TOPLEFT, self.Frame, UP_TOPLEFT, 10, -28 * UP)
  ZygorGuidesViewerFrameScroll:SetPoint(UP_BOTTOMRIGHT, self.Frame, -10, 10 * UP)

  -- resizers
  ZygorGuidesViewerFrame_ResizerBottom:ClearAllPoints()
  ZygorGuidesViewerFrame_ResizerBottom:SetPoint(UP_BOTTOMLEFT, 10, 0)
  ZygorGuidesViewerFrame_ResizerBottom:SetPoint(
    UP_TOPRIGHT,
    self.Frame,
    UP_BOTTOMRIGHT,
    -10,
    10 * UP
  )
  ZygorGuidesViewerFrame_ResizerBottomLeft:ClearAllPoints()
  ZygorGuidesViewerFrame_ResizerBottomLeft:SetPoint(UP_BOTTOMLEFT, 0, 0)
  ZygorGuidesViewerFrame_ResizerBottomRight:ClearAllPoints()
  ZygorGuidesViewerFrame_ResizerBottomRight:SetPoint(UP_BOTTOMRIGHT, 0, 0)

  --local back=ZygorGuidesViewerFrame_Border:GetRegions()

  -- textures
  ZygorGuidesViewerFrame_Border_TopLeft:SetWidth(100)
  ZygorGuidesViewerFrame_Border_TopLeft:SetHeight(100 * 1.225)
  ZygorGuidesViewerFrame_Border_TopLeft:ClearAllPoints()
  ZygorGuidesViewerFrame_Border_TopLeft:SetPoint(UP_TOPLEFT, -35, 16 * UP)
  ZygorGuidesViewerFrame_Border_TopLeft:SetTexCoord(
    UPcoords(0.095703125, 0.2900390625, 0.12109375, 0.59765625)
  )

  ZygorGuidesViewerFrame_Border_Gear1:ClearAllPoints()
  ZygorGuidesViewerFrame_Border_Gear1:SetPoint(
    'CENTER',
    ZygorGuidesViewerFrame_Skipper,
    UP_TOPLEFT,
    10,
    -32 * UP
  )
  ZygorGuidesViewerFrame_Border_Gear2:ClearAllPoints()
  ZygorGuidesViewerFrame_Border_Gear2:SetPoint(
    'CENTER',
    ZygorGuidesViewerFrame_Skipper,
    UP_TOPLEFT,
    4,
    -15 * UP
  )
  ZygorGuidesViewerFrame_Border_Gear3:ClearAllPoints()
  ZygorGuidesViewerFrame_Border_Gear3:SetPoint(
    'CENTER',
    ZygorGuidesViewerFrame_Skipper,
    UP_TOPLEFT,
    20,
    -56 * UP
  )

  ZygorGuidesViewerFrame_Border_TopRight:SetWidth(100)
  ZygorGuidesViewerFrame_Border_TopRight:SetHeight(100 * 1.225)
  ZygorGuidesViewerFrame_Border_TopRight:ClearAllPoints()
  ZygorGuidesViewerFrame_Border_TopRight:SetPoint(UP_TOPRIGHT, 35, 16 * UP)
  ZygorGuidesViewerFrame_Border_TopRight:SetTexCoord(
    UPcoords(0.515625, 0.7099609375, 0.12109375, 0.59765625)
  )

  ZygorGuidesViewerFrame_Border_BottomLeft:SetWidth(22)
  ZygorGuidesViewerFrame_Border_BottomLeft:SetHeight(22)
  ZygorGuidesViewerFrame_Border_BottomLeft:ClearAllPoints()
  ZygorGuidesViewerFrame_Border_BottomLeft:SetPoint(UP_BOTTOMLEFT, -3, -4 * UP)
  ZygorGuidesViewerFrame_Border_BottomLeft:SetTexCoord(
    UPcoords(161 / 1024, 204 / 1024, 385 / 512, 428 / 512)
  )

  ZygorGuidesViewerFrame_Border_BottomRight:SetWidth(22)
  ZygorGuidesViewerFrame_Border_BottomRight:SetHeight(22)
  ZygorGuidesViewerFrame_Border_BottomRight:ClearAllPoints()
  ZygorGuidesViewerFrame_Border_BottomRight:SetPoint(UP_BOTTOMRIGHT, 3, -4 * UP)
  ZygorGuidesViewerFrame_Border_BottomRight:SetTexCoord(
    UPcoords(204 / 1024, 161 / 1024, 385 / 512, 428 / 512)
  )

  ZygorGuidesViewerFrame_Border_Top:ClearAllPoints()
  ZygorGuidesViewerFrame_Border_Top:SetHeight(35)
  ZygorGuidesViewerFrame_Border_Top:SetPoint(
    UP_TOPLEFT,
    self.db.profile.Boarder_topleft_offsetX,
    self.db.profile.Boarder_topleft_offsetY * UP
  )
  ZygorGuidesViewerFrame_Border_Top:SetPoint(
    UP_TOPRIGHT,
    self.db.profile.Boarder_topright_offsetX,
    self.db.profile.Boarder_topright_offsetX * UP
  )
  local tx = ZygorGuidesViewerFrame_Border_Top:GetTexture()
  ZygorGuidesViewerFrame_Border_Top:SetTexture(1)
  ZygorGuidesViewerFrame_Border_Top:SetTexture(tx, true)
  ZygorGuidesViewerFrame_Border_Top:SetTexCoord(UPcoords(0, 1, 0, 1))

  -- Position Left Border using values from Z_SKINS
  ZygorGuidesViewerFrame_Border_Left:ClearAllPoints()
  ZygorGuidesViewerFrame_Border_Left:SetPoint(
    UP_TOPLEFT,
    self.db.profile.Boarder_Left_TopLeft_X,
    self.db.profile.Boarder_Left_TopLeft_Y * UP
  )
  ZygorGuidesViewerFrame_Border_Left:SetPoint(
    UP_BOTTOMRIGHT,
    self.Frame,
    UP_BOTTOMLEFT,
    self.db.profile.Boarder_Left_BottomRight_X,
    self.db.profile.Boarder_Left_BottomRight_Y * UP
  )
  tx = ZygorGuidesViewerFrame_Border_Left:GetTexture()
  ZygorGuidesViewerFrame_Border_Left:SetTexture(1)
  ZygorGuidesViewerFrame_Border_Left:SetTexture(tx, true)

  -- Position Right Border using values from Z_SKINS
  ZygorGuidesViewerFrame_Border_Right:ClearAllPoints()
  ZygorGuidesViewerFrame_Border_Right:SetPoint(
    UP_TOPRIGHT,
    self.db.profile.Boarder_Right_TopRight_X,
    self.db.profile.Boarder_Right_TopRight_Y * UP
  )
  ZygorGuidesViewerFrame_Border_Right:SetPoint(
    UP_BOTTOMLEFT,
    self.Frame,
    UP_BOTTOMRIGHT,
    self.db.profile.Boarder_Right_BottomLeft_X,
    self.db.profile.Boarder_Right_BottomLeft_Y * UP
  )
  ZygorGuidesViewerFrame_Border_Right:SetTexture(1)
  ZygorGuidesViewerFrame_Border_Right:SetTexture(tx, true)

  ZygorGuidesViewerFrame_Border_Bottom:ClearAllPoints()
  ZygorGuidesViewerFrame_Border_Bottom:SetPoint(UP_TOPLEFT, self.Frame, UP_BOTTOMLEFT, 13, 10 * UP)
  ZygorGuidesViewerFrame_Border_Bottom:SetPoint(UP_BOTTOMRIGHT, -13, -5 * UP)
  ZygorGuidesViewerFrame_Border_Bottom:SetTexture(1)
  ZygorGuidesViewerFrame_Border_Bottom:SetTexture(tx, true)

  ZygorGuidesViewerFrame_Border_Logo:ClearAllPoints()
  ZygorGuidesViewerFrame_Border_Logo:SetPoint(
    'CENTER',
    ZygorGuidesViewerFrame_Border_Bottom,
    'CENTER',
    0,
    0
  )

  -- flash stuff... this is a royal PITA.
  ZygorGuidesViewerFrame_Border_Flash_Top:ClearAllPoints()
  ZygorGuidesViewerFrame_Border_Flash_Top:SetHeight(80)
  ZygorGuidesViewerFrame_Border_Flash_Top:SetPoint(
    UP_BOTTOMLEFT,
    ZygorGuidesViewerFrame_Border_Top,
    UP_BOTTOMLEFT,
    10,
    -8 * UP
  )
  ZygorGuidesViewerFrame_Border_Flash_Top:SetPoint(
    UP_BOTTOMRIGHT,
    ZygorGuidesViewerFrame_Border_Top,
    UP_BOTTOMRIGHT,
    0,
    -8 * UP
  )
  local tx = ZygorGuidesViewerFrame_Border_Flash_Top:GetTexture()
  ZygorGuidesViewerFrame_Border_Flash_Top:SetTexture(1)
  ZygorGuidesViewerFrame_Border_Flash_Top:SetTexture(tx, true)
  ZygorGuidesViewerFrame_Border_Flash_Top:SetTexCoord(UPcoords(0, 1, 0, 1))

  ZygorGuidesViewerFrame_Border_Flash_TopLeft:SetWidth(125)
  ZygorGuidesViewerFrame_Border_Flash_TopLeft:SetHeight(139)
  ZygorGuidesViewerFrame_Border_Flash_TopLeft:ClearAllPoints()
  ZygorGuidesViewerFrame_Border_Flash_TopLeft:SetPoint(
    UP_BOTTOMRIGHT,
    ZygorGuidesViewerFrame_Border_TopLeft,
    UP_BOTTOMRIGHT,
    7,
    3 * UP
  )
  ZygorGuidesViewerFrame_Border_Flash_TopLeft:SetTexCoord(
    UPcoords(62 / 1024, 311 / 1024, 23 / 512, 300 / 512)
  )

  ZygorGuidesViewerFrame_Border_Flash_TopRight:SetWidth(130)
  ZygorGuidesViewerFrame_Border_Flash_TopRight:SetHeight(90)
  ZygorGuidesViewerFrame_Border_Flash_TopRight:ClearAllPoints()
  ZygorGuidesViewerFrame_Border_Flash_TopRight:SetPoint(
    UP_BOTTOMLEFT,
    ZygorGuidesViewerFrame_Border_TopRight,
    UP_BOTTOMLEFT,
    -13,
    51 * UP
  )
  ZygorGuidesViewerFrame_Border_Flash_TopRight:SetTexCoord(
    UPcoords(505 / 1024, 760 / 1024, 28 / 512, 200 / 512)
  )

  ZygorGuidesViewerFrame_Border_Flash_BottomLeft:SetWidth(64)
  ZygorGuidesViewerFrame_Border_Flash_BottomLeft:SetHeight(64)
  ZygorGuidesViewerFrame_Border_Flash_BottomLeft:ClearAllPoints()
  ZygorGuidesViewerFrame_Border_Flash_BottomLeft:SetPoint(
    UP_TOPRIGHT,
    ZygorGuidesViewerFrame_Border_BottomLeft,
    UP_TOPRIGHT,
    20,
    20 * UP
  )
  ZygorGuidesViewerFrame_Border_Flash_BottomLeft:SetTexCoord(
    UPcoords(121 / 1024, 244 / 1024, 345 / 512, 468 / 512)
  )

  ZygorGuidesViewerFrame_Border_Flash_BottomRight:SetWidth(64)
  ZygorGuidesViewerFrame_Border_Flash_BottomRight:SetHeight(64)
  ZygorGuidesViewerFrame_Border_Flash_BottomRight:ClearAllPoints()
  ZygorGuidesViewerFrame_Border_Flash_BottomRight:SetPoint(
    UP_TOPLEFT,
    ZygorGuidesViewerFrame_Border_BottomRight,
    UP_TOPLEFT,
    -20,
    20 * UP
  )
  ZygorGuidesViewerFrame_Border_Flash_BottomRight:SetTexCoord(
    UPcoords(244 / 1024, 121 / 1024, 345 / 512, 468 / 512)
  )

  ZygorGuidesViewerFrame_Border_Flash_Left:ClearAllPoints()
  ZygorGuidesViewerFrame_Border_Flash_Left:SetPoint(UP_TOPLEFT, -17, -85 * UP)
  ZygorGuidesViewerFrame_Border_Flash_Left:SetPoint(
    UP_BOTTOMRIGHT,
    self.Frame,
    UP_BOTTOMLEFT,
    9,
    10 * UP
  )

  ZygorGuidesViewerFrame_Border_Flash_Right:ClearAllPoints()
  ZygorGuidesViewerFrame_Border_Flash_Right:SetPoint(
    UP_TOPLEFT,
    self.Frame,
    UP_TOPRIGHT,
    -10,
    -35 * UP
  )
  ZygorGuidesViewerFrame_Border_Flash_Right:SetPoint(
    UP_BOTTOMRIGHT,
    self.Frame,
    UP_BOTTOMRIGHT,
    16,
    10 * UP
  )
  ZygorGuidesViewerFrame_Border_Flash_Right:SetTexCoord(1, 0, 1, 1, 0, 0, 0, 1)

  ZygorGuidesViewerFrame_Border_Flash_Bottom:ClearAllPoints()
  ZygorGuidesViewerFrame_Border_Flash_Bottom:SetPoint(
    UP_TOPLEFT,
    self.Frame,
    UP_BOTTOMLEFT,
    13,
    9 * UP
  )
  ZygorGuidesViewerFrame_Border_Flash_Bottom:SetPoint(
    UP_BOTTOMRIGHT,
    self.Frame,
    UP_BOTTOMRIGHT,
    -13,
    -15 * UP
  )
  --	ZygorGuidesViewerFrame_Border_Flash_Bottom:SetTexCoord(UPcoords(1,0,0,0,1,1,0,1))
  if upsideup then
    ZygorGuidesViewerFrame_Border_Flash_Bottom:SetTexCoord(1, 0, 0, 0, 1, 1, 0, 1)
  else
    ZygorGuidesViewerFrame_Border_Flash_Bottom:SetTexCoord(0, 0, 1, 0, 0, 1, 1, 1)
  end

  ZygorGuidesViewerFrame_Border_Flash_Logo:ClearAllPoints()
  ZygorGuidesViewerFrame_Border_Flash_Logo:SetPoint(
    'CENTER',
    ZygorGuidesViewerFrame_Border_Logo,
    'CENTER'
  )
end

function me:UpdateSkin()
  SKINDIR = DIR .. '\\Skin\\' .. self.db.profile.skin

  ZygorGuidesViewerFrame_Border_GuideButton.ntx:SetTexture(SKINDIR .. '\\leavesofsteel_dropdown_up')
  ZygorGuidesViewerFrame_Border_GuideButton.ptx:SetTexture(
    SKINDIR .. '\\leavesofsteel_dropdown_down'
  )
  ZygorGuidesViewerFrame_Border_GuideButton.htx:SetTexture(SKINDIR .. '\\leavesofsteel_dropdown_hi')

  ZygorGuidesViewerFrame_Border_TopLeft:SetTexture(SKINDIR .. '\\leavesofsteel')
  ZygorGuidesViewerFrame_Border_TopRight:SetTexture(SKINDIR .. '\\leavesofsteel')
  ZygorGuidesViewerFrame_Border_BottomLeft:SetTexture(SKINDIR .. '\\leavesofsteel')
  ZygorGuidesViewerFrame_Border_BottomRight:SetTexture(SKINDIR .. '\\leavesofsteel')

  ZygorGuidesViewerFrame_Skipper_PrevButton.ntx:SetTexture(SKINDIR .. '\\titlebuttons')
  ZygorGuidesViewerFrame_Skipper_PrevButton.ptx:SetTexture(SKINDIR .. '\\titlebuttons')
  ZygorGuidesViewerFrame_Skipper_PrevButton.htx:SetTexture(SKINDIR .. '\\titlebuttons')
  ZygorGuidesViewerFrame_Skipper_NextButton.ntx:SetTexture(SKINDIR .. '\\titlebuttons')
  ZygorGuidesViewerFrame_Skipper_NextButton.ptx:SetTexture(SKINDIR .. '\\titlebuttons')
  ZygorGuidesViewerFrame_Skipper_NextButton.htx:SetTexture(SKINDIR .. '\\titlebuttons')

  ZygorGuidesViewerFrame_Border_CloseButton.ntx:SetTexture(SKINDIR .. '\\titlebuttons')
  ZygorGuidesViewerFrame_Border_CloseButton.ptx:SetTexture(SKINDIR .. '\\titlebuttons')
  ZygorGuidesViewerFrame_Border_CloseButton.htx:SetTexture(SKINDIR .. '\\titlebuttons')

  ZygorGuidesViewerFrame_Border_MiniButton.ntx:SetTexture(SKINDIR .. '\\titlebuttons')
  ZygorGuidesViewerFrame_Border_MiniButton.ptx:SetTexture(SKINDIR .. '\\titlebuttons')
  ZygorGuidesViewerFrame_Border_MiniButton.htx:SetTexture(SKINDIR .. '\\titlebuttons')

  ZygorGuidesViewerFrame_Border_LockButton.ntx:SetTexture(SKINDIR .. '\\titlebuttons')
  ZygorGuidesViewerFrame_Border_LockButton.ptx:SetTexture(SKINDIR .. '\\titlebuttons')
  ZygorGuidesViewerFrame_Border_LockButton.htx:SetTexture(SKINDIR .. '\\titlebuttons')

  ZygorGuidesViewerFrame_Border_SettingsButton.ntx:SetTexture(SKINDIR .. '\\titlebuttons')
  ZygorGuidesViewerFrame_Border_SettingsButton.ptx:SetTexture(SKINDIR .. '\\titlebuttons')
  ZygorGuidesViewerFrame_Border_SettingsButton.htx:SetTexture(SKINDIR .. '\\titlebuttons')

  ZygorGuidesViewerMapIcon.ntx:SetTexture(SKINDIR .. '\\zglogo')
  ZygorGuidesViewerMapIcon.ptx:SetTexture(SKINDIR .. '\\zglogo')
  ZygorGuidesViewerMapIcon.htx:SetTexture(SKINDIR .. '\\zglogo')

  ZygorGuidesViewerFrame_Border_Top:SetTexture(SKINDIR .. '\\leavesofsteel_top')

  ZygorGuidesViewerFrame_Border_SectionTitle:SetTextColor(unpack(self.db.profile.skincolors.text))

  ZygorGuidesViewerFrameScrollScrollBarScrollUpButton:SetNormalTexture(
    CreateTextureWithCoords(
      ZygorGuidesViewerFrameScrollScrollBarScrollUpButton,
      SKINDIR .. '\\titlebuttons',
      0.750,
      0.875,
      0.00,
      0.25
    )
  )
  ZygorGuidesViewerFrameScrollScrollBarScrollUpButton:SetPushedTexture(
    CreateTextureWithCoords(
      ZygorGuidesViewerFrameScrollScrollBarScrollUpButton,
      SKINDIR .. '\\titlebuttons',
      0.750,
      0.875,
      0.25,
      0.50
    )
  )
  ZygorGuidesViewerFrameScrollScrollBarScrollUpButton:SetDisabledTexture(
    CreateTextureWithCoords(
      ZygorGuidesViewerFrameScrollScrollBarScrollUpButton,
      SKINDIR .. '\\titlebuttons',
      0.750,
      0.875,
      0.75,
      1.00
    )
  )
  ZygorGuidesViewerFrameScrollScrollBarScrollUpButton:SetHighlightTexture(
    CreateTextureWithCoords(
      ZygorGuidesViewerFrameScrollScrollBarScrollUpButton,
      SKINDIR .. '\\titlebuttons',
      0.750,
      0.875,
      0.50,
      0.75
    )
  )
  ZygorGuidesViewerFrameScrollScrollBarScrollDownButton:SetNormalTexture(
    CreateTextureWithCoords(
      ZygorGuidesViewerFrameScrollScrollBarScrollDownButton,
      SKINDIR .. '\\titlebuttons',
      0.875,
      1.000,
      0.00,
      0.25
    )
  )
  ZygorGuidesViewerFrameScrollScrollBarScrollDownButton:SetPushedTexture(
    CreateTextureWithCoords(
      ZygorGuidesViewerFrameScrollScrollBarScrollDownButton,
      SKINDIR .. '\\titlebuttons',
      0.875,
      1.000,
      0.25,
      0.50
    )
  )
  ZygorGuidesViewerFrameScrollScrollBarScrollDownButton:SetDisabledTexture(
    CreateTextureWithCoords(
      ZygorGuidesViewerFrameScrollScrollBarScrollDownButton,
      SKINDIR .. '\\titlebuttons',
      0.875,
      1.000,
      0.75,
      1.00
    )
  )
  ZygorGuidesViewerFrameScrollScrollBarScrollDownButton:SetHighlightTexture(
    CreateTextureWithCoords(
      ZygorGuidesViewerFrameScrollScrollBarScrollDownButton,
      SKINDIR .. '\\titlebuttons',
      0.875,
      1.000,
      0.50,
      0.75
    )
  )
  ZygorGuidesViewerFrameScrollScrollBarThumbTexture:SetTexture(SKINDIR .. '\\leavesofsteel')
  ZygorGuidesViewerFrameScrollScrollBarTrackerTexture:SetTexture(SKINDIR .. '\\leavesofsteel')

  local choice = self.skin_choice[self.db.profile.skin]
  if choice then
    LoadSkin(choice)
  else
    --LoadDefault()
    self:Print('Load skin Error!')
  end
  self:UpdateLocking()
  self:AlignFrame()
end

function me:ResizeFrame()
  --autosize
  --if (self.db.profile.autosize) then
  --print("resize")
  if self.frameNeedsResizing and self.frameNeedsResizing > 0 then
    self.frameNeedsResizing = self.frameNeedsResizing - 1
  end
  if self.frameNeedsResizing > 0 then
    return nil
  end
  if not self.db then
    return
  end

  if ZygorGuidesViewerFrame_Border_Bottom:GetRect() then
    local xsize = select(3, ZygorGuidesViewerFrame_Border_Bottom:GetRect()) / 200
    local ysize = select(4, ZygorGuidesViewerFrame_Border_Left:GetRect()) / 100
    local ysize2 = select(4, ZygorGuidesViewerFrame_Border_Right:GetRect()) / 100
    ZygorGuidesViewerFrame_Border_Left:SetTexCoord(0.2, 0.8, 0, 1 * ysize)
    ZygorGuidesViewerFrame_Border_Right:SetTexCoord(0.2, 0.8, 0, 1 * ysize2)
    ZygorGuidesViewerFrame_Border_Bottom:SetTexCoord(0, -xsize, 1, -xsize, 0, xsize, 1, xsize)
  end

  ZygorGuidesViewerFrame_Border:SetBackdropColor(
    self.db.profile.skincolors.back[1],
    self.db.profile.skincolors.back[2],
    self.db.profile.skincolors.back[3],
    self.db.profile.backopacity
  )

  --self:Debug("resizing from "..tostring(ZygorGuidesViewerFrame:GetHeight()))

  if self.db.profile.showallsteps or self.db.profile.displaymode == 'gold' then
    ZygorGuidesViewerFrameScrollScrollBar:Show()
  else
    -- only autoresize when showing ONE step. If we have many steps, the user handles resizing.
    ZygorGuidesViewerFrameScrollScrollBar:Hide()
    --if not self.CurrentStepNum or not _G['ZygorGuidesViewerFrame_Step'..self.CurrentStepNum] then return end
    local height = 0
    for i = 1, self.db.profile.showcountsteps do
      if i > 1 then
        height = height + STEP_SPACING
      end
      height = height + self.stepframes[i]:GetHeight()
    end

    height = height + 40
    --self:Debug("Height "..height.."  min "..MIN_HEIGHT)
    if height < MIN_HEIGHT then
      height = MIN_HEIGHT
    end
    self.Frame:SetHeight(height)
  end

  --self:Debug(("%d %d"):format(left,bottom))
  --		ZygorGuidesViewerFrame:SetHeight(ZygorGuidesViewerFrame_Text:GetHeight()+35)

  --	if ZygorGuidesViewerFrame_ActiveStep_Line1:GetTop() then
  --ZygorGuidesViewerFrame_Resize.max = ZygorGuidesViewerFrame_Line1:GetTop()-ZygorGuidesViewerFrame_TextInfo2:GetBottom()+35
  --ZygorGuidesViewerFrame_Resize:Stop()
  --ZygorGuidesViewerFrame_Resize:Play()

  --		ZygorGuidesViewerFrame:SetHeight(ZygorGuidesViewerFrame_ActiveStep_Line1:GetTop()-ZygorGuidesViewerFrame_TextInfo2:GetBottom()+35)
  --	end

  --	end
end

function me:GoalProgress(goal)
  return 'epic fail'
end

function me:ScrollToCurrentStep()
  --	if self.ForceScrollToCurrentStep and self.CurrentStep then
  --		self.ForceScrollToCurrentStep = false
  if self.CurrentStep and self.db.profile.displaymode == 'guide' then
    local height = 0
    local step
    if self.db.profile.showallsteps then
      local topstep = self.stepframes[1].stepnum
      if not topstep then
        return
      end
      if
        self.stepframes[1].stepnum > self.CurrentStepNum --above
        or (topstep + self.StepLimit - 1 < self.CurrentStepNum) --way below
        --				or (ZygorGuidesViewerFrame_Step1:GetTop()-_G['ZygorGuidesViewerFrame_Step'..(self.CurrentStepNum-topstep+1)]:GetBottom()+STEP_SPACING>ZygorGuidesViewerFrameScroll:GetHeight()) --barely offscreen
        or not self.stepframes[self.CurrentStepNum - topstep + 1]:IsShown()
        or self.stepframes[self.CurrentStepNum - topstep + 1].truncated
      then
        ZygorGuidesViewerFrameScrollScrollBar:SetValue(self.CurrentStepNum)
        ZygorGuidesViewerFrameScrollScrollBar:Show()
      end
    else
      ZygorGuidesViewerFrameScrollScrollBar:Hide()
    end
  end
  --	else
  --		self.ForceScrollToCurrentStep = true
  --	end
end

function me:IsVisible()
  return self.Frame:IsVisible()
end

function me:SetVisible(info, onoff)
  if not onoff and self:IsVisible() then
    self:ToggleFrame()
  end
  if onoff and not self:IsVisible() then
    self:ToggleFrame()
  end
end

function me:ToggleFrame()
  if self:IsVisible() then
    self.Frame:Hide()
  else
    self.Frame:Show()
  end
end

function me:IsDefaultFitting(default)
  -- deprecated?
  local _, race = UnitRace('player')
  local _, class = UnitClass('player')
  if class == 'DEATHKNIGHT' then
    race = class
  end
  default = default:upper()
  race = race:upper()
  class = class:upper()
  return race == default or class == default or race .. ' ' .. class == default
end

--- Checks if the player's race/class matches the requirements.
-- @param requirement May be a string or a table of strings (which are then ORed).
-- @return true if matching, false if not.
function me:RaceClassMatch(fit, dkfix)
  if type(fit) == 'table' then
    for i, v in ipairs(fit) do
      if self:RaceClassMatch(v) then
        return true
      end
    end
    return false --otherwise
  end

  local _, race = UnitRace('player')
  local _, class = UnitClass('player')
  race = race:upper()
  class = class:upper()
  if dkfix and class == 'DEATHKNIGHT' then
    race = 'BLAH'
  end
  fit = fit:upper()
  local neg = false
  if fit:sub(1, 1) == '!' then
    neg = true
    fit = fit:sub(2)
  end
  local ret = (race == fit or class == fit or race .. ' ' .. class == fit)
  if neg then
    return not ret
  else
    return ret
  end
end

function me:RaceClassMatchList(list)
  list = list .. ','
  local st, en = 1
  for fit in list:gmatch('(.-),') do
    if self:RaceClassMatch(fit) then
      return true
    end
  end
end

function me:SkipStep(delta, fast)
  if not self.CurrentGuide then
    return
  end

  local skipped = 0
  local atstart = false

  if self.completioninterval > self.completionintervallong then
    self.completioninterval = self.completionintervallong
  end

  self.completionelapsed = 0
  repeat
    self:Debug('SkipStep ' .. delta .. ' ' .. (fast and 'fast' or ''))
    local i = self.CurrentStepNum + delta
    if i < 1 then
      --if self.CurrentGuideName==1 then return end		-- first section? bail.
      if self.CurrentGuide.defaultfor then
        atstart = true
        break
      end -- no skipping back from a starter section.

      --local default = self:FindDefaultGuide()

      if self.CurrentGuide['prev'] then
        self:SetGuide(self.CurrentGuide['prev'])
      else
        local founddef = false
        for i, v in ipairs(self.registeredguides) do
          if
            v.next == self.CurrentGuideName
            and (not v.defaultfor or self:RaceClassMatch(v.defaultfor))
          then
            self:SetGuide(i)
            founddef = true
            break
          end
        end
        if not founddef then
          atstart = true
          break
        end
      end

      --[[
			if self.CurrentGuide.defaultfor and self.CurrentGuide.defaultfor ~= race then		-- wrong default section? move to ours.
				self:SetGuide(default)
			end
			--]]
      i = #self.CurrentGuide['steps']
    end
    if i > #self.CurrentGuide['steps'] or (delta > 0 and self.CurrentStep.finish) then
      if self.CurrentGuide['next'] then
        self:SetGuide(self.CurrentGuide['next'])
        i = 1
      else
        -- no next? capping
        if self.CurrentStep.finish then
          -- capped
          self.pause = true
          return
        else
          -- cap it
          self.CurrentStep = {
            num = self.CurrentStepNum + 1,
            parentGuide = self.CurrentStep.parentGuide,
            finish = true,
          }
          self.CurrentStep.goals = {
            [1] = {
              num = 1,
              action = '',
              text = 'This guide is now complete.',
              parentStep = self.CurrentStep,
            },
          }
          setmetatable(self.CurrentStep, ZGV.StepProto_mt)
          setmetatable(self.CurrentStep.goals[1], ZGV.GoalProto_mt)
          tinsert(self.CurrentGuide.steps, self.CurrentStep)
        end
      end
    end

    self.pause = not fast
    self.fastforward = fast

    self.LastSkip = delta
    --self:Debug("LastSkip "..self.LastSkip)

    self:FocusStepQuiet(i) --quiet!
    skipped = skipped + 1
    if skipped > 10000 then
      error('Looping on skipping! guide ' .. self.CurrentGuideName .. ' step ' .. i)
    end
  until self.CurrentStep:AreRequirementsMet()

  if atstart then
    self.pause = true
    self.fastforward = false
  end

  self:FocusStep(self.CurrentStepNum)
end

function me:Print(s)
  ChatFrame1:AddMessage(L['name'] .. ': ' .. tostring(s))
end

function me:AnimateGears()
  if ZygorGuidesViewerFrame_Border:IsVisible() then
    ZygorGuidesViewerFrame_Border_Gear1_turn2:Stop()
    --ZygorGuidesViewerFrame_Border_Gear1_turn2:GetAnimations():SetSmoothing(ZygorGuidesViewerFrame_Border_Gear1_turn2:IsPlaying() and "OUT" or "IN_OUT")
    ZygorGuidesViewerFrame_Border_Gear1.tangle = self.CurrentStepNum * -11
    ZygorGuidesViewerFrame_Border_Gear1_turn2:Play()

    ZygorGuidesViewerFrame_Border_Gear2_turn2:Stop()
    --ZygorGuidesViewerFrame_Border_Gear2_turn2:GetAnimations():SetSmoothing(ZygorGuidesViewerFrame_Border_Gear2_turn2:IsPlaying() and "OUT" or "IN_OUT")
    ZygorGuidesViewerFrame_Border_Gear2.tangle = self.CurrentStepNum * 65
    ZygorGuidesViewerFrame_Border_Gear2_turn2:Play()

    ZygorGuidesViewerFrame_Border_Gear3_turn2:Stop()
    --ZygorGuidesViewerFrame_Border_Gear3_turn2:GetAnimations():SetSmoothing(ZygorGuidesViewerFrame_Border_Gear3_turn2:IsPlaying() and "OUT" or "IN_OUT")
    ZygorGuidesViewerFrame_Border_Gear3.tangle = self.CurrentStepNum * 85
    ZygorGuidesViewerFrame_Border_Gear3_turn2:Play()
  end
end

local function dumpquest(quest)
  local s = ('%d. "%s" ##%d (lv=%d%s):\n'):format(
    quest.index,
    quest.title,
    quest.id,
    quest.level,
    quest.complete and ', complete' or ''
  )
  for i, goal in ipairs(quest.goals) do
    s = s
      .. ('... %d. "%s" (%s, %s/%s%s)\n'):format(
        i,
        goal.leaderboard,
        goal.type,
        goal.num,
        goal.needed,
        goal.complete and ', complete' or ''
      )
  end
  return s
end

function me:UNIT_INVENTORY_CHANGED(event, unit)
  if unit == 'player' then
    self:TryToCompleteStep(true)
  end
end

local blobstate = nil
function me:PLAYER_REGEN_DISABLED()
  --ZygorGuidesViewerFrame_Cover:Show()
  --ZygorGuidesViewerFrame_Cover:EnableMouse(true)
  self:UpdateCooldowns()
  if self.db.profile.hideincombat then
    if self.Frame:IsVisible() then
      UIFrameFadeOut(self.Frame, 0.5, 1.0, 0.0)
      self.hiddenincombat = true
    end
  end

  blobstate = WorldMapBlobFrame:IsShown()
  WorldMapBlobFrame:SetParent(nil)
  --WorldMapBlobFrame:ClearAllPoints()
  WorldMapBlobFrame:Hide()
  WorldMapBlobFrame.Hide = function()
    blobstate = nil
  end
  WorldMapBlobFrame.Show = function()
    blobstate = true
  end
end

function me:PLAYER_REGEN_ENABLED()
  --ZygorGuidesViewerFrame_Cover:Hide()
  --ZygorGuidesViewerFrame_Cover:EnableMouse(false)
  if self.CurrentStep then
    self.CurrentStep:PrepareCompletion()
  end
  self:UpdateFrameCurrent()
  self:UpdateCooldowns()
  if self.hiddenincombat then
    UIFrameFadeIn(self.Frame, 0.5, 0.0, 1.0)
  end
  self.hiddenincombat = nil

  self:UpdateLocking()

  WorldMapBlobFrame:SetParent(WorldMapFrame)
  --WorldMapBlobFrame:SetAllPoints(WorldMapDetailFrame)
  WorldMapBlobFrame.Hide = nil
  WorldMapBlobFrame.Show = nil
  if blobstate then
    WorldMapBlobFrame:Show()
  end
end

function me:SPELL_UPDATE_COOLDOWN()
  --self:Debug("Updating cooldowns")
  self:UpdateFrameCurrent()
  self:UpdateCooldowns()
end

function me:PLAYER_CONTROL_GAINED()
  GetRealZoneText()
  self:TryToCompleteStep(true)
end

function me:FindData(array, what, data)
  if not (type(array) == 'table') then
    return nil
  end
  local i, d
  for i, d in pairs(array) do
    if d[what] == data then
      return d
    end
  end
end

function me:NewQuestEvent(questTitle, id)
  self:Debug('New Quest: ' .. (questTitle or '?') .. ' id ' .. (id or '?'))
  if not id or not questTitle then
    return
  end
  --[[
	if self.db.profile.debug then
		for index,quest in pairs(self.quests) do if quest.title==questTitle then
			print(dumpquest(quest))
		end end
	end
	--]]

  self.recentlyAcceptedQuests[questTitle] = true
  self.recentlyAcceptedQuests[id] = true

  if self.Writer then
    self.Writer:NotifyQuest('NEW', id, questTitle)
  end
end

function me:CompletedQuestEvent(questTitle, id, daily)
  self:Debug('Completed Quest: ' .. tostring(questTitle) .. ', id: ' .. tostring(id))

  --[[
	if not id then
		for qid,title in pairs(self.db.global.instantDailies) do
			if title==questTitle then id=qid daily=true end
		end
	end
	--]]

  self.completingQuest = nil

  if id then
    self.completedQuests[id] = true
    --self.recentlyCompletedQuests[id]=true
    --if daily then self.db.char.completedDailies[id]=time() end

    if self.CurrentGuide and self.CurrentGuide.daily and daily then
      self.db.char.permaCompletedDailies[id] = true
    end
  else
    self.completedQuestTitles[questTitle] = true
    QueryQuestsCompleted() -- start a re-fetch, just in case
    --self.recentlyCompletedQuests[questTitle]=true
    --if daily then self.db.char.completedDailies[questTitle]=time() end
  end

  if self.Writer then
    self.Writer:NotifyQuest('COMPLETED', id, questTitle)
  end
end

function me:LostQuestEvent(questTitle, id, surelyComplete)
  self:Debug(
    'Lost Quest: '
      .. tostring(questTitle)
      .. ', id: '
      .. tostring(id)
      .. ', complete: '
      .. tostring(surelyComplete)
  )

  -- NO sure-completing. A quest may well be abandoned while complete.
  surelyComplete = false

  --[[
	if (tostring(self.completingQuest)==questTitle or surelyComplete) then
		self.db.char.completedQuests[questTitle]=true
		if id then self.db.char.completedQuests[id]=true end
		self.completingQuest = nil
	end
	--]]

  if self.Writer then
    self.Writer:NotifyQuest('LOST', id, questTitle)
  end
end

function me:Frame_OnShow()
  PlaySound('igQuestLogOpen')
  --ZygorGuidesViewerFrame_Filter()
  --[[
	if UnitFactionGroup("player")=="Horde" then
		ZygorGuidesViewerFrameTitleAlliance:Hide()
	else
		ZygorGuidesViewerFrameTitleHorde:Hide()
	end
	--]]
  self.db.profile.visible = not not self.Frame:IsVisible()
  self:UpdateFrame(true)
  self:AlignFrame()

  if self.db.profile.hidearrowwithguide then
    self:SetWaypoint()
  end
end

function me:Frame_OnHide()
  PlaySound('igQuestLogClose')
  self.db.profile.visible = not not self.Frame:IsVisible()
  if not InCombatLockdown() then
    for i = 1, linecount, 1 do
      local action = _G['ZygorGuidesViewerFrame_Act' .. i .. 'Action']
      if action then
        action:Hide()
      end
      local cooldown = _G['ZygorGuidesViewerFrame_Act' .. i .. 'ActionCooldown']
      if cooldown then
        cooldown:Hide()
      end
    end
  end

  -- this is a HELL ugly hack.
  -- "Do not hide when it's the World Map that hid us".
  if
    self.db.profile.hidearrowwithguide
    and not WorldMapFrame.blockWorldMapUpdate -- this would mean we're enlarging the small map
    and not debugstack():find('TOGGLEWORLDMAP') -- UGLY hack
  then
    self:Debug('Hiding arrow with guide')
    self:SetWaypoint(false)
  end
end

function me:GoalOnClick(goalframe, button)
  local stepframe = goalframe:GetParent():GetParent()
  if not self.db.profile.showallsteps and stepframe.step ~= self.CurrentStep then
    return
  end -- no clicking on non-current steps in compact mode
  --if stepframe:GetScript("OnClick") then stepframe:GetScript("OnClick")(stepframe,button) end

  local goal = goalframe:GetParent().goal
  if not goal then
    return
  end

  --No Clue why they didnt code this in the first place
  if goal:OnClick(button) then
    return
  end

  --local num=goalframe.goalnum
  self:Debug('goal clicked ' .. tostring(goal.num))
  --local goal = self.CurrentStep.goals[num]
  if button == 'LeftButton' then
    if goal.x and not goal.force_noway then
      self:SetWaypoint(goal.num)
    elseif goal.questid then
      --if InCombatLockdown() then return end
      if self.questsbyid[goal.questid] and WorldMap_OpenToQuest then -- 3.3.0
        WorldMap_OpenToQuest(goal.questid)
        local done, posX, posY, obj = QuestPOIGetIconInfo(goal.questid)
        if posX or posY then
          local q = self.questsbyid[goal.questid]
          local title
          if q then
            title = q.title
          end
          self:Debug('Setting waypoint to POI: ' .. posX .. ' ' .. posY)
          self:SetWaypoint(posX * 100, posY * 100, title)
        end
      end
      local max = self.maxQuestLevels[goal.questid]
      self:Print(
        'Quest "'
          .. goal.quest
          .. '" (#'
          .. tostring(goal.questid)
          .. '): done at level '
          .. tostring(goal.parentStep.level)
          .. ', reaches to level '
          .. tostring(max)
      )
      local mentioned = me:GetMentionedFollowups(goal.questid)
      if #mentioned > 1 then
        local s = ''
        for i = 2, #mentioned do
          if #s > 0 then
            s = s .. '\n'
          end
          s = s
            .. '"'
            .. (me:GetQuestData(mentioned[i][1]) or '?')
            .. '" (#'
            .. tostring(mentioned[i][1])
            .. ') at level '
            .. mentioned[i][2]
        end
        self:Print('Follow-ups:\n' .. s)
      else
        self:Print('No follow-ups.')
      end
    end
  else
    if self.recentlyCompletedGoals[goal] then
      self.recentlyCompletedGoals[goal] = false
      self.recentlyStickiedGoals[goal] = false
      self.recentlyVisitedCoords[goal] = false
      if goal.quest and IsShiftKeyDown() then
        self.completedQuests[goal.quest] = nil
        if goal.questid then
          self.completedQuests[goal.questid] = nil
        end
        self:Print(
          "Marking quest '"
            .. goal.quest
            .. "'"
            .. (goal.questid and ' (#' .. goal.questid .. ')' or '')
            .. ' as not completed.'
        )
      else
        self:Print('Marking step as incomplete.')
      end
    else
      --self.recentlyCompletedGoals[goal]=true
      self.recentlyStickiedGoals[goal] = true
      if goal.quest and IsShiftKeyDown() then
        self.completedQuests[goal.quest] = true
        if goal.questid then
          self.completedQuests[goal.questid] = true
        end
        self:Print(
          "Marking quest '"
            .. goal.quest
            .. "'"
            .. (goal.questid and ' (#' .. goal.questid .. ')' or '')
            .. ' as completed.'
        )
      end
    end
    self.pause = nil
    self.LastSkip = 1
    --self.AutoskipTemp = true
    self:UpdateFrame()
  end
end

function me:GoalOnEnter(goalframe)
  local goal = goalframe:GetParent().goal
  if not goal then
    return
  end

  local wayline, infoline, image

  if goal.tooltip and not self.db.profile.tooltipsbelow then
    infoline = '|cff00ff00' .. goal.tooltip .. '|r'
  end
  if goal.x and goal.y and goal.map then
    -- if locked or force_noway, then no clicking, bare info.
    if self.db.profile.windowlocked or goal.force_noway then
      wayline = L['tooltip_waypoint_coords']:format(goal.map .. ' ' .. goal.x .. ';' .. goal.y)
    else
      wayline = L['tooltip_waypoint']:format(goal.map .. ' ' .. goal.x .. ';' .. goal.y)
    end
  end

  if goal.image then
    image = DIR .. '\\Images\\' .. goal.image .. '.tga'
  end

  if infoline or wayline or image then
    GameTooltip:SetOwner(goalframe, 'ANCHOR_TOPRIGHT')
    GameTooltip:ClearAllPoints()
    GameTooltip:SetPoint('BOTTOM', goalframe, 'TOP')
    GameTooltip:SetText(goal:GetText())

    local lines = 1
    if infoline then
      GameTooltip:AddLine(infoline, 0, 1, 0)
      if _G['GameTooltipTextLeft' .. lines]:GetWidth() > 300 then
        _G['GameTooltipTextLeft' .. lines]:SetWidth(300)
      end
      lines = lines + 1
    end
    if wayline then
      GameTooltip:AddLine(wayline, 0, 1, 0)
      if _G['GameTooltipTextLeft' .. lines]:GetWidth() > 300 then
        _G['GameTooltipTextLeft' .. lines]:SetWidth(300)
      end
      lines = lines + 1
    end
    GameTooltip:Show()
    if image then
      local img

      --[[
			local img = _G['GameTooltipZygorImage']
			if not img then
				img = GameTooltip:CreateTexture("GameTooltipZygorImage","ARTWORK")
			end
			--]]
      img = GameTooltipTexture1
      GameTooltip:AddLine(' ')
      GameTooltip:AddTexture(image)
      img:ClearAllPoints()
      img:SetPoint('TOPLEFT', _G['GameTooltipTextLeft' .. lines], 'BOTTOMLEFT')
      --img:SetTexture(image)
      img:SetWidth(128)
      img:SetHeight(128)
      img:Show()
      GameTooltip:Show()
      GameTooltip:SetHeight(150 + lines * 20)
    end
  end
end

function me:GoalOnLeave(goalframe, num)
  GameTooltip:Hide()
end

local function insert_guides(arr, guides)
  local data
  for i, guide in ipairs(guides) do
    data = ZGV:GetGuideByTitle(guide.full)
    local item = {
      text = guide.step and L['menu_last_entry']:format(guide.short or '?', guide.step)
        or (guide.short or '?'),
      checked = function()
        return ZGV.CurrentGuideName == guide.full
      end,
      func = function()
        CloseDropDownMenus()
        ZGV:SetGuide(guide.full, guide.step)
      end,
      tooltipTitle = data and data.description and guide.short,
      tooltipText = data and data.description,
      tooltipOnButton = true,
    }
    tinsert(arr, item)
  end
end

local function group_to_array(group)
  local arr = {}
  for i, group in ipairs(group.groups) do
    local item = {
      text = group.name,
      hasArrow = true,
      menuList = group_to_array(group),
      keepShownOnClick = true,
      func = function(self)
        _G[self:GetName() .. 'Check']:Hide()
      end,
      --notCheckable = true
    }
    --if #item.menuTable>0 then
    tinsert(arr, item)
    --end
  end
  insert_guides(arr, group.guides)
  return arr
end

local function BuildDropDown_GuideMenu(level, value)
  local self = ZGV
  --[[
	local menu = { }

	menu = group_to_array(self.registered_groups)
	EasyMenu(menu,ZGVFMenu,"ZygorGuidesViewerFrame_Border_TitleBar",30,10,"MENU",3)
	--]]
end

function me:OpenGuideMenu()
  --Dewdrop:Register(ZygorGuidesViewerFrame_Border_TitleBar, 'children', BuildDropDown_GuideMenu, 'point', "TOPRIGHT", 'relativePoint', "RIGHT", 'dontHook', true)
  --Dewdrop:Open(ZygorGuidesViewerFrame_Border_TitleBar)

  -- basic guides
  local menu = group_to_array(self.registered_groups)

  -- history
  tinsert(menu, { text = L['menu_last'], isTitle = true })
  insert_guides(menu, self.db.char.guides_history)

  -- display!
  UIDropDownMenu_SetAnchor(
    ZGVFMenu,
    -50,
    15,
    'TOPRIGHT',
    ZygorGuidesViewerFrame_Border_TitleBar,
    'BOTTOMRIGHT'
  )
  --local backdrop = DropDownList1:GetBackdrop()
  --backdrop.edgeSize=16
  --DropDownList1:SetBackdrop(backdrop)
  EasyMenu(menu, ZGVFMenu, nil, 30, 10, 'MENU', 3)
  UIDropDownMenu_SetWidth(ZGVFMenu, 300)
end

function me:OpenQuickMenu()
  local menu = {
    --[[
		{
			text = L['opt_group_window'],
			isTitle = true,
		},
		--]]
    {
      text = L['opt_hideborder'],
      tooltipTitle = L['opt_hideborder'],
      tooltipText = L['opt_hideborder_desc'],
      checked = function()
        return self.db.profile.hideborder
      end,
      func = function()
        self:SetOption('Display', 'hideborder')
      end,
      keepShownOnClick = true,
    },
    {
      text = L['opt_windowlocked'],
      tooltipTitle = L['opt_windowlocked'],
      tooltipText = L['opt_windowlocked_desc'],
      checked = function()
        return self.db.profile.windowlocked
      end,
      func = function()
        self:SetOption('Display', 'windowlocked')
      end,
      keepShownOnClick = true,
    },
    {
      text = L['opt_miniresizeup'],
      tooltipTitle = L['opt_miniresizeup'],
      func = function()
        self:SetOption('Display', 'resizeup')
      end,
      checked = function()
        return self.db.profile.resizeup
      end,
      keepShownOnClick = true,
    },
    {
      text = L['opt_hideincombat'],
      tooltipTitle = L['opt_hideincombat'],
      tooltipText = L['opt_hideincombat_desc'],
      checked = function()
        return self.db.profile.hideincombat
      end,
      func = function()
        self:SetOption('Display', 'hideincombat')
      end,
      keepShownOnClick = true,
    },
    --[[
		{
			name = L['opt_group_step'],
			isTitle = true,
		},
		{
			text = L["opt_do_searchforgoal"],
			notCheckable = true,
			func = function() ZGV:SearchForCompleteableGoal() end
		}
		--]]
  }

  EasyMenu(menu, ZGVFMenu, 'ZygorGuidesViewerFrame_Border_SettingsButton', 0, 0, 'MENU', 3)
end

function me:OpenQuickSteps()
  local menu = {
    {
      text = L['opt_showcountsteps'],
      isTitle = true,
    },
    {
      text = L['opt_showcountsteps_all'],
      func = function()
        self:SetOption('Display', 'showcountsteps 0')
      end,
      checked = function()
        return self.db.profile.showallsteps
      end,
    },
    {
      text = '1',
      func = function()
        self:SetOption('Display', 'showcountsteps 1')
      end,
      checked = function()
        return not self.db.profile.showallsteps and self.db.profile.showcountsteps == 1
      end,
    },
    {
      text = '2',
      func = function()
        self:SetOption('Display', 'showcountsteps 2')
      end,
      checked = function()
        return not self.db.profile.showallsteps and self.db.profile.showcountsteps == 2
      end,
    },
    {
      text = '3',
      func = function()
        self:SetOption('Display', 'showcountsteps 3')
      end,
      checked = function()
        return not self.db.profile.showallsteps and self.db.profile.showcountsteps == 3
      end,
    },
    {
      text = '4',
      func = function()
        self:SetOption('Display', 'showcountsteps 4')
      end,
      checked = function()
        return not self.db.profile.showallsteps and self.db.profile.showcountsteps == 4
      end,
    },
    {
      text = '5',
      func = function()
        self:SetOption('Display', 'showcountsteps 5')
      end,
      checked = function()
        return not self.db.profile.showallsteps and self.db.profile.showcountsteps == 5
      end,
    },
  }

  EasyMenu(menu, ZGVFMenu, 'cursor', 0, 0, 'MENU', 3)
end

local function split(str, sep)
  local fields = {}
  str = str .. sep
  str:gsub('(.-)' .. sep, function(c)
    tinsert(fields, c)
  end)
  return fields
end

local function FindGroup(self, rawpath, title)
  --AutoGuideSorting
  local _, _, s, e = string.find(title, '.+%((%d+)-(%d+)%)')

  if s then
    truestart = math.floor(s / 10) * 10
    rawpath = rawpath .. '\\Level ' .. tostring(truestart) .. '-' .. tostring(truestart + 10)
  end

  local path = split(rawpath, '\\')

  -- create one
  local group = self
  for i = 1, #path do
    local found = false
    for n, gr in ipairs(group.groups) do
      if gr.name == path[i] then
        found = true
        group = gr
      end
    end
    if not found then
      tinsert(group.groups, { name = path[i], groups = {}, guides = {} })
      group = group.groups[#group.groups]
    end
  end
  return group
end

me.registered_groups = { groups = {}, guides = {} }

function me:RegisterGuide(title, data, extra)
  if type(data) == 'table' then
    return me:RegisterGuide(title, extra, data)
  end

  local group, tit = title:match('^(.*)\\+(.-)$')
  if group then
    group = FindGroup(self.registered_groups, group, tit)
  else
    group = self.registered_groups
  end

  local guide =
    { ['title'] = title, ['title_short'] = tit or title, ['rawdata'] = data, ['extra'] = extra }

  tinsert(group.guides, { full = title, short = tit or title, num = #self.registeredguides + 1 })
  tinsert(self.registeredguides, guide)
end

--function me:RegisterGuideCompat(...)
--  local n = select("#", ...)            -- number of arguments
--	local title, data = select(1,...),select(n, ...)
--print("Compat Guide: "..title)
--	me:RegisterGuide(title, data)
--end

me.registered_mapspotset_groups = { groups = {}, guides = {} }

function me:RegisterMapSpots(title, data)
  local group, tit = title:match('^(.*)\\+(.-)$')
  if group then
    group = FindGroup(self.registered_mapspotset_groups, group, tit)
  else
    group = self.registered_mapspotset_groups
  end

  local set = self.MapSpotSetProto:NewRaw(title, tit or title, data)

  tinsert(
    group.guides,
    { full = title, short = tit or title, num = #self.registeredmapspotsets + 1 }
  )
  tinsert(self.registeredmapspotsets, set)
end

--[[
function me:UnregisterGuide(name)
	local data
	if type(name)=="number" then
		if self.registeredguides[name] then
			data = self.registeredguides[name].data
			table.remove(self.registeredguides,name)
			self:Print("Unregistered guide number: "..name)
		else
			self:Print("Cannot find guide number: "..name)
			return false
		end
	else
		local i,v
		for i,v in ipairs(self.registeredguides) do
			if v.title==name then
				data = v
				table.remove(self.registeredguides,i)
				self:Print("Unregistered guide: "..name)
			end
		end
		if not data then
			self:Print("Cannot find guide: "..name)
			return false
		end
	end
	if data.is_stored then
		self.db.global.storedguides[name] = nil
		self:Print("Removed stored data for: "..name)
	end
	return true
end
--]]

function me:Startup()
  if self.guidesloaded then
    return
  end
  if me:ParseGuides() then
    self:OnGuidesLoaded()
  end
end

function me:OnGuidesLoaded()
  self.Log:Add('Guides loaded. -----')

  self:QueryQuests()

  self:ListMentionedQuests()
  self:CacheMentionedFollowups()

  self.completiontimer = self:ScheduleRepeatingTimer('TryToCompleteStep', 0.1)
  --self.notetimer = self:ScheduleRepeatingTimer("SetWaypoint", 1)
  self.dailytimer = self:ScheduleRepeatingTimer('QuestTracking_ResetDailies', 5)

  --self:CancelTimer(self.startuptimer,true)

  self.pause = true

  self:Print(L['welcome_guides']:format(#self.registeredguides))

  self:UpdateFrame(true)

  self:OnFirstQuestLogUpdate()
end

function me:ParseGuides()
  if not self.db.char.maint_startguides then
    return true
  end
  self.loading = true

  if #self.registeredguides > 0 then
    for i, guide in ipairs(self.registeredguides) do
      if guide.rawdata then
        local status, parsed, err, line, linedata = pcall(self.ParseEntry, self, guide)
        if status and parsed then
          for k, v in pairs(parsed) do
            guide[k] = v
          end
          guide.rawdata = nil
          self.loadprogress = i / #self.registeredguides
        else
          if not status then
            err = parsed
          end
          if err then
            self:Print(
              L['message_errorloading_full']:format(guide.title, line or 0, linedata or '???', err)
            )
          else
            self:Print(L['message_errorloading_brief']:format(guide.title))
          end
          guide.rawdata = nil
        end
        self:UpdateFrame(true)
        return false
      end
    end
  end

  if #self.registeredmapspotsets > 0 then
    for i, guide in ipairs(self.registeredmapspotsets) do
      if guide.rawdata then
        local status, parsedset, err, line, linedata = pcall(self.MapSpotSetProto.ParseRaw, guide)
        if status then
          self.loadprogress = i / #self.registeredmapspotsets
          guide:Show()
        else
          if not status then
            err = parsedset
            line = 0
            linedata = ''
          end
          if err then
            self:Print(L['message_errorloading_full']:format(guide.title, line, linedata, err))
          else
            self:Print(L['message_errorloading_brief']:format(guide.title))
          end
          guide.rawdata = nil
        end
        self:UpdateFrame(true)
        return false
      end
    end

    local tab1 = self.Frame.Border.Gears.Tab1
    tab1:SetPoint('LEFT', self.Frame.Border, 'TOPLEFT', 65, -12)
    tab1:SetText('Guides')
    tab1:SetNormalFontObject(ZGVFTabFont)
    --PanelTemplates_TabResize(self.Tab1,0);
    --_G[self.Tab1:GetName().."HighlightTexture"]:SetWidth(self.Tab1:GetTextWidth() + 20);
    tab1:SetScript('OnClick', function()
      ZGV:SetDisplayMode('guide')
    end)
    tab1:Show()

    local tab2 = self.Frame.Border.Gears.Tab2
    tab2:SetPoint('LEFT', tab1, 'RIGHT')
    tab2:SetText('Spots')
    tab2:SetNormalFontObject(ZGVFTabFont)
    --ZGVFrameTab2Text:SetText("Spots")
    --PanelTemplates_TabResize(self.Tab2,0);
    --_G[self.Tab2:GetName().."HighlightTexture"]:SetWidth(self.Tab2:GetTextWidth() + 20);
    tab2:SetScript('OnClick', function()
      ZGV:SetDisplayMode('gold')
    end)
    tab2:Show()
  end

  self.loading = nil
  self.guidesloaded = true
  return true
end

--[[
function me:RegisterStoredGuides()
	local k,v
	for k,v in pairs(self.db.global.storedguides) do
		table.insert(self.registeredguides,{title=k,data=v,is_stored=true})
		self:Print("Retrieved guide "..k.." from storage.")
	end
end
--]]

function me:UpdateMapButton()
  if self.db.profile.showmapbutton then
    ZygorGuidesViewerMapIcon:Show()
  else
    ZygorGuidesViewerMapIcon:Hide()
  end
end

function me:GetGuides()
  if
    not ZygorGuidesViewer
    or not ZygorGuidesViewer.db
    or not ZygorGuidesViewer.registeredguides
  then
    return {}
  end
  local t = {}
  for i, data in ipairs(ZygorGuidesViewer.registeredguides) do
    t[i] = data.title
  end
  return t
end

function me.GetGuidesRev()
  if
    not ZygorGuidesViewer
    or not ZygorGuidesViewer.db
    or not ZygorGuidesViewer.registeredguides
  then
    return {}
  end
  local t = {}
  for i, data in ipairs(ZygorGuidesViewer.registeredguides) do
    t[data.title] = i
  end
  return t
end

-- function me:Search(s) --removed
-- function me:Find(s) --removed

local function tostr(val)
  if type(val) == 'string' then
    return '"' .. val .. '"'
  elseif type(val) == 'number' then
    return tostring(val)
  elseif not val then
    return 'nil'
  elseif type(val) == 'boolean' then
    return tostring(val) .. ' [' .. type(val) .. ']'
  end
end

local function superconcat(table, glue)
  local s = ''
  for i = 1, #table do
    if #s > 0 then
      s = s .. glue
    end
    s = s .. tostring(table[i])
  end
  return s
end

local function anytostring(s)
  if type(s) == 'table' then
    return superconcat(s, ',')
  else
    return tostring(s)
  end
end

function me:BugReport(maint)
  if not self.dumpFrame then
    self:CreateDumpFrame()
  end

  HideUIPanel(InterfaceOptionsFrame)
  HideUIPanel(ZygorGuidesViewerMaintenanceFrame)

  local s = ''
  s = ('Zygor Guides Viewer v%s\n'):format(self.version)
  s = s .. '\n'
  s = s
    .. ('Guide: %s\nStep: %d\n'):format(tostr(self.CurrentGuideName), tostr(self.CurrentStepNum))

  if maint then
    s = s
      .. '\nMAINTENANCE OPTIONS THAT WERE ENABLED PROPERLY: ______________\nMAINTENANCE OPTION THAT CAUSED DISCONNECTION: _______________\n\n'
  end

  local step = self.CurrentStep
  if step then
    for k, v in pairs(step) do
      if
        k ~= 'goals'
        and k ~= 'num'
        and k ~= 'L'
        and k ~= 'isobsolete'
        and k ~= 'isauxiliary'
        and type(v) ~= 'function'
      then
        s = s .. ('  %s: %s\n'):format(k, anytostring(v))
      end
    end
    s = s
      .. ('  (completed: %s, auxiliary: %s, obsolete: %s)\n'):format(
        step:IsComplete() and 'YES' or 'no',
        step:IsAuxiliary() and 'YES' or 'no',
        step:IsObsolete() and 'YES' or 'no'
      )

    s = s .. 'Goals: \n'

    for i, goal in ipairs(step.goals) do
      s = s
        .. ('%d. %s %s\n'):format(
          i,
          ('. '):rep(goal.indent),
          goal.text and '"' .. goal.text .. '"' or '<' .. goal:GetText() .. '>'
        )
      for k, v in pairs(goal) do
        if
          k ~= 'map'
          and k ~= 'x'
          and k ~= 'y'
          and k ~= 'dist'
          and k ~= 'indent'
          and k ~= 'text'
          and k ~= 'parentStep'
          and k ~= 'num'
          and k ~= 'status'
          and k ~= 'useitem'
          and k ~= 'useitemid'
          and k ~= 'castspell'
          and k ~= 'castspellid'
          and k ~= 'quest'
          and k ~= 'questid'
          and k ~= 'questreqs'
          and k ~= 'mobs'
          and k ~= 'target'
          and k ~= 'targetid'
          and k ~= 'objnum'
          and type(v) ~= 'function'
        then
          s = s .. ('    %s: %s\n'):format(k, anytostring(v))
        end
      end
      if goal.x or goal.y or goal.action == 'goto' then
        s = s
          .. ('    map: %s %s,%s'):format(goal.map or 'unknown', goal.x or 'nil', goal.y or 'nil')
        if goal.dist then
          s = s .. ('  +/- %s'):format(goal.dist)
        end
        s = s .. '\n'
      end
      if goal.useitemid or goal.useitem then
        s = s .. ('   useitem: "%s"  ##%s'):format(tostring(goal.useitem), tostring(goal.useitemid))
        if goal.useitemid then
          local a = { GetItemInfo(goal.useitemid) }
          s = s .. ('  GetItemInfo(%d) == %s\n'):format(goal.useitemid, superconcat(a, ','))
        elseif goal.useitem then
          local a = { GetItemInfo(goal.useitem) }
          s = s .. ('  GetItemInfo("%s") == %s\n'):format(goal.useitem, superconcat(a, ','))
        end
      end
      if goal.castspellid or goal.castspell then
        s = s
          .. ('   castspell: "%s"  ##%s'):format(
            tostring(goal.castspell),
            tostring(goal.castspellid)
          )
        if goal.castspellid then
          local a = { GetSpellInfo(goal.castspellid) }
          s = s .. ('  GetSpellInfo(%d) == %s\n'):format(goal.castspellid, superconcat(a, ','))
        elseif goal.castspell then
          local a = { GetSpellInfo(goal.castspell) }
          s = s .. ('  GetSpellInfo("%s") == %s\n'):format(goal.castspell, superconcat(a, ','))
        end
      end
      if goal.quest or goal.questid then
        s = s .. ('    quest: "%s" ##%d'):format(tostring(goal.quest), tostring(goal.questid))
        if goal.questid then
          local questdata = self.questsbyid[goal.questid]
          if goal.objnum then
            if questdata then
              local goaltext = questdata.goals[goal.objnum].item
              if not goaltext then
                goaltext = '???'
              end
              s = s .. (' goal %d: "%s"'):format(goal.objnum, goaltext)
            else
              s = s .. (' goal %d'):format(goal.objnum)
            end
          else
            s = s .. ' (no goal)'
          end
          if questdata then
            s = s .. '  - quest "' .. questdata['title'] .. '" ##' .. questdata['id'] .. ' in log '
          else
            s = s .. '  - quest not in log '
          end
          if self.completedQuests[goal.questid] then
            s = s .. '(id: completed)'
          else
            s = s .. '(id: not completed)'
            if self.completedQuestTitles[goal.quest] then
              s = s .. ' (title: completed)'
            else
              s = s .. ' (title: not completed)'
            end
          end
        end
        s = s .. '\n'
      end
      if goal.target then
        s = s .. ('    target: "%s"'):format(goal.target)
        if goal.targetid then
          s = s .. (' ##%d\n'):format(goal.targetid)
        end
        s = s .. '\n'
      end
      if goal.mobs then
        s = s .. '    mobs: '
        for k, v in ipairs(goal.mobs) do
          s = s .. v.name .. '  '
        end
        s = s .. '\n'
      end
      if goal.questreqs and #goal.questreqs > 0 then
        s = s .. '    questreqs: ' .. superconcat(goal.questreqs, ',') .. '\n'
      end
      if goal.condition_visible then
        s = s .. '    visibility condition: ' .. goal.condition_visible_raw .. '\n'
      end

      if goal:IsCompleteable() then
        local comp, poss = goal:IsComplete()
        s = s
          .. ('    (complete: %s, possible: %s, auxiliary: %s, obsolete: %s)\n'):format(
            comp and 'YES' or 'no',
            poss and 'YES' or 'no',
            step:IsAuxiliary() and 'YES' or 'no',
            step:IsObsolete() and 'YES' or 'no'
          )
      else
        s = s .. '    (not completeable)\n'
      end

      s = s .. '    Status: ' .. goal:GetStatus() .. '\n'
    end
    s = s .. '\n'
  else
    s = s .. 'No current step loaded.\n\n'
  end

  s = s .. '--- Player information ---\n'
  s = s
    .. ('Race: %s  Class: %s  Level: %d\n'):format(
      select(2, UnitRace('player')),
      select(2, UnitClass('player')),
      UnitLevel('player')
    )
  local x, y = GetPlayerMapPosition('player')
  s = s
    .. ("Position: realzone:'%s' x:%g,y:%g (zone:'%s' subzone:'%s' minimapzone:'%s')\n"):format(
      GetRealZoneText(),
      x * 100,
      y * 100,
      GetZoneText(),
      GetSubZoneText(),
      GetMinimapZoneText()
    )
  if GetLocale() ~= 'enUS' then
    s = s
      .. ("    enUS: realzone:'%s' zone:'%s' subzone:'%s' minimapzone:'%s')\n"):format(
        BZR[GetRealZoneText()],
        BZR[GetZoneText()],
        BZR[GetSubZoneText()] or '(' .. GetSubZoneText() .. ')',
        BZR[GetMinimapZoneText()] or '(' .. GetMinimapZoneText() .. ')'
      )
    s = s .. ('Locale: %s\n'):format(GetLocale())
  end
  s = s .. '\n'

  s = s .. '-- Cached quest log --\n'
  for index, quest in pairs(self.quests) do
    s = s .. dumpquest(quest)
  end
  s = s .. '\n'

  s = s .. '-- Cached quest log, by ID --\n'
  for id, quest in pairs(self.questsbyid) do
    s = s .. ('#%d: %s\n'):format(id, quest.title)
  end
  s = s .. '\n'

  s = s .. '-- Items --\n'
  local inventory = {}
  for bag = -2, 4 do
    for slot = 1, GetContainerNumSlots(bag) do
      local item = GetContainerItemLink(bag, slot)
      if item then
        local id, name = string.match(item, 'item:(.-):.-|h%[(.-)%]')
        local tex, count = GetContainerItemInfo(bag, slot)
        tinsert(inventory, ('    %s ##%d x%d\n'):format(name, id, count))
      end
    end
  end
  table.sort(inventory)
  s = s .. table.concat(inventory, '')
  s = s .. '\n'

  s = s .. '-- Buffs/debuffs --\n'
  for i = 1, 30 do
    local name, _, tex = UnitBuff('player', i)
    if name then
      s = s .. ('%s ("%s")\n'):format(name, tex)
    end
  end
  for i = 1, 30 do
    local name, _, tex = UnitDebuff('player', i)
    if name then
      s = s .. ('%s ("%s")\n'):format(name, tex)
    end
  end
  s = s .. '\n'

  s = s .. '-- Pet action bar --\n'
  for i = 1, 12 do
    local name, _, tex = GetPetActionInfo(i)
    if name then
      s = s .. ('%d. %s ("%s")\n'):format(i, name, tex)
    end
  end
  s = s .. '\n'

  s = s .. '-- Flight Paths --\n'
  if self.LibTaxi then
    s = s .. table.concat(TableKeys(self.db.char.taxis), ' , ')
  end
  s = s .. '\n\n'

  s = s .. '-- Options --\n'
  s = s .. 'Profile:\n'
  for k, v in pairs(self.db.profile) do
    s = s .. '  ' .. k .. ' = ' .. anytostring(v) .. '\n'
  end
  s = s .. '\n'

  --s = s .. self:DumpVal(self.quests,0,4,true)
  --self:Print(s)
  s = s .. '-- Log --\n'
  s = s .. self.Log:Dump(100)

  self.dumpFrame.editBox:SetText(s)
  local title = maint and 'Zygor Guides Viewer' or (self.CurrentGuideName or L['report_notitle'])
  local author = maint and 'zygor@zygorguides.com'
    or (self.CurrentGuide and self.CurrentGuide.author or L['report_noauthor'])
  self.dumpFrame.title:SetText(L['report_title']:format(title, author))
  ShowUIPanel(self.dumpFrame)
  self.dumpFrame.editBox:HighlightText(0)
  self.dumpFrame.editBox:SetFocus(true)
end

function me:DumpVal(val, lev, maxlev, nofun)
  if not lev then
    lev = 1
  end
  if not maxlev then
    maxlev = 1
  end

  if lev > maxlev then
    return '...'
  end
  local s = ''
  if type(val) == 'string' then
    s = ('"%s"'):format(val)
  elseif type(val) == 'number' then
    s = ('%s'):format(tostring(val))
  elseif type(val) == 'function' then
    s = ''
  elseif type(val) == 'table' then
    s = '\n'
    for k, v in pairs(val) do
      if type(k) ~= 'string' or not k:find('^parent') then
        if type(v) ~= 'function' then
          s = s .. ('   '):rep(lev) .. ('%s=%s'):format(k, self:DumpVal(v, lev + 1, maxlev, nofun))
        elseif not nofun then
          s = s .. ('   '):rep(lev) .. ('%s(function)\n'):format(k)
        end
      end
    end
  end

  return s .. '\n'
end

-- misc:

function me:CreateDumpFrame()
  local name = 'ZygorGuidesViewer_DumpFrame'

  local frame = CreateFrame('Frame', name, UIParent)
  self.dumpFrame = frame
  frame:SetBackdrop({
    bgFile = [[Interface\DialogFrame\UI-DialogBox-Background]],
    edgeFile = [[Interface\DialogFrame\UI-DialogBox-Border]],
    tile = true,
    tileSize = 16,
    edgeSize = 16,
    insets = { left = 3, right = 3, top = 5, bottom = 3 },
  })
  frame:SetBackdropColor(0, 0, 0, 1)
  frame:SetWidth(500)
  frame:SetHeight(400)
  frame:SetPoint('CENTER', UIParent, 'CENTER')
  frame:Hide()
  frame:SetFrameStrata('DIALOG')
  tinsert(UISpecialFrames, name)

  local scrollArea =
    CreateFrame('ScrollFrame', name .. 'Scroll', frame, 'UIPanelScrollFrameTemplate')
  scrollArea:SetPoint('TOPLEFT', frame, 'TOPLEFT', 8, -50)
  scrollArea:SetPoint('BOTTOMRIGHT', frame, 'BOTTOMRIGHT', -30, 8)

  local editBox = CreateFrame('EditBox', nil, frame)
  editBox:SetMultiLine(true)
  editBox:SetMaxLetters(99999)
  editBox:EnableMouse(true)
  editBox:SetAutoFocus(false)
  editBox:SetFontObject(ChatFontSmall)
  editBox:SetWidth(400)
  editBox:SetHeight(270)
  editBox:SetScript('OnEscapePressed', function()
    frame:Hide()
  end)
  self.dumpFrame.editBox = editBox

  scrollArea:SetScrollChild(editBox)

  local close = CreateFrame('Button', nil, frame, 'UIPanelCloseButton')
  close:SetPoint('TOPRIGHT', frame, 'TOPRIGHT')

  local title = frame:CreateFontString(nil, 'OVERLAY', 'GameFontNormalSmall')
  self.dumpFrame.title = title
  title:SetPoint('TOPLEFT', frame, 'TOPLEFT', 10, -10)
  title:SetPoint('BOTTOMRIGHT', frame, 'TOPRIGHT', -30, -45)
  title:SetJustifyH('CENTER')
  title:SetJustifyV('TOP')
end

local math_floor = math.floor
local function round(num, digits)
  -- banker's rounding
  local mantissa = 10 ^ digits
  local norm = num * mantissa
  norm = norm + 0.5
  local norm_f = math_floor(norm)
  if norm == norm_f and (norm_f % 2) ~= 0 then
    return (norm_f - 1) / mantissa
  end
  return norm_f / mantissa
end
function me:Test(arg1, arg2)
  local a = { GetMapZones(GetCurrentMapContinent()) }
  local x, y = GetPlayerMapPosition('player')
  local id = round(x * 10000, 0) + round(y * 10000, 0) * 10001
  self:Print("You're in " .. a[GetCurrentMapZone()] .. ' at Cart2 coords ' .. id)
end

function me:Echo(s)
  --if not self.db.profile.silent then
  self:Print(tostring(s))
  --end
end

function me:Debug(s)
  self.Log:Add(s)
  if self and self.db and self.db.profile and self.db.profile.debug then
    self.DebugI = (self.DebugI or 0) + 1
    self:Echo('|cffaaaaaa#' .. self.DebugI .. ': ' .. tostring(s))
  end
end

function me:GetQuestData(qid)
  if not self.db.char.maint_fetchquestdata then
    return nil
  end
  Gratuity:SetHyperlink('|Hquest:' .. qid .. ':1|h[q]|h')

  local n = Gratuity:NumLines()
  if n <= 0 then
    return
  end

  local title, objs

  for i = 1, n do
    local line =
      Gratuity:GetLine(i):gsub('|c%x%x%x%x%x%x%x%x', ''):gsub('|r', ''):gsub('[\n\t]', ' ')
    if i == 1 then
      title = line
    else
      local line = line:match('^%s+%- (.+)$')
      if line then
        local o, n = line:match('^(.-) x.?.?(%d+)$')
        if not o then
          o = line
        end
        if not objs then
          objs = {}
        end
        table.insert(objs, o)
      end
    end
  end

  return title, objs
end

function me:GetItemData(itemid, n)
  if not self.db.char.maint_fetchitemdata then
    return nil
  end
  if not itemid then
    return
  end
  Gratuity:SetHyperlink('|Hitem:' .. itemid .. ':0:0:0:0:0:0:0:0|h[q]|h')

  local n = Gratuity:NumLines()
  if n <= 0 then
    return
  end

  local title, objs

  local line = Gratuity:GetLine(1)
  line = line:gsub('|c%x%x%x%x%x%x%x%x', ''):gsub('|r', ''):gsub('[\n\t]', ' ')
  if line == RETRIEVING_ITEM_INFO then
    return
  else
    return line
  end
end

-- HACKS
function me:ListQuests(from, to)
  local CQI = Cartographer_QuestInfo
  local qlog = ''
  for i = from, to do
    local level = CQI:PeekQuest(i)
    --if not level then level=0 end
    if level then
      local title, _, _, _, nobjs = CQI:GetQuestText(i, level)
      --if not title then title = CQI:GetQuestText(i,level) end -- well, they said to repeat it...
      --self:Print(i..": |cff808080|Hquest:"..i..":"..level.."|h["..tostring(title).."]|h|r "..(type(objs)=="table" and "{"..table.concat(nobjs,",").."}" or ""))
      qlog = qlog
        .. i
        .. ': '
        .. tostring(title)
        .. (type(nobjs) == 'table' and ' {' .. table.concat(nobjs, ',') .. '}' or '')
        .. '|n'
    end
  end
  if Chatter then
    Chatter:GetModule('Chat Copy').editBox:SetText(qlog)
    Chatter:GetModule('Chat Copy').editBox:HighlightText(0)
    Chatter:GetModule('Chat Copy').frame:Show()
  end
end

function me:GetTranslatedNPC(num)
  if not ZygorGuidesNPCs then
    return
  end
  local s = ZygorGuidesNPCs[num]
  if not s then
    return
  end
  local name, desc = s:match('.|(.-)|(.*)')
  if desc == '' then
    desc = nil
  end
  return name, desc
end

function me:PruneNPCs()
  if not ZygorGuidesNPCs then
    return
  end
  local faction, _ = UnitFactionGroup('player')
  if not faction then
    return
  end
  local badf = (faction == 'Alliance') and 'H' or 'A'
  for i, d in pairs(ZygorGuidesNPCs) do
    if d:sub(1, 1) == badf then
      ZygorGuidesNPCs[i] = nil
    end
  end
end

function me:ReloadTranslation()
  for i, guide in ipairs(self.registeredguides) do
    for s, step in ipairs(guide.steps) do
      for g, goal in ipairs(step.goals) do
        goal.L = false
      end
    end
  end
end

-- used for steps and goals
--[[
function me.ConditionTrue(subject,case)
	if not subject.conditions then return false end
	local f=subject.conditions[case]
	if type(f)=="function" then
		return f()
	elseif type(f)=="string" then
		f=subject.conditions[f]
		assert(type(f)=="function","What? This step has cross-referencing conditions? wtf.")
		return not f()
	end
end
--]]

function me.gradient3(perc, ar, ag, ab, br, bg, bb, cr, cg, cb, middle)
  if perc == 1 then
    return cr, cg, cb
  elseif perc == 0 then
    return ar, ag, ab
  else
    if perc <= middle then
      perc = perc / middle
      return ar + (br - ar) * perc, ag + (bg - ag) * perc, ab + (bb - ab) * perc
    else
      perc = (perc - middle) / (1 - middle)
      return br + (cr - br) * perc, bg + (cg - bg) * perc, bb + (cb - bb) * perc
    end
  end
end

local getstickies_temp = {}
function ZGV:GetStickiesAt(stepnum, laststepnum) -- was stepnum,show_complete but second param was no longer used
  --ZGV:Debug(tostring(stepnum).." "..tostring(laststepnum))
  local laststepnum = laststepnum or stepnum
  local changed = false

  for _, stickystep in ipairs(getstickies_temp) do
    if stickystep:IsComplete() or not stickystep:CanBeSticky() then
      changed = true
    end
  end

  stepnum = stepnum or self.CurrentStepNum
  local step = self.CurrentGuide.steps[stepnum]
  wipe(getstickies_temp)

  if step.stickies then
    for _, stickystep in ipairs(step.stickies) do
      --print(stickystep.num,laststepnum,stickystep:IsComplete(),stickystep:CanBeSticky() )
      if
        (stickystep.num > laststepnum)
        and not stickystep:IsComplete()
        and stickystep:CanBeSticky()
      then
        tinsert(getstickies_temp, stickystep)
      end
    end
  end
  return getstickies_temp, changed
end

--hooksecurefunc("WorldMapFrame_UpdateQuests",function() if not InCombatLockdown() then text=nil end end)
--hooksecurefunc("QuestInfo_Display",function() if not InCombatLockdown() then shownFrame=nil bottomShownFrame=nil end end)
