-- Copyright (C) 2010 Tyson Brown
-- 
-- This file is part QuestHelper2, an AddOn for World of Warcraft.
-- 
-- QuestHelper2 is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
-- 
-- QuestHelper2 is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU General Public License for more details.
-- 
-- You should have received a copy of the GNU General Public License
-- along with QuestHelper2. If not, see <http://www.gnu.org/licenses/>.

if LibDebug then LibDebug() end
local shared = select(2, ...)
local L = shared.L

-- Helpers for creating the widgets on the configuration panels

local localized_font_strings = {}

-- Maps panel names to a function to call when populating them.
local extra_config = {}

local function addSpace(frame, px)
  frame.gap = frame.gap + 8
end

local function addText(panel, text, font, key)
  local font_string = panel:CreateFontString(nil, "OVERLAY", font or "GameFontHighlightSmallLeft")
  
  font_string:SetPoint("LEFT", 16, 0)
  
  if panel.prev_element then
    font_string:SetPoint("TOP", panel.prev_element, "BOTTOM", 0, -panel.gap)
  else
    font_string:SetPoint("TOP", 0, -panel.gap)
  end
  --font_string:SetPoint("RIGHT", -16, 0)
  -- Simply setting the right edge doesn't cause the text to wrap for some strange reason,
  -- although explicitly setting the width does. >_<
  font_string:SetWidth(panel:GetWidth()-32)
  
  if key then
    font_string:SetText(L[key])
    localized_font_strings[font_string] = key
  else
    font_string:SetText(text)
  end
  
  panel.prev_element, panel.gap = font_string, 0
  
  return font_string
end

local function disableText(font_string, disabled)
  font_string:SetAlpha(disabled and 0.5 or 1)
end

local function addFrame(panel, frame, label_key)
  local label
  
  if label_key then
    label = panel:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmallLeft")
    label:SetText(L[label_key])
    
    localized_font_strings[label] = label_key
    
    label:SetPoint("LEFT", 16, 0)
    
    if label:GetStringWidth() > 140 then
      frame:SetPoint("LEFT", label, "RIGHT", 4, 0)
    else
      frame:SetPoint("LEFT", panel, 160, 0)
    end
    
    if panel.prev_element then
      frame:SetPoint("TOP", panel.prev_element, "BOTTOM", 0, -panel.gap)
      label:SetPoint("TOP", panel.prev_element, "BOTTOM", 0, -panel.gap-(frame:GetHeight()-label:GetHeight())/2)
    else
      frame:SetPoint("TOP", panel, 0, -panel.gap)
      label:SetPoint("TOP", panel, 0, (frame:GetHeight()-label:GetHeight())/2-panel.gap)
    end
  else
    if panel.prev_element then
      frame:SetPoint("LEFT", panel, 16, 0)
      frame:SetPoint("TOP", panel.prev_element, "BOTTOM", 0, -panel.gap)
    else
      frame:SetPoint("TOPLEFT", panel, 16, -panel.gap)
    end
  end
  
  panel.prev_element, panel.gap = frame, 0
  return frame, label
end

local function addButton(panel, label_key, tooltip_key, cb)
  local button = CreateFrame("Button", "QuestHelper2_Setting_"..label_key, panel, "UIPanelButtonTemplate2")
  button:SetWidth(140)
  button:SetHeight(22)
  button:SetText(L[label_key])
  button:SetScript("OnClick", cb)
  
  button:SetScript("OnEnter", function ()
    GameTooltip:SetOwner(button, "ANCHOR_TOP")
    GameTooltip:SetText(L[label_key])
    GameTooltip:AddLine(L[tooltip_key], 1, 1, 1, true)
    GameTooltip:Show()
  end)
  
  button:SetScript("OnLeave", function ()
    GameTooltip:Hide()
  end)
  
  -- A button isn't a font string, but it has a SetText function, so this works.
  localized_font_strings[button] = label_key
  
  addFrame(panel, button)
end

local create_widget = {}

function create_widget.boolean(panel, setting, attribute)
  local checkbox = CreateFrame("CheckButton", "QuestHelper2_Setting_"..setting, panel, "InterfaceOptionsCheckButtonTemplate")
  
  checkbox:SetScript("OnClick", function ()
    if checkbox:GetChecked() then
      PlaySound("igMainMenuOptionCheckBoxOn")
      shared.settings[setting] = true
    else
      PlaySound("igMainMenuOptionCheckBoxOff")
      shared.settings[setting] = false
    end
  end)
  
  shared.onSetting("locale", function ()
    local title = L["SETTING_"..setting:upper()]
    _G[checkbox:GetName().."Text"]:SetText(title)
    checkbox.tooltipText = title
    checkbox.tooltipRequirement = L["SETTING_"..setting:upper().."_TOOLTIP"]
  end)
  
  shared.onSettingOverride(setting, function (overridden)
    checkbox[overridden and "Disable" or "Enable"](checkbox)
    disableText(_G[checkbox:GetName().."Text"], overridden)
  end)
  
  shared.onSetting(setting, function (enabled) checkbox:SetChecked(enabled) end)
  
  addFrame(panel, checkbox)
end

function create_widget.enum(panel, setting, attribute)
  local dropdown = CreateFrame("Frame", "QuestHelper2_Setting_"..setting, panel, "UIDropDownMenuTemplate")
  
  local function clicked(info)
    if attribute.subtype == "sound" then
      PlaySoundFile(info.value)
    end
    
    shared.settings[setting] = info.value
  end
  
  UIDropDownMenu_JustifyText(dropdown, "LEFT")
  UIDropDownMenu_SetWidth(dropdown, 160)
  
  shared.onSetting("locale", function ()
    UIDropDownMenu_ClearAll(dropdown)
    
    UIDropDownMenu_Initialize(dropdown, function ()
      for i,enum in ipairs(attribute) do
        local info =  UIDropDownMenu_CreateInfo()
        
        if attribute.translate_keys then
          local lang_key = "SETTING_"..setting:upper().."_"..tostring(enum):upper()
          local title = L[lang_key]
          local desc = L[lang_key.."_TOOLTIP"]
          
          info.text = title
          info.tooltipTitle = title
          info.tooltipText = desc
        else
          if attribute.filenames then
            info.text = enum:match("([^\\/]+)%.[^%.]+$") or enum
          else
            info.text = enum:match("([^\\/]+)$") or enum
          end
          
          info.tooltipTitle = nil
          info.tooltipText = nil
        end
        
        info.value = enum
        info.func = clicked
        
        UIDropDownMenu_AddButton(info)
      end
    end)
    
    UIDropDownMenu_SetSelectedValue(dropdown, shared.settings[setting])
  end)
  
  shared.onSetting(setting, function (value)
    UIDropDownMenu_SetSelectedValue(dropdown, value)
  end)
  
  shared.onSettingOverride(setting, function (overridden)
    (overridden and UIDropDownMenu_DisableDropDown or UIDropDownMenu_EnableDropDown)(dropdown)
  end)
  
  local label = select(2, addFrame(panel, dropdown, "SETTING_"..setting:upper()))
  
  shared.onSettingOverride(setting, function (overridden)
    (overridden and UIDropDownMenu_DisableDropDown or UIDropDownMenu_EnableDropDown)(dropdown)
    disableText(label, overridden)
  end)
end

function create_widget.range(panel, setting, attribute)
  local slider = CreateFrame("Slider", "QuestHelper2_Setting_"..setting, panel)
  
  slider:SetPoint("RIGHT", panel, -16, 0)
  slider:SetHeight(17)
  
  local slider_max = 10000
  slider:SetMinMaxValues(0, slider_max)
  slider:SetValueStep(0.01)
  
  slider:SetOrientation("HORIZONTAL")
  slider:SetThumbTexture("Interface\\Buttons\\UI-SliderBar-Button-Horizontal")
  slider:SetBackdrop({
    bgFile = "Interface\\Buttons\\UI-SliderBar-Background",
    edgeFile = "Interface\\Buttons\\UI-SliderBar-Border",
    edgeSize = 8, tile = true, tileSize = 8,
    insets = {left = 3, right = 3, top = 6, bottom = 6}
  })
  
  local low, range, exponent = attribute.low, attribute.high-attribute.low, attribute.exponent
  
  local value_fmt = attribute.integer and "%d" or "%.2f"
  
  slider:SetScript("OnValueChanged", function (_, value)
    local value = (value/slider_max)^exponent*range+low
    
    if attribute.integer then
      value = math.floor(value+0.5)
    end
    
    if GameTooltip:IsVisible() then
      -- Update the displayed value in tooltip.
      _G[GameTooltip:GetName().."TextRight3"]:SetText(value_fmt:format(value))
    end
    
    shared.settings[setting] = value
  end)
  
  slider:SetScript("OnEnter", function ()
    local key = "SETTING_"..setting:upper()
    GameTooltip:SetOwner(slider, "ANCHOR_TOP")
    GameTooltip:SetText(L[key])
    GameTooltip:AddLine(L[key.."_TOOLTIP"], 1, 1, 1, true)
    GameTooltip:AddDoubleLine(L.SLIDER_VALUE, value_fmt:format(shared.settings[setting]), nil, nil, nil, 1, 1, 1)
    GameTooltip:Show()
  end)
  
  slider:SetScript("OnLeave", function ()
    GameTooltip:Hide()
  end)
  
  shared.onSetting(setting, function (value)
    local slider_pos = math.floor(0.5+((slider_max^exponent*(value-low))/range)^(1/exponent))
    
    slider:SetValue(slider_pos)
  end)
  
  local label = select(2, addFrame(panel, slider, "SETTING_"..setting:upper()))
  
  shared.onSettingOverride(setting, function (overridden)
    slider[overridden and "Disable" or "Enable"](slider)
    slider:SetAlpha(overridden and 0.7 or 1)
    disableText(label, overridden)
  end)
end

function create_widget.fallback(panel, setting, attribute)
  addText(panel, nil, nil, "SETTING_"..setting:upper())
end

-- The code for generating and displaying the panels themselves.

local function panelOkay(panel)
  for i, setting in ipairs(panel.settings) do
    if shared.settings_attributes[setting].requires_restart and panel.original_values[setting] ~= shared.settings[setting] then
      -- Changed a setting that requires the game to be restarted; ask the interface options panel to warn the user about this.
      InterfaceOptionsFrame.logout = true
    end
    
    panel.original_values[setting] = select(2, shared.getSetting(setting))
  end
end

local function panelRefresh(panel)
  -- This is almost identical to panelOkay, except that we don't check if the user changed a value that requires a restart.
  for i, setting in ipairs(panel.settings) do
    panel.original_values[setting] = select(2, shared.getSetting(setting))
  end
end

local function panelShow(panel)
  panel:SetScript("OnShow", nil)
  
  panel.gap = 16
  
  addText(panel, panel.name, "GameFontNormalLargeLeft", panel.name_key)
  addSpace(panel)
  addText(panel, nil, nil, panel.name_key.."_DESCRIPTION")
  addSpace(panel)
  
  for i, setting in ipairs(panel.settings) do
    local attribute = shared.settings_attributes[setting]
    local create = create_widget[attribute.type] or create_widget.fallback
    create(panel, setting, attribute)
    
    panel.original_values[setting] = select(2, shared.getSetting(setting))
  end
  
  local extra = extra_config[panel.name_key]
  
  if extra then extra(panel) end
end

local function panelCancel(panel)
  for setting, original_value in pairs(panel.original_values) do
    shared.settings[setting] = original_value
  end
end

local function panelDefaults(panel)
  for i, setting in ipairs(panel.settings) do
    -- Assigning nil resets the setting to the default.
    shared.settings[setting] = nil
  end
end

local function initPanel(panel, name_key)
  panel:Hide()
  panel.name, panel.name_key = L[name_key or ""], name_key
  panel.settings, panel.original_values = {}, {}
  panel.refresh, panel.okay, panel.cancel, panel.default = panelRefresh, panelOkay, panelCancel, panelDefaults
  panel:SetScript("OnShow", panelShow)
end


local panels = {}

shared.onLoad(function ()
  local panel = CreateFrame("Frame", nil, InterfaceOptionsFramePanelContainer)
  initPanel(panel, "PANEL_QUESTHELPER2")
  InterfaceOptions_AddCategory(panel)
  panels.qh2 = panel
  
  for setting, attribute in shared.enumerateSettings() do
    local name = setting:match("^([^_]+)_")
    
    if name then
      panel = panels[name]
      if not panel then
        panel = CreateFrame("Frame", nil, InterfaceOptionsFramePanelContainer)
        initPanel(panel, "PANEL_"..name:upper())
        panels[name] = panel
        panel.parent = panels.qh2.name
        InterfaceOptions_AddCategory(panel)
      end
    else
      panel = panels.qh2
    end
    
    table.insert(panel.settings, setting)
  end
  
  shared.onSetting("locale", function()
    for name, panel in pairs(panels) do
      if panel.name_key then
        panel.name = L[panel.name_key]
        if panel.parent then
          panel.parent = panels.qh2.name
        end
      end
    end
    
    for font_string, translation_key in pairs(localized_font_strings) do
      font_string:SetText(L[translation_key])
    end
    
    InterfaceCategoryList_Update()
    InterfaceAddOnsList_Update()
  end)
end)

-- Extra stuff

function extra_config.PANEL_TRACKER(panel)
  addSpace(panel)
  
  local db_reset = addButton(panel, "TRACKER_RECENTER", "TRACKER_RECENTER_TOOLTIP", function ()
    QuestHelper2_Tracker:ClearAllPoints()
    QuestHelper2_Tracker:SetPoint("CENTER", UIParent)
  end)
end

function extra_config.PANEL_QUEST(panel)
  addSpace(panel)
  
  local text = addText(panel, "")
  
  local function updateFilterText()
    shared.callOnce(function()
      local names = {}
      
      for id in shared.iterateActiveQuests() do
        if not shared.questFilterCheck(id) then
          local name = GetQuestLogTitle(GetQuestLogIndexByID(id))
          if name then
            table.insert(names, name)
          end
        end
      end
      
      if #names == 0 then
        text:SetText(L.PANEL_QUEST_NONE_FILTERED)
      else
        table.sort(names)
        text:SetFormattedText(L.PANEL_QUEST_FILTERED, #names, table.concat(names, ", "))
      end
    end)
  end
  
  shared.onSetting("quest_filter_tracked", updateFilterText)
  shared.onSetting("quest_filter_zone", updateFilterText)
  shared.onSetting("quest_filter_group", updateFilterText)
  shared.onSetting("quest_filter_level", updateFilterText)
  shared.onSetting("quest_filter_level_offset", updateFilterText)
end

function extra_config.PANEL_DATABASE(panel)
  addSpace(panel)
  
  local text = addText(panel, nil, nil, "")
  
  addSpace(panel)
  
  local db_reset = addButton(panel, "DATABASE_WIPE", "DATABASE_WIPE_TOOLTIP", function ()
    StaticPopupDialogs["QH2_DATABASE_WIPE"] = {
      text = L.DATABASE_WIPE_TOOLTIP,
      button1 = ACCEPT,
      button2 = CANCEL,
      OnAccept = shared.wipeDatabase,
      timeout = 0,
      whileDead = true,
      hideOnEscape = true,
      showAlert = true
    }
    
    StaticPopup_Show("QH2_DATABASE_WIPE")
  end)
  
  local function updateText()
    local quests, achievements, size, versions = shared.databaseStatistics()
    text:SetText(L.DATABASE_STATISTICS:format(size, quests, achievements, versions, "http://smariot.no-ip.org/qh2/"))
  end
  
  panel:SetScript("OnShow", updateText)
  shared.onSetting("locale", updateText)
end

function extra_config.PANEL_OVERLAY(panel)
  -- Adds a preview of the map overlay to the frame, because the map can't normally be seen at the
  -- same time as the configuration panel.
  
  -- We don't handle the map's overlay textures, so the maps will appear unexplored.
  -- All well, it was better than nothing.
  
  addSpace(panel)
  
  local overlay_preview = CreateFrame("Frame", nil, panel)
  
  local textures = {}
  
  for i=1,16 do
    textures[i] = overlay_preview:CreateTexture(nil, "BACKGROUND")
  end
  
  local function setTextures()
    local map_name, texture_name
    local level = GetCurrentMapDungeonLevel() - (DungeonUsesTerrainMap() and 1 or 0)
    
    map_name = GetMapInfo() or "World"
    
    if level > 0 then
      texture_name = "Interface\\WorldMap\\"..map_name.."\\"..map_name..level.."_"
    else
      texture_name = "Interface\\WorldMap\\"..map_name.."\\"..map_name
    end
    
    for i=1,16 do
      textures[i]:SetTexture(texture_name..i)
    end
  end
  
  local function positionTextures()
    local w, h = overlay_preview:GetWidth()*(1024/1002/4), overlay_preview:GetHeight()*(768/668/3)
    
    for y=0,2 do for x=0,3 do
      local texture = textures[y*4+x+1]
      texture:SetWidth(w)
      texture:SetHeight(h)
      texture:SetPoint("TOPLEFT", w*x, h*-y)
    end end
  end
  
  addFrame(panel, overlay_preview)
  overlay_preview:SetPoint("LEFT", panel, 48, 0)
  overlay_preview:SetPoint("BOTTOMRIGHT", panel, -48, 16)
  
  overlay_preview:RegisterEvent("WORLD_MAP_UPDATE")
  overlay_preview:SetScript("OnEvent", setTextures)
  setTextures()
  
  overlay_preview:SetScript("OnSizeChanged", positionTextures)
  positionTextures()
  
  shared.addOverlayToFrame(overlay_preview)
end

function shared.openConfig(panel_name)
  if WorldMapFrame:IsVisible() then
    ToggleFrame(WorldMapFrame)
  end
  
  InterfaceOptionsFrame_OpenToCategory(panels[panel_name] or panels.qh2)
  
  -- Try to find the button for our panel, and make sure its subcategories are displayed.
  for i, button in ipairs(InterfaceOptionsFrameAddOns.buttons) do
    if button.element == panels.qh2 then
      if button.element.collapsed then
        InterfaceOptionsListButton_ToggleSubCategories(button)
      end
      
      break
    end
  end
end

-- Slash commands

local function print(...)
  _G.print("|cff96ff70QuestHelper2|r:", ...)
end

local commands = {}

shared.onLoad(function ()
  -- Let's not process slash commands until we're fully loaded, ok?
  
  _G.SLASH_QuestHelper2_1 = "/qh2"
  _G.SLASH_QuestHelper2_2 = "/questhelper2"

  function SlashCmdList.QuestHelper2_(msg)
    local cmd, extra = msg:match("^([^%s]+)(.*)$")
    
    if not cmd then
      cmd, extra = "config", ""
    else
      cmd = cmd:lower()
    end
    
    if commands[cmd] then
      commands[cmd](extra)
    else
      local list = {}
      for command in pairs(commands) do table.insert(list, command) end
      table.sort(list)
      print(L.BAD_SLASH_COMMAND:format(cmd, table.concat(list, ", ")))
    end
  end
end)

function commands.help()
  print("I'd love to help you, but nobody has written me yet.")
end

commands.config = shared.openConfig