from string import replace, find, lower
from DateTime import DateTime
import string

class Dummy:
    pass

class OAISchema:
    
    def __init__(self, prefix):
        self.id = prefix
        self._namespaces = {}
        self._fields = []
        self._multi_sortables = []
        self._result_columns = []
        self._item_fields = []
        self._fulltext_fields = []
        self._user_searchables = []
        self._sort_on = None
        self._sort_order = 'ascending'
        
    def setFields(self, fields):
        self._fields = fields
        
    def setMultiSortables(self, sortables):
        self._multi_sortables = sortables

    def setResultColumns(self, columns):
        self._result_columns = columns

    def setItemFields(self, fields):
        self._item_fields = fields

    def setFulltextFields(self, fields):
        self._fulltext_fields = [
            'metadata_' + fieldname for fieldname in fields]
        
    def setUserSearchables(self, fields):
        self._user_searchables = [
            'metadata_' + fieldname for fieldname in fields]

    def setInitialSortOn(self, fieldname):
        self._sort_on = 'metadata_' + fieldname

    def setInitialSortOrder(self, sort_order):
        self._sort_order = sort_order

    def getInitialSortOn(self):
        return self._sort_on

    def getInitialSortOrder(self):
        return self._sort_order
        
    def getMultiSortables(self):
        return self._multi_sortables

    def getResultColumns(self):
        return [column.get_dict() for column in self._result_columns]

    def getItemFields(self):
        return [field.get_dict() for field in self._item_fields]
    
    def getFulltextFields(self):
        return self._fulltext_fields

    def getUserSearchables(self):
        return self._user_searchables

    def getFields(self):
        return self._fields

    def getFieldsAndSortables(self):
        return self._fields + self._multi_sortables

    def getNamespace(self):
        return self._namespace

    def getNamespaces(self):
        return self._namespaces

    def addNamespace(self, prefix, namespace):
        self._namespaces[prefix] = namespace
        
    def installIndexes(self, catalog):
        """install indexes for the fields which are to be indexed in the
        specified catalog
        """
        for field in self._fields:
            field.installIndex(catalog)
        for sortable in self._multi_sortables:
            sortable.installIndex(catalog)
        ids =[]
        for column in self._result_columns:
            id = column.id
            catalog.manage_addColumn(id)
            ids.append(id)
        for field_name in self._user_searchables:
            if not field_name in ids:
                catalog.manage_addColumn(field_name)
            
    def preprocess(self, map):
        self.addTuplesToMap(map)
        for field in self.getFields():
            if map.has_key(field.id):
                map[field.id] = field.preprocess(map[field.id])
            
    def addTuplesToMap(self, map):
        for sortable in self._multi_sortables:
            values = []
            for fieldname in sortable.getFieldNames():
                if map.has_key(fieldname):
                    value = map[fieldname]
                else:
                    value = []
                values.append(value)
            map[sortable.id] = tuple(values)
        return map
    
class OAIField:
    def __init__(self, id, xpath):
        self.id = "metadata_" + id
        self._name = id
        self._xpath = xpath
   
    def getXpath(self):
        """Return the xpath expression for field.
        """
        return self._xpath

    def getIndexType(self):
        """Which index type is used to index field in the catalog.
        """
        raise NotImplementedError

    def getId(self):
        return self.id
    
    def getName(self):
        return self._name
    
    def getTitle(self):
        return replace(self._name, '_', ' ').title()        
    
    def installIndex(self, catalog):
        """Creates an index in the catalog for this field, if necessary.
        """
        index_type = self.getIndexType()
        if not index_type is None:
            catalog.manage_addIndex(self.id, index_type)
                
    def preprocess(self, value):
        """Massage the value into the right data type.
        """
        if len(value) == 0:
            return None
        return value

    def addEditField(self, form, filters, catalog):
        """Add a formulator field to edit form for this OAIField.
        """
        # By default do not add the field
        pass
    
    def addPublicField(self, form, unique_values):    
        """Add a formulator field to public form for this OAIField.
        """
        raise NotImplementedError
        
    def updateQuery(self, query, filters):
        """Update the query dictionary with query parameter(s) for field.
        """
        id = self.getId()
        value = filters.get(id, None)
        if value:
            query[id] = value
        
    def updatePublicQuery(self, query, form_results, unique_values=None):
        """Update the query dictionary with query parameter(s) for field.
        
        This adds further restrictions to the predefined query.
        """
        id = self.getId()
        value = form_results.get(id, None)
        if value:
            query[id] = value

    def renderItemField(self, value):
        """Render the field for use in the public Item Page Template.
        """
        return ' '.join(value)
        
class OAIDateField(OAIField):
    def getIndexType(self):
        return 'DateIndex'
    
    def preprocess(self, value):
        try:
            value = DateTime(value[0])
        except:
            # XXX Bare except!
            value = None
        return value

    def addEditField(self, form, filters, catalog):
        id = self.getId()
        title = self.getTitle()
        start_date  = filters.get('start_dt_' + id, None)
        end_date  = filters.get('end_dt_' + id, None)
        form.manage_addField(
            'start_dt_' + id,
            'From ' + title,
            'DateTimeField')
        values = form.get_field('start_dt_' + id).values
        values['required'] = 0
        values['unicode'] = 1
        values['date_only'] = 1
        if start_date is not None:
            values['default'] = start_date
        form.manage_addField(
            'end_dt_' + id, 
            'Until ' + title,
            'DateTimeField')
        values = form.get_field('end_dt_' + id).values
        values['required'] = 0
        values['unicode'] = 1
        values['date_only'] = 1
        if end_date is not None:
            values['default'] = end_date
        
    def addPublicField(self, form, unique_values):    
        id = self.getId()
        title = self.getTitle()
        form.manage_addField(
            'year_' + id,
            'Year of ' + title,
            'IntegerField'
            )
        values = form.get_field('year_' + id).values
        values['required'] = 0
        values['unicode'] = 1

    def updateQuery(self, query, filters):
        id = self.getId()
        dates = []
        range = ''
        start_date = filters.get('start_dt_' + id, None)
        if start_date:
            dates.append(start_date)
            range = 'min'
        end_date = filters.get('end_dt_' + id, None)
        if end_date:
            dates.append(end_date)
            range = range + 'max'
        if range:
            query[id] = {'query': dates, 'range': range}
        
    def updatePublicQuery(self, query, form_results, unique_values=None):
        id = self.getId()
        year = form_results.get('year_' + id, None)
        if year:
            query[id] = {
                'query': [DateTime('01/01/%s' % year ), DateTime('12/31/%s' % year )],
                'range': 'minmax'
                }

    def renderItemField(self, value):
        return value.strftime('%d-%m-%Y')
    
class OAIKeywordField(OAIField):
    def getIndexType(self):
        return 'KeywordIndex'

    def addEditField(self, form, filters, catalog):
        id = self.getId()
        title = self.getTitle()
        form.manage_addField(id, title, 'MultiListField')
        values = form.get_field(id).values
        values['size'] = 3
        values['required'] = 0
        values['unicode'] = 1
        default_value = filters.get(id, None)
        if default_value is not None:
            values['default'] = default_value
        result = [('All values', 'none')]
        for value in catalog.uniqueValuesFor(id):
            if value:
                result.append((value[:60], value))
        values['items'] = result

    def addPublicField(self, form, unique_values):    
        id = self.getId()
        title = self.getTitle()
        form.manage_addField('txt_' + id, title, 'StringField')
        values = form.get_field('txt_' + id).values
        values['required'] = 0
        values['unicode'] = 1
        result = [('All values', 'none')]
        result = result + unique_values
        if len(result) > 1:
            form.manage_addField(
                'list_' + id, '', 'ListField')
            values = form.get_field('list_' + id).values
            values['title'] = ''
            values['size'] = 1
            values['required'] = 0
            values['unicode'] = 1
            values['items'] = result

    def updatePublicQuery(self, query, form_results, unique_values=None):
        id = self.getId()
        txt = lower(form_results.get('txt_' + id, None))
        if txt:
            search_list = []
            for value in unique_values:
                if find(lower(value), txt) > -1:
                    search_list.append(value)
            query[id] = search_list
        else:
            lst = form_results.get('list_' + id, 'none')
            if lst is not None and lst != 'none':
                query[id] = lst
        
class OAIFulltextField(OAIField):
    def getIndexType(self):
        return 'ZCTextIndex'
    
    def installIndex(self, catalog):
        id = self.getId()
        dummy = Dummy()
        dummy.index_type = 'Okapi BM25 Rank'
        dummy.lexicon_id = 'oai_lexicon'
        catalog.manage_addProduct['ZCTextIndex'].manage_addZCTextIndex(
            id, extra=dummy)
            
    def addPublicField(self, form, unique_values):    
        id = self.getId()
        title = self.getTitle()
        form.manage_addField(id, title, 'StringField')
        values = form.get_field(id).values
        values['required'] = 0
        values['unicode'] = 1
        
class OAINoIndexField(OAIField):    
    def getIndexType(self):
        return None
    
    def installIndex(self, catalog):
        # Not to be indexed, so don't install anything
        pass

    def addPublicField(self, form, unique_values):    
        pass
    
    def updateQuery(self, query, value):
        pass
        
    def updatePublicQuery(self, query, form_results, unique_values=None):
        pass
    
class OAIMultiSortable:
    def __init__(self, id, fieldnames):
        self.id = "metadata_" + id
        self._fieldnames = ["metadata_" + name for name in fieldnames]

    def getFieldNames(self):
        return self._fieldnames
    
    def installIndex(self, catalog):
        catalog.manage_addIndex(self.id, 'FieldIndex')

class OAIColumn:
    
    def __init__(self, id, title, sort):
        self.id = "metadata_" + id
        self.title = title   
        self.sort = "metadata_" + sort
        
    def get_dict(self):
        return {'id' : self.id, 'title' : self.title, 'sort' : self.sort}
    
class OAIItemField:
    
    def __init__(self, id, title, display):
        self.id = "metadata_" + id
        self.title = title   
        self.display = display
        
    def get_dict(self):
        return {'id' : self.id, 'title' : self.title, 'display' : self.display}
