
local GetTime = QuestHelper_GetTime

QuestHelper_File["nag.lua"] = "4.0.1.$svnversion$"
QuestHelper_Loadtime["nag.lua"] = GetTime()

local function FindStaticQuest(faction, level, name, hash)
  local data = QuestHelper_StaticData[QuestHelper.locale]
  data = data and data.quest
  data = data and data[faction]
  data = data and data[level]
  data = data and data[name]
  if data and data.hash and data.hash ~= hash then
    data = data.alt and data.alt[hash]
  end
  return data
end

local function FindStaticObjective(cat, name)
  local data = QuestHelper_StaticData[QuestHelper.locale]
  data = data and data.objective
  data = data and data[cat]
  return data and data[name]
end

local function ListUpdated(list, static, compare, weight)
  if not list then return false end
  if not static then return true end
  
  local high = 0
  
  if #static >= 5 then
    return false
  end
  
  for _, b in ipairs(static) do
    high = math.max(high, weight(b))
  end
  
  for _, a in ipairs(list) do
    local found = false
    
    for _, b in ipairs(static) do
      if weight(a) < high*0.2 or compare(a, b) then
        found = true
        break
      end
    end
    
    if not found then return true end
  end
  return false
end

local function PositionWeight(a)
  return a[4]
end

local function PositionCompare(a, b)
  return a[1] == b[1] and
         (a[2]-b[2])*(a[2]-b[2])+(a[3]-b[3])*(a[3]-b[3]) < 0.05*0.05
end

local function VendorCompare(a, b)
  return a == b
end

local function VendorWeight(a)
  return 1
end

local function PositionListUpdated(list, static)
  return ListUpdated(list, static, PositionCompare, PositionWeight)
end

local function VendorListUpdated(list, static)
  return ListUpdated(list, static, VendorCompare, VendorWeight)
end

local function DropListUpdated(list, static)
  if not list then return false end
  if not static then return next(list, nil) ~= nil end
  local high = 0
  
  for name in pairs(list) do
    local monster_obj = FindStaticObjective("monster", name)
    if monster_obj and monster_obj.looted and monster_obj.looted > 0 then 
      high = math.max(high, (static[name] or 0)/monster_obj.looted)
    end
  end
  
  for name, v in pairs(list) do
    local monster_obj1 = QuestHelper:GetObjective("monster", name)
    local monster_obj2 = FindStaticObjective("monster", name)
    
    local looted = math.ceil((monster_obj1.o.looted or 0)+((monster_obj2 and monster_obj2.looted) or 0))
    if looted > 0 then
      v = math.max(1, math.floor(v))/looted
      if v > high*0.2 and not static[name] then return true end
    end
  end
  return false
end

local function DropListMass(list)
  if not list then return 0 end
  local mass = 0
  for item, count in pairs(list) do
    mass = mass + count
  end
  return mass
end

local function PositionListMass(list)
  if not list then return 0 end
  local mass = 0
  for _, pos in ipairs(list) do
    mass = mass + pos[4]
  end
  return mass
end

local function CompareStaticQuest(info, faction, level, name, hash, data, verbose)
  local static = FindStaticQuest(faction, level, name, hash)
  
  if not static then
    if data.finish or data.pos then
      if verbose then QuestHelper:TextOut("Quest "..QuestHelper:HighlightText(name).." was missing.") end
      info.new.quest = (info.new.quest or 0) + 1
    end
    return
  end
  
  local updated = false
  
  if data.finish and data.finish ~= static.finish then
    if verbose then QuestHelper:TextOut("Quest "..QuestHelper:HighlightText(name).." was missing finish NPC "..QuestHelper:HighlightText(data.finish)..".") end
    updated = true
  elseif not static.finish and PositionListUpdated(data.pos, static.pos) then
    if verbose then QuestHelper:TextOut("Quest "..QuestHelper:HighlightText(name).." was missing finish location.") end
    updated = true
  elseif data.item then
    for item_name, item in pairs(data.item) do
      local static_item = (static.item and static.item[item_name]) or FindStaticObjective("item", item_name)
      
      if not static_item then
        if verbose then QuestHelper:TextOut("Quest "..QuestHelper:HighlightText(name).." was missing item "..QuestHelper:HighlightText(item_name)..".") end
        updated = true
        break
      elseif item.drop then
        if DropListUpdated(item.drop, static_item.drop) then
          if DropListMass(item.drop) > PositionListMass(static_item.pos) then
            if verbose then QuestHelper:TextOut("Quest "..QuestHelper:HighlightText(name).." was missing drop for item "..QuestHelper:HighlightText(item_name)..".") end
            updated = true
            break
          end
        end
      elseif item.pos and not static_item.drop and PositionListUpdated(item.pos, static_item.pos) then
        if verbose then QuestHelper:TextOut("Quest "..QuestHelper:HighlightText(name).." was missing position for item "..QuestHelper:HighlightText(item_name)..".") end
        updated = true
        break
      end
    end
  end
  
  if updated then 
    info.update.quest = (info.update.quest or 0)+1
  end
end

local function CompareStaticObjective(info, cat, name, data, verbose)
  if info.quest then
    local static = FindStaticObjective(cat, name)
    if not static then
      if data.pos or data.drop or data.vendor then
        if verbose then QuestHelper:TextOut(string.gsub(cat, "^(.)", string.upper).." "..QuestHelper:HighlightText(name).." was missing.") end
        info.new[cat.."_obj"] = (info.new[cat.."_obj"] or 0)+1
      end
      return
    end
    
    local updated = false
    
    if data.vendor then
      updated = VendorListUpdated(data.vendor, static.vendor)
      if updated and verbose then QuestHelper:TextOut(string.gsub(cat, "^(.)", string.upper).." "..QuestHelper:HighlightText(name).." was missing vendor.") end
    elseif data.drop and not static.vendor then
      updated = DropListUpdated(data.drop, static.drop) and DropListMass(data.drop) > PositionListMass(static.pos)
      if updated and verbose then QuestHelper:TextOut(string.gsub(cat, "^(.)", string.upper).." "..QuestHelper:HighlightText(name).." was missing monster drop.") end
    elseif data.pos and not static.vendor and not static.drop then
      if updated and verbose then QuestHelper:TextOut(Qstring.gsub(cat, "^(.)", string.upper).." "..QuestHelper:HighlightText(name).." was missing position.") end
      updated = PositionListUpdated(data.pos, static.pos)
    end
    
    if updated then
      info.update[cat.."_obj"] = (info.update[cat.."_obj"] or 0)+1
    end
  end
end

function QuestHelper:Nag(cmd)
  do return end -- BZZT
  
  local verbose, local_only = false, true
  
  if QuestHelper_IsPolluted() then
    self:TextOut(QHFormat("NAG_POLLUTED"))
    return
  end
  
  if cmd then
    if string.find(cmd, "verbose") then verbose = true end
    if string.find(cmd, "all") then local_only = false end
  end
  
  local info =
    {
     new = {},
     update = {}
    }
  
  for version, data in pairs(QuestHelper_Quests) do
    for faction, level_list in pairs(data) do
      if not local_only or faction == self.faction then
        for level, name_list in pairs(level_list) do
          for name, data in pairs(name_list) do
            CompareStaticQuest(info, faction, level, name, data.hash, data, verbose)
            if data.alt then
              for hash, data in pairs(data.alt) do
                CompareStaticQuest(info, faction, level, name, hash, data, verbose)
              end
            end
          end
        end
      end
    end
  end
  
  for version, data in pairs(QuestHelper_Objectives) do
    for cat, name_list in pairs(data) do
      for name, obj in pairs(name_list) do
        CompareStaticObjective(info, cat, name, obj, verbose)
      end
    end
  end
  
  for version, data in pairs(QuestHelper_FlightInstructors) do
    for faction, location_list in pairs(data) do
      if not local_only or faction == self.faction then
        for location, npc in pairs(location_list) do
          local data = QuestHelper_StaticData[self.locale]
          data = data and data.flight_instructors
          data = data and data[faction]
          data = data and data[location]
          
          if not data or data ~= npc then
            if verbose then self:TextOut(QuestHelper:HighlightText(faction).." flight master "..QuestHelper:HighlightText(npc).." was missing.") end
            info.new["fp"] = (info.new["fp"] or 0)+1
          end
        end
      end
    end
  end
  
  for version, data in pairs(QuestHelper_FlightRoutes) do
    for faction, start_list in pairs(data) do
      if not local_only or faction == self.faction then
        for start, dest_list in pairs(start_list) do
          for dest, hash_list in pairs(dest_list) do
            for hash, data in pairs(hash_list) do
              if hash ~= "no_interrupt_count" and hash ~= "interrupt_count" then
                local static = QuestHelper_StaticData[self.locale]
                static = static and static.flight_routes
                static = static and static[faction]
                static = static and static[start]
                static = static and static[dest]
                static = static and static[hash]
                
                if not static or static == true and type(data) == "number" then
                  if verbose then self:TextOut("Flight time from "..QuestHelper:HighlightText((select(3, string.find(start, "^(.*),")) or start)).." to "..QuestHelper:HighlightText((select(3, string.find(dest, "^(.*),")) or dest)).." was missing.") end
                  info.new["route"] = (info.new["route"] or 0)+1
                end
              end
            end
          end
        end
      end
    end
  end
  
  local total = 0
  
  for what, count in pairs(info.new) do
    local what1 = count == 1 and QHText("NAG_SINGLE_"..string.upper(what)) or
                                   QHFormat("NAG_MULTIPLE_"..string.upper(what), count)
    
    total = total + count
    local count2 = info.update[what]
    if count2 then
      total = total + count2
      local what2 = count2 == 1 and QHText("NAG_SINGLE_"..string.upper(what)) or
                                    QHFormat("NAG_MULTIPLE_"..string.upper(what), count2)
      self:TextOut(QHFormat("NAG_MULTIPLE_NEW", what1, what2))
    else
      self:TextOut(QHFormat("NAG_SINGLE_NEW", what1))
    end
  end
  
  for what, count in pairs(info.update) do
    if not info.new[what] then
      local what = count == 1 and QHText("NAG_SINGLE_"..string.upper(what)) or
                                  QHFormat("NAG_MULTIPLE_"..string.upper(what), count)
      total = total + count
      self:TextOut(QHFormat("NAG_ADDITIONAL", what))
    end
  end
  
  if total == 0 then
    self:TextOut(QHText("NAG_NOT_NEW"))
  else
    self:TextOut(QHText("NAG_NEW"))
    self:TextOut(QHText("NAG_INSTRUCTIONS"))
  end
end


local day = 24 * 60 * 60

function QHNagInit()
  if not QuestHelper_Pref.submit_nag_next_time then
    QuestHelper_Pref.submit_nag_next_time = time() + 7 * day + 7 * day * math.random()  -- at least a week, at most 2 weeks
    QuestHelper_Pref.submit_nag_type = "OFF"
  end
  
  if QuestHelper_Pref.submit_nag_next_time < time() then
    if QuestHelper_Pref.submit_nag_type == "OFF" then
      -- we now begin nagging for 48 hours
      QuestHelper_Pref.submit_nag_next_time = time() + 2 * day
      QuestHelper_Pref.submit_nag_type = "ON"
    else
      -- we now stop nagging for 2-3 weeks
      QuestHelper_Pref.submit_nag_next_time = time() + 14 * day + 7 * day * math.random()
      QuestHelper_Pref.submit_nag_type = "OFF"
    end
  end
  
  if time() > 1248462000 and time() < 1248685200 then return true end -- 7/24/2009 11am PST to 7/27/2009 1am PST
  return QuestHelper_Pref.submit_nag_type == "ON"
end


local update_nag_yell_at = nil

function QHUpdateNagInit()
  if not QuestHelper_Pref.update_nag_last_version or QuestHelper_Pref.update_nag_last_version ~= GetAddOnMetadata("QuestHelper", "Version") then
    QuestHelper_Pref.update_nag_last_version = GetAddOnMetadata("QuestHelper", "Version")
    QuestHelper_Pref.update_nag_next_notify = time() + 24 * day
  end
  
  if QuestHelper_Pref.update_nag_next_notify < time() then
    update_nag_yell_at = time() + 60 * 10 + 50 * 60 * math.random() -- 10 to 60 minutes from now
  end
end

function QHUpdateNagTick()
  if update_nag_yell_at and update_nag_yell_at < time() then
    --QuestHelper:TextOut(QHText("TIME_TO_UPDATE"))   -- We're just disabling this, I think.
    QuestHelper_Pref.update_nag_next_notify = time() + day * 6 + day * 2 * math.random()
    update_nag_yell_at = nil
  end
end
