mission.GetLocalTimeHMS = mission.GetLocalTimeHMS or common.GetLocalTimeHMS

function GetTimestamp()
  local time = mission.GetLocalTimeHMS()
  local minutes = time.h * 60 + time.m
  local seconds = minutes * 60 + time.s
  local ms = seconds * 1000 + time.ms
  return (((time.h * 60) + time.m) * 60 + time.s) * 1000 + time.ms
end

local timestampOffset = 0
local dayInMs = 86400000
Global("cachedTimestamp", GetTimestamp())

function UpdateCachedTimestamp()
  local now = GetTimestamp()
  if now < cachedTimestamp then
    timestampOffset = timestampOffset + dayInMs
  end
  cachedTimestamp = now + timestampOffset
end

function IsIdPlayerOrPlayerPet(unitId)
  local playerId = avatar.GetId()
  return unitId == playerId or unitId == unit. GetActivePet (playerId)
end

function IsIdEnemy(unitId)
  if not unitId then
    return false
  end
  return unit.IsEnemy(unitId)
end

function IsIdFriendly(unitId)
  if not unitId then
    return false
  end
  return unit.IsFriend(unitId)
end

function IsUnitIdGroupMember(unitId, number)
  if not number then
    return true
  end
  if group.IsPlayerInGroup(avatar.GetId()) then
    local members = group.GetMembers() 
    for k, v in members do
      if k == number then
        return v.id == unitId
      end
    end
  end
  return false
end

-- This functions checks the unit 
function ConfigUnitCheck(config, unitId, casterId)  
  local playerId = avatar.GetId()
  local petId = unit. GetActivePet (playerId)
  local targetId = unit.GetTarget(playerId)
  local targetOfTargetId = targetId and unit.GetTarget(targetId) or nil
  return (not config.castedbyme or IsIdPlayerOrPlayerPet(casterId)) and
         ((config.you and (unitId == playerId)) or
         (config.yourTarget and (unitId == targetId)) or
         (config.targetoftarget and (unitId == targetOfTargetId)) or
         (config.pet and (unitId == petId)) or
         config.anyunit) and
         (not config.ifgroupmembernumber or (config.ifgroupmembernumber and IsUnitIdGroupMember(unitId, config.groupmembernumber)))
end

function ConfigExtendedConditionsCheck(data, unitId) 
  return (unitId == nil or object.IsExist(unitId))
		 and
	     ((data.infight and isPlayerInCombat) or (data.notinfight and not isPlayerInCombat) or (not data.notinfight and not data.infight))
         and
         ((data.solo and isPlayerSolo) or (data.inraid and isPlayerInRaid) or (data.ingroup and isPlayerInGroup) or (not data.solo and not data.ingroup and not data.inraid))
         and
         ((data.mounted and isPlayerMounted) or (data.notmounted and not isPlayerMounted) or (not data.mounted and not data.notmounted))
         and
         (not unitId or data.spellCD or data.itemCD or (data.hostile and IsIdEnemy(unitId)) or (data.friendly and unitId and IsIdFriendly(unitId)) or (not data.friendly and not data.hostile))	 
         and
         (data.spellCD or data.itemCD or (not data.ifgroupmembernumber) or (data.ifgroupmembernumber and IsUnitIdGroupMember(unitId, data.groupmembernumber)))
end

function ConfigTimeCheck(reference)
  local config = reference.config
  
  if config.timermin or config.timermax then
    local timerValue = (config.timerremaining and reference.timerremaining) or
                     (config.timerelapsed and reference.timerelapsed)
    return (not config.timermin or (config.timerminvalue <= timerValue)) and
           (not config.timermax or (config.timermaxvalue >= timerValue))
  else
    return reference.timerremaining > 0 or not reference.showDuration
  end
end

function ConfigStacksizeAndTimeCheck(reference)
  local config = reference.config
  local timeCheck = ConfigTimeCheck(reference)
  return (not config.stackmin or (config.stackminvalue <= reference.stackSize))
            and
            (not config.stackmax or (config.stackmaxvalue >= reference.stackSize))
			and
			timeCheck
end
function IsReferenceVisible(reference, config)
  if config.buff or config.debuff then
    return reference.isVisible
  elseif config.spellCD then
    return reference.isVisible and reference.spellCDFinished ~= globalCooldownFinishedTimeStamp
  elseif config.itemCD then
    return reference.isVisible and reference.itemCDFinished ~= globalCooldownFinishedTimeStamp
  end
end
function UpdateActiveReference(config)
  if not config then
    return nil
  end
  if not config.references then
    config.activeReference = nil
	return nil
  end
  local result = nil
  local timer
  if config.timerremaining then
    timer = (config.hide and 0) or (config.timermin and config.timerminvalue) or 0
    for k, v in config.references do   
      if k.isEnabled and (timer < k.timerremaining or not k.showDuration) and IsReferenceVisible(k, config) then        
        if not k.showDuration then
		  result = k
		  break
		else
		  timer = k.timerremaining
		  result = k
		end
      end
    end
  elseif config.timerelapsed then
    timer = config.timermax and config.timermaxvalue or 2000000000
    for k, v in config.references do
      if k.isEnabled and (timer > k.timerelapsed or not k.showDuration) and IsReferenceVisible(k, config) then        
        if not k.showDuration then
		  result = k
		  break
		else
		  timer = k.timerelapsed
		  result = k
		end
      end
    end
  end
  
  config.activeReference = result
  return result
end
function AuraDataCooldown(auraData)
  local activeReference = auraData.activeReference  
  if not auraData or not activeReference then
    return nil
  end

  return (auraData.timerremaining and (activeReference.showDuration and activeReference.timerremaining or nil)) or 
		 (auraData.timerelapsed and (activeReference.showDuration and activeReference.timerelapsed or nil))  
end
function AuraDataProgressRange(config)
  local activeReference = config and config.activeReference or nil
  if not activeReference or not activeReference.showDuration then
    return nil
  end
  if config.show then
    if config.timermin and config.timermax then
	  return config.timerminvalue, config.timermaxvalue
    elseif config.timermin then
	  return config.timerminvalue, config.timerremaining and activeReference.duration or activeReference.timerelapsed + activeReference.timerremaining
	elseif config.timermax then
	  return 0, config.timermaxvalue
	else
	  return 0, config.timerremaining and activeReference.duration or activeReference.timerelapsed + activeReference.timerremaining
	end
  else
    if config.timermin and config.timermax then	  
	  return config.timerminvalue, config.timermaxvalue
    elseif config.timermin then
	  return 0, config.timerminvalue
	elseif config.timermax then
	  return config.timermaxvalue, config.timerremaining and activeReference.duration or activeReference.timerelapsed + activeReference.timerremaining
	else
	  return 0, config.timerremaining and activeReference.duration or activeReference.timerelapsed + activeReference.timerremaining
	end
  end
end

function AuraDataActive(auraData)
  if not auraData then
    -- if you change that, then it will not work correctly.
	-- auraData can be nil, because GetUnderlyingAuraData result nil, 
	-- that is returned when no recursive setting available.
    return true
  end
  local activeReference = auraData.activeReference
  if not activeReference then
    return auraData.hide
  end
  if activeReference.isActive and activeReference.isVisible then	  	  
    return auraData.show
  end
  return auraData.hide
end

local auraDataVisibleRecursiveProtect = { }
function CleanAuraDataVisibleRecursiveProtect()
  for k, _ in auraDataVisibleRecursiveProtect do
    auraDataVisibleRecursiveProtect[k] = nil
  end
end
function AddToAuraDataVisibleRecursiveProtect(auraData)
  auraDataVisibleRecursiveProtect[auraData] = true
end
function RemoveFromAuraDataVisibleRecursiveProtect(auraData)
  auraDataVisibleRecursiveProtect[auraData] = nil
end
function AuraDataVisible(auraData)
  if not auraData then
    -- if you change that, then it will not work correctly.
	-- auraData can be nil, because GetUnderlyingAuraData result nil, 
	-- that is returned when no recursive setting available.
    return true 
  end

  -- protect for endless recursion
  AddToAuraDataVisibleRecursiveProtect(auraData)
  local underlyingAuraData = GetUnderlyingAuraData(auraData)
  local result = nil 
  
  local activeReference = auraData.activeReference
  if activeReference then
    if auraData.buff or auraData.debuff then
	  result = activeReference.isVisible      
    elseif auraData.spellCD then
      result = activeReference.isVisible and activeReference.spellCDFinished ~= globalCooldownFinishedTimeStamp
    elseif auraData.itemCD then
      result = activeReference.isVisible and activeReference.itemCDFinished ~= globalCooldownFinishedTimeStamp
    end
  end
  if result == nil then
    if auraData.references then
	  for k, v in auraData.references do
	    if k.isEnabled then
	      result = false
		  break;
		end
	  end
	end
	if result == nil then
      result = ConfigExtendedConditionsCheck(auraData)
	end
  end
  
  result = result and (not underlyingAuraData or AuraDataVisible(underlyingAuraData)) and (not underlyingAuraData or AuraDataActive(underlyingAuraData))
  RemoveFromAuraDataVisibleRecursiveProtect(auraData)
  return result
end


function GetUnderlyingAuraData(auraDataItem)
  if auraDataItem and auraDataItem.useotherrule and auraDataItem.otherrule and common.IsWString(auraDataItem.otherrule) then
    if auraDataItem.otherruleInstance then
	  return auraDataItem.otherruleInstance
	end
	local otherrule = auraDataItem.otherrule
    for k, v in auraData do
      if common.CompareWString(v.entryname, otherrule) == 0 then
	    auraDataItem.otherruleInstance = v
		return v 
	  end
	end
  end
  return nil
end

function AuraDataTexture(auraData)
  if not auraData then
    return nil
  end
  if auraData.buff or auraData.debuff then
    if not auraData.references then
      return auraData.defaultTexture
    end  
    for k, v in auraData.references do
      if k.texture then
	    if not auraData.defaultTexture then
		  auraData.defaultTexture = k.texture
		  if common.GetTexturePath then -- content of the if is part of the iconDb-Feature => need common.GetTexturePath for this
		    Save()
		  end
		end
        return k.texture
      end
    end
	return auraData.defaultTexture
  elseif auraData.spellCD then
    local texture = auraData.defaultTexture
    texture = texture or GetSpellTexture(auraData.name)
    auraData.defaultTexture = texture
    return texture
  elseif auraData.itemCD then
    local texture = auraData.defaultTexture
	texture = texture or GetItemTexture(auraData.name)
	auraData.defaultTexture = texture
	return texture
  end
  return nil
end