# -*- coding: iso-8859-1 -*-
"""
    MoinMoin - IssueSearch Macro

    <<IssueSearch>> or <<IssueSearch()>>
        displays an issue search dialogue.  Uses the issuesearch
	action as a backend to actually find the issue.

    <<IssueSearch(MoinMoinProject/*)>>
        embeds a search result into a page, as if you entered
        'MoinMoinProject/*' into the search box. Only those pages
	that both match the search *and* have an embedded issue
	number are displayed.

    The macro creates a page list without context or match info, just
    like PageList macro. However the list is ordered by Issue number.
    Pages that match, but don't have an issue number are omitted from
    the embedded list.

    @copyright: 2005, 2008 Gary V. Vaughan <gary@gnu.org>
    @license: GNU GPL, see COPYING for details.
"""

import re, urllib
from MoinMoin import config, wikiutil, search

from MoinMoin.Page import Page
from MoinMoin.search.results import SearchResults

Dependencies = ["pages"]

########################################################################
# Class Extender code from:
#     http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/412717

def get_obj(name): return eval(name)

class ExtendInplace(type):
    def __new__(self, name, bases, dict):
        prevclass = get_obj(name)
        del dict['__module__']
        del dict['__metaclass__']

        # We can't use prevclass.__dict__.update since __dict__
        # isn't a real dict
        for k,v in dict.iteritems():
            setattr(prevclass, k, v)
        return prevclass
#
########################################################################

# Add an issue number retriever to the Page class
class Page:
    __metaclass__ = ExtendInplace

    issue_re = r'\bIssue\b.*\b([0-9]+)\b'

    def getIssueNumber(self):
	""" Convenience function to get an issue number from the page text

	@rtype: unicode
	@return: issue number, or None
	"""

	# Lazily compile regex on first use. All instances share the same
	# regex, compiled once when the first call in an instance is done.
	if isinstance(self.__class__.issue_re, (str, unicode)):
	    self.__class__.issue_re = re.compile(self.__class__.issue_re, re.MULTILINE | re.UNICODE)

	try:
	    if self.issue_number: pass
	except AttributeError:
	    body = self.get_raw_body() or ''
	    match = self.__class__.issue_re.search(body)

	    self.issue_number = None
	    if match:
		self.issue_number = match.group(1)

	return self.issue_number

# Add an issueList method to the SearchResults class
class SearchResults:
    __metaclass__ = ExtendInplace

    def issueList(self, request, formatter):
	""" Format a list of found issue pages

	@param request: current request
	@param formatter: formatter to use
	@rtype: unicode
	@return formatted issue list
	"""
	self._reset(request, formatter)
	write = self.buffer.write
	list = formatter.number_list
	querystr = self.querystring()

	# Add pages formatted as list
	if self.hits:
	    write(list(1, type="plain"))

	    for hit in self.hits:
		issue = Page(request, hit.page_name).getIssueNumber()
		item = [
		    formatter.listitem(1, value=issue),
		    formatter.pagelink(1, hit.page_name, querystr=querystr),
		    self.formatTitle(hit),
		    formatter.pagelink(0, hit.page_name),
		    formatter.listitem(0),
		    ]
		write(''.join(item))

	    write(list(0))

	return self.getvalue()

    def sortByIssueNumber(self, request):
	""" Sorts a list of found pages by issue number """
	tmp = [(int(Page(request, hit.page_name).getIssueNumber()), hit)
	  for hit in self.hits if Page(request, hit.page_name).getIssueNumber()]
	tmp.sort()
	self.hits = [item[1] for item in tmp if item[0]]
	self.sort = 'issue_number'



########################################################################

def macro_IssueSearch(macro, needle, label=u'Enter an issue#'):
    request = macro.request
    _ = request.getText

    # if no args given, show an issue search dialogue
    if needle is None or needle == '':
	html = [
	    u'<form method="get" action="">',
	    u'<div>',
            u'<input type="hidden" name="action" value="issuesearch">',
	    u'%s: ' % _(label),
	    u'<input type="text" name="value" size="5" value="">',
	    u'<input type="submit" value="show">',
	    u'</div>',
	    u'</form>',
	    ]
	return u'\n'.join(html)

    # With whitespace argument, show error message like the one used
    # in the search box
    # TODO: search should implement those errors message for clients
    elif needle.isspace():
        err = _('Please use a more selective search term instead of '
                '{{{"%s"}}}') %  needle
        return '<span class="error">%s</span>' % err

    # Search the pages and return the results
    query = search.QueryParser().parse_query(needle.strip())
    results = search.searchPages(request, query)
    results.sortByIssueNumber(request)

    return results.issueList(request, macro.formatter)
