# pylint:disable-msg=E0222
#
# Copyright (c) 2005 DoCoMo Euro-Labs GmbH (Munich, Germany).
# Copyright (c) 2001-2005 LOGILAB S.A. (Paris, FRANCE).
#
# http://www.docomolab-euro.com/ -- mailto:tarlano@docomolab-euro.com
# http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# 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
"""interfaces and adapters for some base elements:

:data handler:
- url
- file
- event
- command
- category

:data structure:
- raw data
- dictionary

:communication:
- protocol handler activator
- email
- IM message / presence
- phone call

:abstraction:
- file-like openable
- command initiator


:version: $Revision:$  
:author: Logilab

:copyright:
  2001-2005 LOGILAB S.A. (Paris, FRANCE)
  
  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 urllib2


from narval.public import Interface, Adapter
from narval.interfaces.core import IError

# interfaces ##################################################################

class IActivator(Interface):
    """a protocol handler activator"""
    
    def master_id(self, master_info):
        """
        :type master_info: `narval.elements.base.MasterInformationsElement`
        :param master_info: element holding master's information

        :rtype: str
        :return: the master id for this protocol (if it applies)
        """

class IURL(Interface):
    """interface for url elements
        
    :ivar address: the URL string
    :ivar encoding: optional encoding of the file located at <address>
    """
    def normalize(self):
        """return the expanded normalized url string"""
    def protocol(self):
        """return the normalized url string"""
    def path(self):
        """return the expanded normalized path string"""

class IFile(IURL): 
    """interface for file elements
    
    :ivar data: data contained in the file
    :ivar mode: mode used to open the file
    :ivar type: file type
    """

class IData(Interface):
    """interface for data container elements
        
    :ivar data: data contained in the file
    """

class ICommand(Interface):
    """interface for command elements

    :ivar command: the name of the command
    :ivar args: a list of arguments to give to the command
    """

class IDictionary(Interface):
    """interface for dictionary elements
    """
    def __getitem__(self, item):
        """support access by subscription notation"""
    def __setitem__(self, item, value):
        """support modification by subscription notation"""
        

class ICategory(Interface):
    """interface for a named category
        
    :ivar name: the name of the category
    """


class IEvent(Interface):
    """interface for a calendar event
        
    :ivar from_time: the start time of the event (mx.DateTime object)
    :ivar to_time:
      the end time of the event (optional mx.DateTime object, maybe None)
      
    :ivar subject: the event's subject (should be short and descriptive)
    :ivar description: a long description
    
    :ivar location: the location of the event
    :ivar attendees: people attendees to the event
    """

class IOpen(Interface):
    """open anything and return a file-like object
    """
    def open(self):
        """return a readable file-like object"""


class ICommandInitiator(Interface):
    """base interface for elements which may initiate a command

    those elements should provide the following method to allow narval
    to give feedback about the command execution
    """
    def feedback(self, msg):
        """give feedback to the user who has triggered the command,
        whatever the used media

        :rtype: `IReplyable`
        :return: the element that will be used to give the feedback
        """

    
class IBaseIMessage(Interface):
    """interface for url elements
        
    :type type: str
    :ivar type: the type of the request, i.e. 'incoming' or 'outgoing'

    :type context: str
    :ivar context:
      the thread context identifier, according to the connection
      and to the sender (user or conference room.)

    :type activator_eid: int
    :ivar activator_eid: the eid of the jabber service activator
    """
    def build_reply(self, answer):
        """create a reply to an incoming request

        :rtype: `IIMessage`
        :return: a new message ready to be sent
        """
    def get_from(self):
        """return the the sender of the given message"""
    def get_to(self):
        """return the receiver of the given message"""
    def get_from_user(self):
        """return the user id of the sender of the given message, handling group
        chats
        """
    def get_to_user(self):
        """return the user id of the receiver of the given message, handling group
        chats
        """

class IIMessage(IBaseIMessage):
    def get_body(self):
        """return the content of the message"""
    
class IIPresence(IBaseIMessage):
    def get_status(self):
        """return the presence'status"""

    def set_status(self, status):
        """set the presence'status"""


class IEmail(Interface):
    """interface for electronic mail

    :ivar message_id: a unique identifier for this message
    :ivar from_name: sender's name
    :ivar from_address: sender's email address
    :ivar to: main recipients
    :ivar cc: carbon copy recipients
    :ivar subject: email's subject line
    :ivar body: email's content
    :ivar date: 
    :ivar type: incoming | outgoing
    :ivar content_type:
    """
    
    def build_reply(self, msg, all=False):
        """create a reply to the sender of this email, include all
        recipients if all==True

        :type msg: unicode
        :var msg: the reply body

        :type all: bool
        :var all: flag indicating whether other recipients should be cced

        :rtype:  `EmailElement`
        :return: an outgoing email
        """

    def from_rfc822(cls, msg, path=None):
        """class method to create a EmailElement from a email message as
        defined by the mailbox module from the python standard library
        """
    from_rfc822 = classmethod(from_rfc822)
    
class IPhoneCall(Interface):
    """interface for a phone call definition

    :ivar sip_uri: the sip address of the callee
    :ivar sentence: the sentence to tell to the callee
    """


# adapters ####################################################################

class AttrDelegatorAdapter(Adapter):
    """base adapter which proxies everything to a delegate, which is stored
    in a special attribute (other than 'original')
    """
    
    __sources__ = ()
    __implements__ = ()
    _delegate = None
    
    def __getattr__(self, attr):
        return getattr(self._delegate, attr)
    
    def __setattr__(self, attr, value):
        if attr == 'original' or attr == '_delegate':
            self.__dict__[attr] = value
        else:
            setattr(self._delegate, attr, value)


class IURLToIOpen(Adapter):
    """adapt IURL to IOpen"""
    __sources__ = (IURL,)
    __implements__ = (IOpen,)
        
    def open(self):
        """return a file-like object from an IURL object"""
        return urllib2.urlopen(self.original.normalize())


class ICommandToIIMessage(AttrDelegatorAdapter):
    """adapt elements with a 'from_msg' attribute to IIMessage"""
    
    __sources__ = (ICommand,)
    __implements__ = (IIMessage,)
    
    def __init__(self, original):
        # FIXME (syt): explain the lattest test...
        if not (original.from_msg and IIMessage in original.from_msg.__implements__):
            # FIXME: raise appropriate exception
            raise Exception()
        self._delegate = original.from_msg
        AttrDelegatorAdapter.__init__(self, original)
        

class ICommandToIIPresence(AttrDelegatorAdapter):
    """adapt elements with a 'from_msg' attribute to IIPresence"""
    
    __sources__ = (ICommand,)
    __implements__ = (IIPresence,)
    
    def __init__(self, original):
        if not (original.from_msg and IIPresence in original.from_msg.__implements__):
            # FIXME: raise appropriate exception
            raise Exception()
        self._delegate = original.from_msg
        AttrDelegatorAdapter.__init__(self, original)


class IErrorToIIMessage(ICommandToIIMessage):
    """adapt IError elements with a 'from_msg' attribute to outgoing IIMessage"""
    __sources__ = (IError,)
    __implements__ = (IIMessage,)
    
    def __init__(self, original):
        ICommandToIIMessage.__init__(self, original)
        self._delegate = original.from_msg.build_reply(original.msg)


class ToICommandInitiator(Adapter):
    """IIMessage and IEmail elements are ICommandInitiator"""
    __sources__ = (IIMessage, IEmail)
    __implements__ = (ICommandInitiator,)

    def __init__(self, original):
        Adapter.__init__(self, original)
        if not original.type == 'incoming':
            # FIXME: raise appropriate exception
            raise Exception()
    
    def feedback(self, msg):
        return self.original.build_reply(msg)


class ICommandToICommandInitiator(Adapter):
    """adapt ICommand elements to ICommandInitiator, through IIMessage"""

    __sources__ = (ICommand,)
    __implements__ = (ICommandInitiator,)

    def __init__(self, original):
        # If we can't adapt to IIMessage, don't go further
        self._msg = IIMessage(original)
        if not self._msg.type == 'incoming':
            # FIXME: raise appropriate exception
            raise Exception()
        Adapter.__init__(self, original)

    def feedback(self, msg):
        return self._msg.build_reply(msg)
    


## class IPhoneCallToICommand(AttrDelegatorAdapter):
##     """adapt IError elements with a 'from_msg' attribute to outgoing IIMessage"""
##     __sources__ = (IPhoneCall,)
##     __implements__ = (ICommand,)
    
##     def __init__(self, original):
##         Adapter.__init__(self, original)
##         if not getattr(original, 'from_cmd', None):
##             # FIXME: raise appropriate exception
##             raise Exception()
##         self._delegate = original.from_cmd
        
## class IDictionaryToIData(Adapter):
##     """adapt IDictionary to IData"""
##     __sources__ = IDictionary,
##     __implements__ = IData,

##     def __init__(self, original):
##         Adapter.__init__(self, original)
##         data_dict = original.as_dict()
##         self.data = '\n'.join(['  - %r : %s' % kv for kv in data_dict.items()])
    
