local registeredItemCDReferences = { }

function ReadFullItemInfo(info)
  local producer = info.producer
  return info.name, info.icon
  -- name, start time, end time, texture, stack size, stack limit
end

function GetItemCDConfiguration(itemName)
  local results = { }
  for k, v in auraData do
    if v.itemCD and
       common.CompareWStringEx(v.name, itemName) == 0 then
      table.insert(results, v)
    end
  end
  return results
end

function RegisterItemCDReferences(references)
  for reference, _ in references do
    local registeredReferencesForItemCDId = registeredItemCDReferences[reference.spellId]
    if not registeredReferencesForItemCDId then
      registeredReferencesForItemCDId = { }
      registeredItemCDReferences[reference.itemId] = registeredReferencesForItemCDId
    end
    registeredReferencesForItemCDId[reference] = true
    reference.config.references[reference] = true
  end
end

function UnregisterItemCDReference(reference)
  reference.config.references[reference] = nil
  local references = registeredItemCDReferences[reference.spellId]
  if references then
    references[reference] = nil
    for k, v in references do
      return
    end
    registeredItemCDReferences[reference.itemId] = nil
  end
end

function CreateItemCDReferences(itemId, cooldownInfo)
  itemId = itemId or avatar.GetInventoryItemId(cooldownInfo.slot)
  local itemInfo = itemId and avatar.GetItemInfo(itemId)
  if not itemInfo then
    return nil
  end
  local itemCDStart, itemCDFinished = cachedTimestamp - cooldownInfo.duration + cooldownInfo.remaining, cooldownInfo.remaining + cachedTimestamp
  local itemName, texture, showDuration = ReadFullItemInfo(itemInfo)
  local results = { }
  local resultsAvailable = false
  for k, config in GetItemCDConfiguration(itemName) do
    if config == nil then
      return nil
    end
    config.references = config.references or {}
    local result = { }  
    result.itemName = itemName
    result.itemId = itemId
    result.config = config
    result.itemCDStart = itemCDStart
	result.duration = cooldownInfo.duration
    result.itemCDFinished = itemCDFinished
    result.texture = texture
    result.showDuration = true
	result.isEnabled = true
    result.CanDelete = function(self)
      local config = self.config
      return (config.timerremaining and config.timermin and self.timerremaining < config.timerminvalue) or
            (config.timerelapsed and config.timermax and self.timerelapsed > config.timermaxvalue) or
            (not config.timermin and not config.timermax and self.timerremaining < 0)
    end  
	result.Unregister = UnregisterItemCDReference
    results[result] = true
    resultsAvailable = true
  end
  RegisterItemCDReferences(results)
  for reference, _ in results do
    UpdateItemCDReference(nil, nil, reference)
  end
  return resultsAvailable and results or nil
end

function RefreshItemCDReference(reference, itemId, cooldownInfo)
  if not reference then
    local references = (itemId and registeredItemCDReferences[itemId]) or nil
    if references then
      for creference, _ in references do
        RefreshItemCDReference(creference, creference.itemId, cooldownInfo)
      end
      return true
    end
    local result = CreateItemCDReferences(itemId, cooldownInfo)
    if result then
      for creference, _ in result do
        RefreshItemCDReference(creference, creference.spellId, cooldownInfo)
      end
      return true
    end
  else
    reference.itemCDFinished = cooldownInfo.remaining + cachedTimestamp
	reference.duration = cooldownInfo.duration
  end
  return reference
end

function UpdateItemCDReference(itemId, cooldownInfo, reference)
  if not reference then
    local references = (itemId and registeredItemCDReferences[itemId]) or nil
	if references then
	  for creference, _ in references do
	    UpdateItemCDReference(creference.itemId, nil, creference)
	  end
	elseif cooldownInfo then
	  -- i didn't need after this to call UpdateItemCDReference,
	  -- because it is done in CreateItemCDReferences
	  CreateItemCDReferences(itemId, cooldownInfo)
	end
  else
    local config = reference.config
	reference.timerremaining = reference.itemCDFinished - cachedTimestamp
    reference.timerelapsed = cachedTimestamp - reference.itemCDStart	
    reference.isActive = ConfigTimeCheck(reference)
    reference.isVisible = ConfigExtendedConditionsCheck(config)
  end
  
end

function UpdateItemCDReferences()
  for k, v in registeredItemCDReferences do
    for reference, _ in v do
      UpdateItemCDReference(nil, nil, reference)
	end
  end
end

function RemoveItemCDReference(itemId, reference)
  if not reference then
    local references = (itemId and registeredItemCDReferences[itemId]) or nil
	if references then
	  for creference, _ in references do
	    RemoveItemCDReference(creference.itemId, creference)
	  end
	end
  else
    UnregisterItemCDReference(reference)
  end
end

function CollectItemCDReferences()
  for k, v in registeredItemCDReferences do
    for reference, _ in v do
	  if reference:CanDelete() then
		UnregisterItemCDReference(reference)
	  end
    end
  end
end