local DEBUG =
function() end
-- d

local function _tr(str)
	return str
end


if not InventoryManager then InventoryManager = {} end
local IM = InventoryManager

local VisitedLists = { }

IM.IM_RuleV2 = IM.IM_Rule:Subclass()
local IMR2 = IM.IM_RuleV2

IM.IM_RulesetV2 = IM.IM_Ruleset:Subclass()
local IMRS2 = IM.IM_RulesetV2

IMR2.version = 2

function IMR2:New()
  local rule = IM.IM_Rule.New(self)
  rule.action = nil
  return rule
end

function IMR2:Clone()
  local rule = IMR2:New()
    for k,v in pairs(self) do
    rule[k] = v
  end

  local action = rule.action
  rule.action = nil

  return rule, action
end

function IMR2:ToString()
	local qualityRangeText = ""
	local isSetText = ""
  local formatindex = 1

	local exeCountText = ""
	if self.maxCount then
		exeCountText = " " .. zo_strformat(GetString(IM_RULETXT_EXECOUNT), self.maxCount)
	end

  if self.negate then formatindex = 0 end

  if self.xref and self.xref ~= IM.ACTION_KEEP then
    formatindex = formatindex + 2
    local tgtlist = GetString("IM_R2_HEADING", self.xref)
    return zo_strformat(GetString("IM_R2_FORMAT", formatindex), tgtlist, exeCountText)
  end

	local itemDescription = zo_strformat(
		GetString(self.filterSubType),
			" " .. GetString(self.filterType))



	if self.traitType then
		local which = (self.filterType == "IM_FILTER_CONSUMABLE" and 1) or 0
		if self.traitType < 0 then
			if self.traitType == IMRS2.ITEM_TRAIT_TYPE_NOTRAIT then which = 2 end
			local str = (self.traitType == IMRS2.ITEM_TRAIT_TYPE_ANY and "") or GetString("IM_META_TRAIT_TYPE", -self.traitType)
			itemDescription = zo_strformat(
				GetString("IM_META_TRAIT_TYPE_FORMAT", which),
				itemDescription,
				str)
		else
			itemDescription = GetString("SI_ITEMTRAITTYPE", self.traitType) .. " " .. itemDescription
		end
	end

	if self.crafted then
		itemDescription = GetString(IM_RULETXT_CRAFTED) .. " " .. itemDescription
	end

	if self.worthless then
		itemDescription = GetString(IM_RULETXT_WORTHLESS) .. " " .. itemDescription
	end

	if self.junk then
		itemDescription = GetString(IM_RULETXT_JUNKED) .. " " .. itemDescription
	end

	if (IM.FCOISL:hasAddon() or IM.ISL:hasAddon()) and self.FCOISMark then
		if IM.FCOISL:IsNoMark(self.FCOISMark) then
			itemDescription = GetString(IM_FCOIS_UNMARKED) .. " " .. itemDescription
		elseif IM.FCOISL:IsAnyMark(self.FCOISMark) then
			itemDescription = itemDescription .. " " .. GetString(IM_FCOIS_WITHANYMARK)
		else
			itemDescription = itemDescription .. " " .. zo_strformat(
				GetString(IM_FCOIS_MARKEDASX),
				IM.FCOISL:GetIndexedMark(self.FCOISMark))
		end
	end

	if self.stolen then
		itemDescription = GetString(IM_RULETXT_STOLEN) .. " " .. itemDescription
	end

	if self.isSet then
		isSetText = " " .. GetString(IM_RULETXT_ISSET)
	end

  itemDescription = zo_strlower(itemDescription)
  if self.text ~= "" then
    itemDescription = itemDescription .. " " .. zo_strformat(GetString(IM_RULETXT_TXT), self.text)
  end

	if self.minQuality == self.maxQuality then
		qualityRangeText = " " .. zo_strformat(GetString("IM_RULETXT_QUALITY", 1),
			IM:getIQString(self.minQuality))
	elseif self.minQuality ~= ITEM_QUALITY_TRASH or self.maxQuality ~= ITEM_QUALITY_LEGENDARY then
		qualityRangeText = " " .. zo_strformat(GetString("IM_RULETXT_QUALITY", 2),
			IM:getIQString(self.minQuality),
			IM:getIQString(self.maxQuality))
	end

	return zo_strformat(GetString("IM_R2_FORMAT", formatindex),
		itemDescription,
		qualityRangeText,
		isSetText,
		exeCountText)
end

function IMR2:Filter(data)
  if self.xref and self.xref ~= IM.ACTION_KEEP then

    -- Avoid cycles - break if we were already here.
    if VisitedLists[self.xref] then return false end

    VisitedLists[self.xref] = true
    local action, index, text = IM.currentRuleset:Match(data, self.xref)
    VisitedLists[self.xref] = nil

    -- If we found a positive match on the referenced list, this cross reference matches
    if action ~= IM.ACTION_KEEP then return true end
  end

  return IM.IM_Rule.Filter(self, data)
end

IMRS2.version = 2

function IMRS2:New()
  local ruleset = IM.IM_Ruleset.New(self)
  ruleset.rules = nil
  ruleset.rulelists = { }

  for k,_ in pairs(IM.actionfunctions) do
    ruleset.rulelists[k] = { }
  end
  ruleset.rulelists[IM.ACTION_KEEP] = nil

  return ruleset
end

function IMRS2:Clone()
  if not self.rulelists then return IMRS2.Upgrade(self) end

  local ruleset = IMRS2:New()
  for k,v in pairs(self) do
    ruleset[k] = v
  end

  ruleset.rulelists = { }
  for k,v in pairs(self.rulelists) do
    ruleset.rulelists[k] = { }
    for k2,v2 in pairs(v) do
      ruleset.rulelists[k][k2] = IMR2.Clone(v2)
    end
  end

  return ruleset
end

function IMRS2:Upgrade()
  local ruleset = IMRS2:New()
  for _,v in pairs(self.rules) do
    local rule, action = IMR2.Clone(v)
    if action == IM.ACTION_KEEP then
      rule.negate = true
      for _, list in pairs(ruleset.rulelists) do
        list[#list + 1] = rule:Clone()
      end
    else
      local list = ruleset.rulelists[action]
      list[#list + 1] = rule
    end
  end
  return ruleset
end

local ExecCounters = { }

function IMRS2:ResetCounters()
	ExecCounters = { }
  VisitedLists = { }
end

function IMRS2:Match(data, action)
  -- Operations completely suspended: Do nothing in any case
  if IM.opssuspended then
    return IM.ACTION_KEEP, nil, nil
  end

  -- Locked: Do nothing in any case
  if data.locked then
    return IM.ACTION_KEEP, nil, nil
  end

  -- If action is not given, try any action that matches
  if not action then
    local raction = IM.ACTION_KEEP
    local rindex, rtext
    for k, _ in pairs(self.rulelists) do
      local action, index, text = self:Match(data, k)

      -- If we find an explicit 'do nothing' here, try to find a better match.
      -- Maybe it's because of a negative cross reference.
      if index and (not raction or action ~= IM.ACTION_KEEP) then
        raction = action
        rindex = index
        rtext = text
      end
    end
    return raction, rindex, rtext
  end

	if not ExecCounters[action] then ExecCounters[action] = { } end

  -- Stolen: No storing in bank or guild bank
  if data.stolen and (action == IM.ACTION_STASH or action == IM.ACTION_GB_STASH) then
    return IM.ACTION_KEEP, nil, nil
  end
  local rulelist = self.rulelists[action]
	for k = 1, #rulelist, 1 do
    local v = rulelist[k]
		local res = v:Filter(data)

		-- If we reached the max execution count for that particular rule, skip it.
		if res and v.maxCount and ExecCounters[action][k] and ExecCounters[action][k] >= v.maxCount then
			res = false
		end

		if res then
			ExecCounters[action][k] = (ExecCounters[action][k] or 0) + 1
      data.action = (v.negate and IM.ACTION_KEEP) or action
			data.guildbank = v.guildbank
			return data.action, action .. ":" .. k, v:ToString()
		end
	end

	return IM.ACTION_KEEP, nil, nil
end

function IMRS2:List()
  for action, rulelist in pairs(self.rulelists) do
    if #rulelist > 0 then
      local str = GetString("IM_R2_HEADING", action) .. GetString(IM_R2_COUNT_TAG)
      CHAT_SYSTEM:AddMessage(zo_strformat(str, #rulelist))

      for i = 1, #rulelist, 1 do
        CHAT_SYSTEM:AddMessage("  " .. GetString(IM_LIST_RULE) .. i .. ":" .. rulelist[i]:ToString())
      end
      CHAT_SYSTEM:AddMessage("----")
    end
  end
end

function IMRS2:GetRuleList(action)
  return self.rulelists[action]
end