##############################################################################
#
# Copyright (c) 2002-2009 Nexedi SA and Contributors. All Rights Reserved.
#                    Jean-Paul Smets-Solanes <jp@nexedi.com>
#                    Vincent Pelletier <vincent@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
##############################################################################

from zope.interface import Interface

class ISearchKeyCatalog(Interface):

  def buildQuery(kw, ignore_empty_string=True, operator='and'):
    """
      Build a ComplexQuery from kw values.

      kw (dict: string keys, any value)
        A query will be emited based on its value. Depending on the type of
        the value it is handled differently. Query values will be passed
        through to result (key is ignored). For all other types, their key
        must be either a known column of a sql_search_tables table, or a
        related key name.
        - String values will be parsed according to the default SearchKey of
          their real column (even for related keys). If parsing was
          successful, Queries will be generated from its output.
          Otherwise, that value will be taken as such.
        - Dictionary values can be composed of the following keys:
          'query': Their payload value, considered as empty if not given.
          'key': The SearchKey to use for this value, overriding default
                 column configuration.
          (for other possible keys, see SearchKeys)
          They will be taken as such.
        - All other types will be taken as such, and no "empty" check will be
          performed on them.
      ignore_empty_string (boolean)
        If True, values from kw which are empty will be skipped.
        This parameter should ultimately disapear *and* be disabled by
        default, as it is bad to ignore parameters based on their value if
        that value has a meaning in SQL.
      operator (string)
        Used to explicit the logical relation between kw entries.
        It must be a valid ComplexQuery logical operator ('and', 'or').
    """

  def buildEntireQuery(kw, query_table='catalog', ignore_empty_string=1,
                       limit=None, extra_column_list=None):
    """
      Construct and return an instance of EntireQuery class from given
      parameters by calling buildQuery.

      ignore_empty_string (boolean)
        See buildQuery.
      limit (1-tuple, 2-tuple)
        If given, will emit SQL to limit the number of result lines.
      group_by_list (list of strings)
        If given, will emit SQL to group found lines on given parameter names
        (their column if they are column names, corresponding virtual columns
        otherwise - as for related keys).
      select_dict (dict, key: string, value: string, None)
        Given values describe columns to make available in SQL result.
        If column is aliased in result set, key is the alias and value is the
        column.
        Otherwise, key is the column, and value can be None or the same as
        key.
      select_list (list of strings)
        Same as providing select_dict with select_list items as keys, and None
        values.
      order_by_list (list of 1-, 2-, or 3-tuples of strings)
        If given, will emit SQL to sort result lines.
        Sort will happen with decreasing precedence in list order.
        Given n-tuples can contain those values, always in this order:
         - parameter name
         - sort order (see SQL documentation of 'ORDER BY')
         - type cast (see SQL documentation of 'CAST')
        Sort will happen on given parameter name (its column if it's a column
        name, corresponding virtual column otherwise - as for related keys).
      Extra parameters are passed through to buildQuery.

      Backward compatibility parameters:
      Those parameters are deprecated and should not be used. They are present
      to provide backward compatibility with former ZSQLCatalog version.
      REQUEST
        Ignored.
      extra_column_list (list)
      query_table (string, None)
        The table to use as catalog table.
        If given and None, not catalog table will be used. Use this when you
        are using SQLCatalog to generate manualy a part of another query.
        That table has a special position in returned query:
         - all other tables are joined on this one (when it is required to use
           other tables)
         - it is expected to have some columns (uid, path)
        It is strongly discouraged to use this parameter for any value other
        than None.
      group_by
        Use group_by_list instead.
      group_by_expression
        Use group_by_list instead.
      select_expression
        Use select_list or select_dict instead.
      sort_on
        Use order_by_list instead.
      sort_order
        Use order_by_list instead.
      from_expression
        This value will be emited in SQL directly in addition to computed
        value.
        There is no replacement.
      where_expression
        This value will be emited in SQL directly in addition to computed
        value.
        Use Query instances instead.
      select_expression_key
        This prevents given column from being ignored even if they could not
        be mapped.
        There is no replacement.
    """


  def buildSQLQuery(query_table='catalog', REQUEST=None,
                    ignore_empty_string=1, only_group_columns=False,
                    limit=None, extra_column_list=None,
                    **kw):
    """
      Return an SQLExpression-generated dictionary (see
      SQLExpression.asSQLExpressionDict). That SQLExpression is generated by
      an EntireQuery, itself generated by buildEntireQuery from given
      parameters.

      only_group_columns
        Replaces former stat__ parameter.
        Used to globally disalow use of non-group columns in SQL.

      For other parameters, see buildEntireQuery.
    """

  def getSearchKey(column, search_key=None):
    """
      Returns the default SearchKey instance for the
      requested column. There is one instance per
      search_key (incl. virtual keys surch as
      source_title) and we try to compute it once
      only and cache it.
      If search_key is provided, it is used as the
      name of the search key class to return.
    """

  def getComparisonOperator(operator):
    """
      Return a comparison operator matching given string.
      String must be a valid SQL comparison operator (=, LIKE, IN, ...).
      String case does not matter.
      There is one comparison operator instance per possible string value.
    """

  def hasColumn(column):
    """
      Check if the given column or virtual column (in case
      of related keys) exists or not
    """

  # TODO: add support for other operators (logical, ensemblist (?))

  def searchResults(REQUEST=None, **kw):
    """
      Invokes queryResults with the appropriate
      ZSQL Method to return a list of results
    """

  def countResults(REQUEST=None, **kw):
    """
      Invokes queryResults with the appropriate
      ZSQL Method to return a statistics for
      a list of results
    """

  def queryResults(sql_method, REQUEST=None, src__=0, build_sql_query_method=None, **kw):
    """
      Return the result of the given 'sql_method' ZSQL Method after
      processing all parameters to build a Query object passed to
      that method.

      The implementation should do the following.

      1- Use **kw parameters to build a Query object
         by invoking buildQuery

      2- Build a ColumnMap instance by invoking
         the buildColumnMap on the Query. Some
         optmisation may happen here to try
         to build the best possible ColumnMap and
         use the best possible indices for joining.
         During the ColumnMap build process, the
         Search Key associated to each Query node
         in the Query tree registers the columns
         which are used (ex. to search) or provided
         (ex. MATCH value for full text search, 
         interleave expression or parameter in a
         UNION Query)

      3- Render the query object as an SQLExpression
         instance. This instance contains all necessary
         parts to generate:
           - where_expression
           - sort_expression
           - group_by_expression
           - select_expression

      4- Invoke sql_method
    """

  def isAdvancedSearchText(search_text):
    """
      Returns True if given value follows Search Text "advanced" syntax,
      False otherwise.
    """

  def parseSearchText(search_text, column=None, search_key=None, is_valid=None):
    """
      Parses given SearchText expression using given column's parser
      (determined by the SearchKey it is configured to use by default), or
      given SearchKey name.

      search_text (string)
        SearchText to parse.
      column (string)
        Column to use to determine which SearchKey to use for parsing.
        Either this parameter or search_key must be provided.
      search_key (string)
        Name of the SearchKey to use for parsing.
        Either this parameter or column must be provided.
      if_valid (callback)
        Callback method to use to decide wether an encountered column-ish
        identifier in SearchText is a valid column.
        If not provided, catalog schema will be used.
    """
