# Copyright (c) 2001-2005 LOGILAB S.A. (Paris, FRANCE).
# http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# Copyright (c) 2004-2005 DoCoMo Euro-Labs GmbH (Munich, Germany).
# http://www.docomolab-euro.com/ -- mailto:tarlano@docomolab-euro.com
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
"""Basic elements for narval memory's:

- data
- url
- file
- command
- dict
- event (only if mxDateTime is available)

:version: $Revision:$  
:author: Logilab

:copyright:
  2001-2005 LOGILAB S.A. (Paris, FRANCE)
  
  2004-2005 DoCoMo Euro-Labs GmbH (Munich, Germany)
  
:contact:
  http://www.logilab.fr/ -- mailto:contact@logilab.fr
  
  http://www.docomolab-euro.com/ -- mailto:tarlano@docomolab-euro.com
"""

__revision__ = "$Id: lib.py,v 1.78 2002/10/14 14:24:11 syt Exp $"
__docformat__ = "restructuredtext en"

import re
from xml.sax.saxutils import escape

from mx.DateTime import now

from narval.public import NO_NS, normalize_url
from narval.element import NSAttribute, ALElement
from narval.xml_handlers import data_handler, ListHandler, DictHandler
from narval.serialutils import date_time_value, date_time_rev_value
from narval.interfaces.base import IURL, IFile, IData, ICommand, IDictionary, \
     ICategory, IEvent, IPhoneCall

# element definitions #########################################################
   
class MasterInformationsElement(ALElement):
    """
    :type jabberid: str
    :ivar jabberid: the jabber id of the bot's master

    :type username: str
    :ivar username: bot's master full name

    :type email: str
    :ivar email: mater's email

    :type sip: str
    :ivar sip: the master's sip uri

    :type prefered_via: str
    :ivar prefered_via: the prefered media for the master to be contacted
    """
    __xml_element__ = (NO_NS, 'master-infos')
    jabberid = NSAttribute(NO_NS, '', str, str)
    username = NSAttribute(NO_NS, '', str, str)
    email = NSAttribute(NO_NS, '', str, str)
    sip = NSAttribute(NO_NS, '', str, str)
    prefered_via = NSAttribute(NO_NS, '', str, str)


class DataElement(ALElement):
    """simple IData implementation"""
    __implements__ = (IData,)
    __xml_element__ = (NO_NS, 'data')
    __child_handler__  = data_handler('data')
    data = ''

    def __init__(self, data = '', **attributes):
        super(DataElement, self).__init__(**attributes)
        self.data = data

    def children_as_xml(self, encoding='UTF-8'):
        """return the XML representation of children in this element.

        :type encoding: str
        :param encoding: the encoding to use in the returned string

        :rtype: str
        :return: XML string representing the element
        """
        return self.data

   
class URLElement(ALElement):
    """IURL implementation"""
    __implements__ = (IURL,)    
    __xml_element__ = (NO_NS, 'url')
    address = NSAttribute(NO_NS, None, str, str)
    encoding = NSAttribute(NO_NS, None, str, str)

    def raw_address(self):
        return self.address
    
    def normalize(self):
        """return the expanded normalized url string"""
        return normalize_url(self.raw_address())[0]

    def protocol(self):
        """return the normalized url string"""
        return normalize_url(self.raw_address())[1][0] or 'file'

    def path(self):
        """return the expanded normalized path string"""
        return normalize_url(self.raw_address())[1][2]


class FileElement(URLElement):
    """IFile and IData implementation"""
    __implements__ = (IFile, IData)
    __xml_element__ = (NO_NS, 'file')
    __child_handler__  = data_handler('data')
    data = ''
    mode = NSAttribute(NO_NS, None, str, str)

    def children_as_xml(self, encoding='UTF-8'):
        """return the XML representation of children in this element.

        :type encoding: str
        :param encoding: the encoding to use in the returned string

        :rtype: str
        :return: XML string representing the element
        """
        return escape(getattr(self, 'data', ''))



class CommandElement(ALElement):
    """ICommand implementation"""
    __implements__ = (ICommand,)

    __xml_element__ = (NO_NS, 'command')
    name = NSAttribute(NO_NS, None, str, str)
    
    __child_handler__  = ListHandler
    list_attr = 'args'
    args = ()

    def children_as_xml(self, encoding='UTF-8'):
        """return the XML representation of children in this element.

        :type encoding: str
        :param encoding: the encoding to use in the returned string

        :rtype: str
        :return: XML string representing the element
        """
        result = []
        for value in self.args:
            result.append('  <listitem>%s</listitem>' % (value,))
        return '\n'.join(result)


class DictElement(ALElement):
    """IDictionary implementation"""
    __implements__ = (IDictionary,)
    __xml_element__ = (NO_NS, 'dictionary')
    __child_handler__  = DictHandler

    def __init__(self, wrapped_dict = None, **attributes):
        super(DictElement, self).__init__(**attributes)
        self._dict = wrapped_dict or {}
        
    def children_as_xml(self, encoding='UTF-8'):
        """return the XML representation of children in this element.

        :type encoding: str
        :param encoding: the encoding to use in the returned string

        :rtype: str
        :return: XML string representing the element
        """
        result = []
        for attr, value in self._dict.items():
            if value is not None:
                result.append('  <%s>%s</%s>' % (attr, value, attr))
        return '\n'.join(result)

    def __getitem__(self, item):
        return self._dict[item]

    def __setitem__(self, item, value):
        self._dict[item] = value

    def as_dict(self):
        return self._dict

        
class CategoryElement(ALElement):
    """ICategory implementation"""
    __implements__ = (ICategory,)
    __xml_element__ = (NO_NS, 'category')
    
    name = NSAttribute(NO_NS, None, str, str)

TIMERGX = re.compile('\d\d:\d\d')
DATERGX = re.compile('((\d\d[-:/]?)+)')


class EventElement(ALElement):
    """IEvent implementation"""

    __implements__ = (IEvent,)
    __xml_element__ = (NO_NS, 'event')
 
    from_time = NSAttribute(NO_NS, None, date_time_value, date_time_rev_value)
    to_time = NSAttribute(NO_NS, None, date_time_value, date_time_rev_value)
    subject = NSAttribute(NO_NS, None, str, str)
    description = NSAttribute(NO_NS, None, str, str)
    location = NSAttribute(NO_NS, None, str, str)
    attendees = NSAttribute(NO_NS, None, str, str)

    def extract_time_from_string(datestr):
        """
        take a string, try to extract a date-time from it and return the
        date time object and the remaining string
        """
        timestr = TIMERGX.finditer(datestr).next().group(0)
        other = TIMERGX.sub('', datestr, 1)
        today = now()
        for group in DATERGX.finditer(datestr):
            parts = [int(part) for part in re.split('[-:/]', group.group(0))]
            day = parts[-1]
            if day > 31 or len(parts) > 3:
                continue
            if len(parts) == 1:
                datestr = '%s-%s-%s' % (today.year, today.month, day)
                break
            month = parts[-2]
            if month > 12:
                continue
            if len(parts) == 2:
                year = today.year
            else:
                year = str(parts[0])
                if len(year) == 2:
                    year = '20' + str(year)
                elif len(year) != 4:
                    continue
            datestr = '%s-%02d-%02d' % (year, month, day)
            other = other.replace(group.group(0), '')
            break
        else:
            raise Exception('Can\'t find date in %r' % datestr)
        return date_time_value('%s %s' % (datestr, timestr)), other
    
    extract_time_from_string = staticmethod(extract_time_from_string)

    date_time_from_string = staticmethod(date_time_value)

    date_time_to_string = staticmethod(date_time_rev_value)



class PhoneCallElement(ALElement):
    """ICategory implementation"""
    __implements__ = (IPhoneCall,)
    __xml_element__ = (NO_NS, 'phone-call')
    
    sip_uri = NSAttribute(NO_NS, None, str, str)
    sentence = NSAttribute(NO_NS, None, str, str)
