LibFilter-1.0 is meant to be a way to create generic filters that can be
used as plugins by other addons. This document describes the API contract
that filters must follow.

Some of the examples below have extra parathesis are a wrapped earlier than
normal code so that the width of this file stays within 80 columns.

TYPES

* Filter Strings

  All filters work on strings in the format of WoW item strings, spell
  strings etc. That is "type:value1:value2:value3:..." Filter should
  transparently strip off hyperlink formatting "|cXXXXXXXX|H...|h[...]|h|r"

* Filter Type

  This may be any of the standard Blizzard types: "achievement", "item",
  "enchant", "item", "quest", "spell", or "talent"; or it may be any
  custom type. See the LibFilter-1.0 library variable FilterType in the
  source code for some recommended types.

LIBRARY CREATION

  A LibFilter filter is a LibStub library that follows the conventions in
  this document. The library name should begin with "LibFilter-".

  As a convience LibFilter can be used to create the library with the
  NewFilter method, but it is recommended that filters use LibStub directly
  to avoid having to depend on the LibFilter-1.0 library.

  Example:
    local MAJOR, MINOR = "LibFilter-myfilter", 1
    local filter = LibStub:NewLibrary(MAJOR, MINOR)
    if not filter then return end

LIBRARY VARIABLES

  Filters are required to have some standard variables

* filter.key (string) [REQUIRED]
  This is string used to identify the filter. This string should not
  be localized.

  Example:
    filter.key = "myfilter"

* filter.filtertype (string) [REQUIRED]
  This is the type of strings the filter supports. If a filter supports
  more than one type of string then this should be set to the main type or
  "GENERIC". It is important that this string not be localize.

  Example:
    filter.filtertype = "item"

* filter.filtertypes (table) [OPTIONAL]
  This is a table of filter types supported by the filter. As with
  filtertype the keys of this table should not be localized.


  Important note: If this table users of this filter may create it and add
  the filter type in filter.filtertype to it.

  Example:
    filter.filtertypes = filter.filtertypes or {}
    filter.filtertypes["item"] = 1
    filter.filtertypes["Slot"] = 1

* filter.author (string) [OPTIONAL]
  The author of the filter. This is the author of the filter itself even if
  it is primarily an interface to another addon.

  Example:
    filter.author = "me"

* filter.credits (string) [OPTIONAL] [LOCALIZED]
  This string should list any credits the author wish to give. If the
  filter is an interface to a specific addon, the author of that addon
  should probably be listed here. May be localized, this is also a good
  spot to put translation credits.

  Example:
    filter.credits = L["XYZ was written by Someone Else."]

* filter.name (string) [OPTIONAL] [LOCALIZED]
  The name of filter localized into the current language. If not set
  filter.key may be used instead.

  Example:
    filter.name = L["MyFilter"]

* filter.description (string) [OPTIONAL] [LOCALIZED]
  A longer description localized into the current language. If not set
  filter.name or filter.key may be used instead.

  Example:
    filter.description = L["This is a sample filter that does nothing"]

* filter.extensions (table) [OPTIONAL]
  This is a table of tables for addon specific extensions. It should be a table
  where the keys are the names of a particular addon and the value is specific to
  that addon.

  Usage Example: Check for MyExtension for MyAddon
    local myextensions = filter and filter.extensions and filter.extensions["MyAddon"]
    if myextensions["MyExtension"] then
      -- Do something special with this filter
    end

  Implementation Example: (Enable "SomeBagAddonExtension" for "SomeBagAddon")
    filter.extensions = filter.extensions or {}
    local extensions =  filter.extensions
    extensions.SomeBagAddon = extensions.SomeBagAddon or {}
    local sba_extensions = extensions.SomeBagAddon
    sba_extensions.SomeBagAddonExtension = true

* filter.icon (string) [OPTIONAL]
  Icon used to represent this filter.

  Example:
    filter.icon = "Interface\\Icons\\INV_Misc_QuestionMark"

LIBRARY METHODS

The following methods may be provided by filters.

* filter:IteratePresets([type]) [OPTIONAL]
  in: type (string or nil) - Type to iterate over.
  out: preset iterator
  usage: for preset, parent in filter:IteratePresets() do ... end
  out: preset (string) - preset name (not localized)
  out: parent (string or nil) - preset parent (not localized)

  This method iterators over strings that can be used in place of
  settings. This is intended to be used as an alternative to settings
  for supporting addons where settings are not an option.
  Use filter:DescribeSettings(preset) to get the localized name of a
  preset. If the string used for parent can be, but does not have to
  be the name of another preset.

  Usage Example:
    for preset in filter:IteratePresets("item") do
      -- Do something to display preset as an option
      local name,icon
      if filter.DescribeSettings then
        name,icon = filter:DescribeSettings(preset)
      end
      name = name or preset
      if name == "" then
        name = filter.name or filter.key
      end
      table.insert(options, {
        type = "toggle",
        name = name,
        desc = name,
        icon = icon,
        get = function() return ActiveFilter = filter and ActivePreset == preset end,
        set = function()
          ActiveFilter = filter
          ActivePreset = preset
          UpdateDisplay()
        end,
      })
    end

  Implementation Example:
    filter.presets = filter.presets or {}
    local presets = filter.presets
    presets[""] = "" -- Default preset
    presets["new"] = "" -- new items
    presets["newgreens"] = "new" -- new green items
    presets["old"] = "" -- old items
    presets["oldgreens"] = "old" -- old green items
    ...
    function filter:IteratePresets(type) return pairs(filter.presets) end
  
* filter:NewSettings([defaults],[type]) [REQUIRED]
  in: defaults (table or string or nil) - Defaults for the settings
  in: type (string or nil) - Type of settings.
  out: settings (table or nil) - Opaque setting table

  This method creates settings that may be used for effectively an
  individual instance of this filter. The contents of the settings
  table should be treated as opaque, but may be stored in a saved
  variable. This function will never return input table. If the filter
  needs no settings it may return nil. For some filters that support
  multiple filtering types type must be provided to tell the filter
  for which type of settings to create a filter. If the filter supports
  presets then the name of the preset may be passed in the defaults
  parameter to get copy of settings based on that preset.

  Usage Example: Create brand new settings
    settings = filter:NewSettings()

  Usage Example: Rebless settings restored from saved variables
    settings = filter:NewSettings(settings)

  Usage Example: Make a copy of settings
    settings_copy = filter:NewSettings(settings_orig)

  Implementation Example: No settings needed
    function filter:NewSettings(defaults) return nil end

  Implementation Example: Simple
    function filter:NewSettings(defaults)
      return {
        option1 = defaults and defaults.option1 or option1default,
        option2 = defaults and defaults.option2 or option2default,
      }
    end

  Implementation Example: With Record keeping
    -- Some private record of active settings
    filter.active_settings = (filter.active_settings
                              or setmetatable({}, {__mode='k'}))
    local active_settings = filter.active_settings

    function filter:NewSettings(defaults)
      local res = {
        option1 = defaults and defaults.option1 or option1default,
        option2 = defaults and defaults.option2 or option2default,
      }
      if not next(active_settings) then
        -- First settings detected start gathering data
      end
      active_settings[res] = 1
      return res
    end

* filter:FreeSettings(settings) [REQUIRED]
  in: settings (table or nil) - Settings no longer to be used.

  This function lets the filter know that the settings passed in will no
  longer be used. It may used this information to determine if it no
  longer needs collect data etc.

  Note: Passing a preset name into this function is a coding error.

  Implementation Example: Simple or no settings needed
    function filter:FreeSettings(settings) end

  Implementation Examlpe: With record keeping
    function filter:FreeSettings(settings)
      active_settings[settings] = nil
      if not next(active_settings) then
        -- Last settings unregistered stop gathering data
      end
    end

* filter:DescribeSettings(settings) [OPTIONAL]
  in: settings (table or string or nil) - Settings to be described
  out: description (string or nil) - Localized description of settings
  out: icon (string or nil) - Path to texture
  out: short (string) - Short version of the description (optional)

  This function returns a localized single line description of the
  settings. If it is not present or returns nil, then the
  filter.name or filter.key should be used in order. If the filter
  supports presets, then settings may be the name of a preset or the
  name of a preset parent.

  Usage Example: Description only
    desc = (filter.DescribeSettings and filter:DescribeSettings(settings)
            or filter.name or filter.key)

  Usage Example: Description and icon
    if filter.DescribeSettings then
      desc,icon,short = filter:DescribeSettings(settings)
    end
    desc = desc or filter.name or filter.key
    short = short or desc

  Implementation Example:
    function filter:DescribeSettings(settings)
      return (L["MyFilter option1: %s option2: %s"]
              :format(settings and settings.option1 or "???"
                     ,settings and settings.option2 or "???"))
    end

* filter:SettingsOptions(settings,[func]) [OPTIONAL]
  filter:SettingsOptions(settings,[table,method])
  in: settings (table) - Settings to be configured
  in: func (function) - Function to be called when settings are changed
  in: table (table) - Table with a method
  in: method (string) - Method of table to be called when setting changed.
  out: optiontable (table) - AceOption table.

  Returns a AceOption table that can be used to display and change the
  options for settings. If neither func nor table and method are specified
  the normal callbacks will be fired if any settings are changed. The
  callback will be given settings as a parameter.

  Note: Passing a preset into SettingsOptions is a coding error.

  If a filter does not have any settings this function will not be defined.

  Usage Example:
    local opttable = filter:Settings(settings,update)
    ...:Feed...(...,optable,...)

  Implementation Example:
    function filter:SettingsOptions(settings,table,method)
      if not settings then return end

      local callback
      if table then
        if type(table) == "string" then
          callback = function() table[func](table,settings) end
        else
          callback = function() table(func,settings) end
        end
      end
      if not callback then
        callback = function() callbacks:Fire("FilterUpdate") end
      end

      local args = {
        {
          order = 1,
          type = "toggle",
          name = L["option1"],
          desc = L["option1desc"],
          get = function() return settings and settings.option1 end,
          set = function(v)
                  settings.option1 = v
                  callback()
                end,
        },
        {
          order = 1,
          type = "toggle",
          name = L["option2"],
          desc = L["option2desc"],
          get = function() return settings and settings.option2 end,
          set = function(v)
                  settings.option2 = v
                  callback()
                end,
        }
      }
      return { type = "group", desc = "myfilter_options", args = args }
    end

* filter:TestIfMatch(settings,linkorstring) [REQUIRED]
  in: settings (table or string or nil) - Settings to be used for test
  in: linkorstring (string or nil) - Filter String to be matched
  out: true if match otherwise nil or false

  This function test if the filter with the give settings matches
  the provided string. If presets are supported settings may be a preset
  string.

  IMPORTANT NOTE: This function should not assume that the linkorstring
  parameter has been normalized in any way. It should removed any link
  formatting and handle missing values as necessary. For strings based
  on Blizzard types the passed value must work when passed to any normal
  function expecting that type of string.

  LibStub("LibFilter-1.0"):Decode can be used if necessary to converted a
  linkorstring into a table of the pieces. Though a filter specific
  approach is normally better.

  Usage Example:
    if (filter:TestIfMatch(settings,"item:6948:0:0:0:0:0:0:0")) then
      -- filter matches Hearthstone
    end

  Implementation Example:
    function filter:TestIfMatch(settings,linkorstring)
      local id = linkorstring and linkorstring:match("(%-?%d+)")
      if not settings or settings.option1 == "hearthstone" then
         if id and id == 6948 then return true end
      end
      return nil
    end

* filter:TestIfMatchSplit(settings,...) [OPTIONAL]
  in: settings(table or string or nil) - Settings to be used for test
  in: ... - linkorstring split along : and hyperlink removed
  out: filter:TestIfMatch(settings,strjoin(":",...))

  If this function is present it should be a optimization for working
  with strings that have already been split into pieces. If presets are
  supported, settings may be a preset string.

  CAUTION: The first parameter after settings must be the type of string.
    IE: Good: filter:TestIfMatchSplit(settings,"item",6948)
        Wrong: filter:TestIfMatchSplit(settings,6948)

  Usage Example:
    local match
    if filter.TestIfMatchSplit then
       match = filter:TestIfMatchSplit(settings,"Slot",bag,slot)
    else
       match = filter:TestIfMatch(settings,strjoin(":","Slot","bag","slot"))
    end

  Implementation Example:
    function filter:TestIfMatchSplit(settings,type,id)
      if not settings or settings.option1 == "hearthstone" then
         if id and id == 6948 then return true end
      end
      return nil
    end

* filter.RegisterCallback(target,"FilterUpdate",method) [OPTIONAL]
  filter.RegisterCallback(target,"FilterUpdate",func) [OPTIONAL]
  in: target (table) - Some table owned by the caller
  in: method (string) - Name of a method on table.
  in: func (function) - Function to call

  Registers a callback that will be called if the filter should be
  retested.

  Usage Example:
    ...
      if filter.RegisterCallback then
        filter.RegisterCallback(actor,"FilterUpdate","Rescan")
      end
    ...
    function actor:Rescan()
      -- Retest filter againsnt whatever
    end

  Implementation Example:
    filter.callbacks = (filter.callbacks
                        or LibStub("CallbackHandler-1.0"):New(filter))
    local callbacks = filter.callbacks

    -- Let our consumers know that they need to retest
    callbacks:Fire("FilterUpdate")

* filter.RegisterCallback(target,"PresetUpdate",method) [OPTIONAL]
  filter.RegisterCallback(target,"PresetUpdate",func) [OPTIONAL]
  in: target (table) - Some table owned by the caller
  in: method (string) - Name of a method on table.
  in: func (function) - Function to call

  Registers a callback that will be called if the available presets
  have changed.

  Usage Example:
    ...
      if filter.RegisterCallback then
        filter.RegisterCallback(actor,"PresetUpdate","RescanPresets")
      end
    ...
    function actor:RescanPresets()
      -- Rescan the list of presets and update option GUI as needed.
    end

  Implementation Example:
    filter.callbacks = (filter.callbacks
                        or LibStub("CallbackHandler-1.0"):New(filter))
    local callbacks = filter.callbacks

    -- Let our consumers know that they need to retest
    callbacks:Fire("PresetUpdate")

* filter.UnregisterCallback(target,"FilterUpdate")
  in: target (table) - Some table owned by the caller

  Stops calling a previously registered callback for filter changes.

* filter.UnregisterCallback(target,"PresetUpdate")
  in: target (table) - Some table owned by the caller

  Stops calling a previously registered callback about preset changes.

* filter.UnregisterAllCallbacks(target)
  in: target (table) - Some table owned by the caller

  Stops calling all previously registered callback for target.
