-- Copyright © 2018 Daniel Pittman <daniel@rimspace.net>
-- See LICENSE for more details.

-- addon core object.
local ADDON_NAME = "SlippyCheezeReadItOnce"
local DISPLAY_NAME = "Read It Once"

local DOUBLE_TAP_TIME = 1000
local previousBook = {id=nil, time=0}

-- saved var: which books have been seen.
local seen

local unpack = table.unpack or unpack
local insert = table.insert
local function dmsg(msg, ...)
   local args = {}
   for n=1, select('#', ...) do
      insert(args, tostring(select(n, ...)))
   end

   d(zo_strformat(msg, unpack(args)))
end

-- return bool, have we seen this before.  never called before saved variables
-- are loaded and initialized.
local function HaveSeenBookBefore(id, title, body)
   if type(id) ~= "number" then
      dmsg("ReadItOnce: id is <<1>> (<<2>>)", type(id), id)
      return false
   end

   -- ensure that we index by string, not number, in the table.
   local id = tostring(id)
   local bodyHash = HashString(body)

   local record = seen[id]
   if record then
      -- probably have seen it before, but check for changes
      if record.id ~= id then
         d("ReadItOnce: book id changed from <<1>> to <<2>>", record.id, id)
      end
      if record.title ~= title then
         d("ReadItOnce: book title changed from '<<1>>' to '<<2>>'", record.title, title)
      end
      if record.bodyHash ~= bodyHash then
         d("ReadItOnce: book body changed")
      end

      -- don't show.
      return true
   end

   -- have not seen, record it, and return that fact
   seen[id] = {id=id, title=title, bodyHash=bodyHash}
   return false
end

-- Sadly, we have to override the original method, which is a local anonymous
-- function, and which we have apparently no access to in order to hook nicely.
--
-- The bulk of this is a direct copy-paste from the lore reader, as of USOUI
-- 100023
--
-- The HaveSeenBook logic is my addition.
local function OnShowBookOverride(eventCode, title, body, medium, showTitle, bookId)
   -- by default, only block books when we are in the default in-game
   -- interaction mode, so that inventory, lore journal, etc, activations do
   -- not get blocked.
   local force_show = not SCENE_MANAGER:IsShowingBaseScene()

   -- handle the case of double-activation to bypass the restriction.
   local now = GetGameTimeMilliseconds()
   if previousBook.id == bookId then
      if (now - previousBook.time) < DOUBLE_TAP_TIME then
         force_show = true
      end
   else
      previousBook.id = bookId
   end
   -- dmsg("force_show <<1>> now <<2>> prev.time <<3>> deltaT <<4>> prev.id <<5>> id <<6>>",
   --      force_show, now, previousBook.time, now - previousBook.time, previousBook.id, id)
   previousBook.time = now

   -- implement the block, if appropriate.
   if HaveSeenBookBefore(bookId, title, body) and not force_show then
      PlaySound(SOUNDS.NEGATIVE_CLICK)

      -- local msg = zo_strformat("<<1>>: You have already read \"<<2>>\"", DISPLAY_NAME, title)
      local msg = zo_strformat("You have already read \"<<1>>\"", title)

      -- ZO_AlertNoSuppression(UI_ALERT_CATEGORY_ALERT, nil, )

      local params = CENTER_SCREEN_ANNOUNCE:CreateMessageParams(CSA_CATEGORY_SMALL_TEXT, nil)
      params:SetText(msg)
      params:SetCSAType(CENTER_SCREEN_ANNOUNCE_TYPE_LORE_BOOK_LEARNED)
      params:SetLifespanMS(850)
      CENTER_SCREEN_ANNOUNCE:AddMessageWithParams(params)

      EndInteraction(INTERACTION_BOOK)
      return
   end

   -- meh, this is copied from the local function in the ZOS code. :(
   if LORE_READER:Show(title, body, medium, showTitle) then
      PlaySound(LORE_READER.OpenSound)
   else
      EndInteraction(INTERACTION_BOOK)
   end
end

local function OnAddonLoaded(_, name)
   if name ~= ADDON_NAME then return end
   EVENT_MANAGER:UnregisterForEvent(ADDON_NAME, EVENT_ADD_ON_LOADED)

   -- if the second argument, the version, changes then the data is wiped and
   -- replaced with the defaults.
   seen = ZO_SavedVars:NewAccountWide("SlippyCheezeReadItOnceData", 1)

   -- replace the original event handler with ours; sadly, we don't have
   -- access to the original implementation to do anything nicer. :/
   LORE_READER.control:UnregisterForEvent(EVENT_SHOW_BOOK)
   LORE_READER.control:RegisterForEvent(EVENT_SHOW_BOOK, OnShowBookOverride)
end

-- bootstrapping
EVENT_MANAGER:RegisterForEvent(ADDON_NAME, EVENT_ADD_ON_LOADED, OnAddonLoaded)