# -*- coding: utf-8 -*-
"""
w2lapp.searchform.py: different search forms

web2ldap - a web-based LDAP Client,
see http://www.web2ldap.de for details

(c) by Michael Stroeder <michael@stroeder.com>

This module is distributed under the terms of the
GPL (GNU GENERAL PUBLIC LICENSE) Version 2
(see http://www.gnu.org/copyleft/gpl.html)
"""

import types,ldap,pyweblib.forms,w2lapp.core,w2lapp.gui,w2lapp.cnf

searchform_mode_text = {
  'adv':'Advanced',
  'base':'Basic',
  'exp':'Expert',
}

SEARCH_OPT_CONTAINS = ur'(%s=*%s*)'
SEARCH_OPT_EXISTS = ur'(%s=*)'
SEARCH_OPT_DOESNT_CONTAIN = ur'(!(%s=*%s*))'
SEARCH_OPT_IS_EQUAL = ur'(%s=%s)'
SEARCH_OPT_IS_NOT = ur'(!(%s=%s))'
SEARCH_OPT_BEGINS_WITH = ur'(%s=%s*)'
SEARCH_OPT_ENDS_WITH = ur'(%s=*%s)'
SEARCH_OPT_SOUNDS_LIKE = ur'(%s~=%s)'
SEARCH_OPT_GE_THAN = ur'(%s>=%s)'
SEARCH_OPT_LE_THAN = ur'(%s<=%s)'

search_options = (
  (SEARCH_OPT_CONTAINS,u'contains'),
  (SEARCH_OPT_EXISTS,u'exists'),
  (SEARCH_OPT_DOESNT_CONTAIN,u"doesn't contain"),
  (SEARCH_OPT_IS_EQUAL,u'is'),
  (SEARCH_OPT_IS_NOT,u'is not'),
  (SEARCH_OPT_BEGINS_WITH,u'begins with'),
  (SEARCH_OPT_ENDS_WITH,u'ends with'),
  (SEARCH_OPT_SOUNDS_LIKE,u'sounds like'),
  (SEARCH_OPT_GE_THAN,u'greater equal than'),
  (SEARCH_OPT_LE_THAN,u'lesser equal than')
)

SEARCH_SCOPE_STR_BASE = u'0'
SEARCH_SCOPE_STR_ONELEVEL = u'1'
SEARCH_SCOPE_STR_SUBTREE = u'2'

SEARCH_SCOPE_OPTIONS = (
  (SEARCH_SCOPE_STR_BASE,u'Base'),
  (SEARCH_SCOPE_STR_ONELEVEL,u'One level'),
  (SEARCH_SCOPE_STR_SUBTREE,u'Sub tree'),
)

def SearchForm_exp(sid,outf,command,form,ls,dn,sub_schema,filterstr=''):
  """Output expert search form"""
  # expert search form for using LDAP filters
  filterstr = form.getInputValue('filterstr',[filterstr])[0]
  result = """
  <fieldset>
    <legend>LDAP filter string</legend>
    <input name="filterstr" maxlength="400" size="%d" value="%s">
  </fieldset>
    """ % (
      w2lapp.cnf.GetParam(ls,'searchform_filterstr_size',90),
      form.utf2display(filterstr),
    )
  return result # SearchForm_exp()


def SearchForm_base(sid,outf,command,form,ls,dn,sub_schema):
  """
  Output basic search form based on a HTML template configured
  with host-specific configuration parameter searchform_template
  """
  searchform_template_name = form.getInputValue('searchform_template',['_'])[0]
  searchform_template_cfg = w2lapp.cnf.GetParam(ls,'searchform_template','')
  assert type(searchform_template_cfg)==types.DictType,TypeError("Host-specific parameter 'searchform_template' has invalid type")
  searchform_template = searchform_template_cfg.get(searchform_template_name,None)
  searchform_template_filename = w2lapp.gui.GetVariantFilename(searchform_template,form.accept_language)
  template_str = open(searchform_template_filename,'r').read()
  return template_str # SearchForm_base()


def SearchForm_adv(sid,outf,command,form,ls,dn,sub_schema):
  """advanced search form with select lists"""

  search_submit = form.getInputValue('search_submit',[u''])[0]

  # Get input values
  search_attr_list = form.getInputValue('search_attr',[u''])
  search_option_list = form.getInputValue('search_option',[None]*len(search_attr_list))
  search_mr_list = form.getInputValue('search_mr',[None]*len(search_attr_list)) 
  search_string_list = form.getInputValue('search_string',[u'']*len(search_attr_list))

  if search_submit.startswith('-'):
    del_row_num = int(search_submit[1:])
    del search_option_list[del_row_num]
    del search_attr_list[del_row_num]
    del search_mr_list[del_row_num]
    del search_string_list[del_row_num]
  elif search_submit.startswith('+'):
    insert_row_num = int(search_submit[1:])
    search_option_list.insert(insert_row_num+1,search_option_list[insert_row_num])
    search_attr_list.insert(insert_row_num+1,search_attr_list[insert_row_num])
    search_mr_list.insert(insert_row_num+1,search_mr_list[insert_row_num])
    search_string_list.insert(insert_row_num+1,u'')

  if not len(search_option_list)==len(search_attr_list)==len(search_string_list):
    raise w2lapp.core.ErrorExit(u'Invalid search form data.')

  search_mode = form.getInputValue('search_mode',[ur'(&%s)'])[0]

  search_mode_select = pyweblib.forms.Select(
    'search_mode',
    u'Search mode',1,
    options=[(ur'(&%s)',u'all'),(ur'(|%s)',u'any')],
    default=search_mode
  )
  search_mode_select.setCharset(form.accept_charset)

  mr_list = [u'']
  mr_list.extend(sorted([unicode(mr) for mr in sub_schema.name2oid[ldap.schema.MatchingRule].keys()]))

  search_attr_options = []
  for attr_type in w2lapp.cnf.GetParam(ls,'search_attrs',[]) or sub_schema.sed[ldap.schema.AttributeType].keys():
    attr_type_se = sub_schema.get_obj(ldap.schema.AttributeType,attr_type)
    if attr_type_se:
      if attr_type_se.names:
        attr_type_name = unicode(attr_type_se.names[0],ls.charset)
      else:
        attr_type_name = unicode(attr_type)
      if attr_type_se.desc:
        attr_type_desc = unicode(attr_type_se.desc,ls.charset)
      else:
        attr_type_desc = None
      search_attr_options.append((
        attr_type_name,
        attr_type_name,
        attr_type_desc
      ))
    else:
      search_attr_options.append((attr_type,attr_type,None))
    search_attr_options.sort()
  search_attrs = [ attr_type for attr_type,_,_ in search_attr_options ]

  # Create a select field instance for attribute type name
  search_attr_select = pyweblib.forms.Select(
    'search_attr',u'Search attribute type',
    w2lapp.cnf.misc.max_searchparams,
    options=sorted(list(set(search_attr_options+filter(None,search_attr_list)))),
  )
  search_attr_select.setCharset(form.accept_charset)

  # Create a select field instance for matching rule name
  search_mr_select = pyweblib.forms.Select(
    'search_mr',u'Search attribute type',
    w2lapp.cnf.misc.max_searchparams,
    options=mr_list,
  )
  search_mr_select.setCharset(form.accept_charset)

  search_fields_html_list = []

  # Output a row of the search form
  for i in range(len(search_attr_list)):
    search_fields_html_list.append('\n'.join((
      '<tr>\n<td rowspan="2">',
      '<button type="submit" name="search_submit" value="+%d">+</button>' % (i),
      '<button type="submit" name="search_submit" value="-%d">-</button>' % (i),
      '</td>\n<td>',
      search_attr_select.inputHTML(default=search_attr_list[i]),
      search_mr_select.inputHTML(default=search_mr_list[i]),
      form.field['search_option'].inputHTML(default=search_option_list[i]),
      '</td></tr>\n<tr><td>',
      form.field['search_string'].inputHTML(default=search_string_list[i]),
      '</td></tr>',
    )))

  # Eigentliches Suchformular ausgeben
  result = """
    <input type="hidden" name="searchform_mode" value="adv">
    <fieldset>
      <legend>Search filter parameters</legend>
      Match %s of the following.<br>
      <table>%s</table>
    </fieldset>
    """ % (
      search_mode_select.inputHTML(),
      '\n'.join(search_fields_html_list),
      )
  return result # SearchForm_adv()


def w2l_SearchForm(
  sid,outf,command,form,ls,dn,
  searchform_mode='base',
  Msg='',
  filterstr='',
  scope=ldap.SCOPE_SUBTREE,
  search_root=None,
):
  """Output a search form"""

  if Msg:
    msg_html = '<p class="ErrorMessage">%s</p>' % (Msg)
  else:
    msg_html = ''

  sub_schema = ls.retrieveSubSchema(dn,w2lapp.cnf.GetParam(ls,'_schema',None))

  searchform_mode = form.getInputValue('searchform_mode',[searchform_mode])[0]
  search_root = form.getInputValue('search_root',[search_root or ls.getSearchRoot(dn)])[0]
  search_root_field = w2lapp.gui.SearchRootField(
    form,ls,dn,
    name='search_root',
    search_root_searchurl=w2lapp.cnf.GetParam(ls,'searchform_search_root_url',None)
  )
  search_root_field.setDefault(search_root)

  ContextMenuList = [
    form.applAnchor(
      'searchform',searchform_mode_text[mode],sid,
      [
        ('dn',dn),
        ('searchform_mode',mode),
        ('search_root',search_root),
        ('filterstr',filterstr),
        ('scope',str(scope)),
      ],
    )
    for mode in searchform_mode_text.keys()
    if mode!=searchform_mode
  ]

  searchform_template_cfg = w2lapp.cnf.GetParam(ls,'searchform_template','')
  if type(searchform_template_cfg)==types.DictType:
    for searchform_template_name in searchform_template_cfg.keys():
      if searchform_template_name!='_':
        ContextMenuList.append(form.applAnchor(
          'searchform',searchform_template_name,sid,
          [
            ('dn',dn),
            ('searchform_mode','base'),
            ('searchform_template',searchform_template_name),
            ('search_root',search_root),
            ('filterstr',filterstr),
            ('scope',str(scope)),
          ],
        ))

  if searchform_mode == 'base':
    # base search form with fixed input fields
    try:
      inner_searchform_html = SearchForm_base(sid,outf,command,form,ls,dn,sub_schema)
    except IOError:
      msg_html = '\n'.join((
        msg_html,
        '<p class="ErrorMessage">I/O error while loading search form template!</p>'
      ))
      inner_searchform_html = SearchForm_adv(sid,outf,command,form,ls,dn,sub_schema)
  elif searchform_mode == 'exp':
    # expert search form with single filter input field
    inner_searchform_html = SearchForm_exp(sid,outf,command,form,ls,dn,sub_schema,filterstr)
  elif searchform_mode == 'adv':
    # base search form with fixed input fields
    inner_searchform_html = SearchForm_adv(sid,outf,command,form,ls,dn,sub_schema)

  searchoptions_template_filename = w2lapp.gui.GetVariantFilename(
    w2lapp.cnf.GetParam(ls,'searchoptions_template',None),
    form.accept_language
  )
  searchoptions_template_str = open(searchoptions_template_filename,'r').read()

  w2lapp.gui.TopSection(
    sid,outf,form,ls,dn,
    '%s Search Form' % searchform_mode_text[searchform_mode],
    w2lapp.gui.MainMenu(sid,form,ls,dn),
    context_menu_list=ContextMenuList
  )

  outf.write("""
  <div id="Input" class="Main">
    {msg_html}
    {form_search_html}
      <p>
        <input type="submit" name="search_submit" value="Search">
        <input type="reset" value="Reset">
      </p>
      {inner_searchform_html}
      {form_dn_html}
      {searchoptions_template_str}
    </form>
  </div>
  """.format(
    form_search_html=form.beginFormHTML('search',sid,'GET'),
    msg_html=msg_html,
    inner_searchform_html=inner_searchform_html,
    form_dn_html=form.hiddenFieldHTML('dn',dn,u''),
    searchoptions_template_str=searchoptions_template_str.format(
      search_root_field.inputHTML(),
      form.field['scope'].inputHTML(
        default=form.getInputValue('scope',[unicode(scope)])[0]
      ),
      form.field['search_resnumber'].inputHTML(
        default=unicode(w2lapp.cnf.GetParam(ls,'search_resultsperpage',10))
      ),
      form.field['search_lastmod'].inputHTML(
        default=form.getInputValue('search_lastmod',[unicode(-1)])[0]
      ),
      form.utf2display(form.getInputValue('search_attrs',[''])[0]),
    ),
  ))

  w2lapp.gui.PrintFooter(outf,form)
