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"]

LIBRARY METHODS

The following methods may be provided by filters.

* filter:NewSettings([defaults]) [REQUIRED]
  in: defaults (table or nil) - Defaults for the 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.

  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.

  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 nil) - Settings to be described
  out: description (string or nil) - Localized description of settings

  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.

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

  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.

  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 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.

  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 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.

  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.UnregisterCallback(target,"FilterUpdate")
  filter.UnregisterAllCallbacks(target)
  in: target (table) - Some table owned by the caller

  Stops calling a previously registered callback.
