import binascii
import logging
import struct
import sys

from Products.statusmessages.message import Message
from Products.statusmessages.adapter import logger

def _encodeCookieValue(text, type, old=None):
    """Encodes text and type to a list of Messages. If there is already some old
       existing list, add the new Message at the end but don't add duplicate
       messages.
    """
    results = []
    message = Message(text, type=type)

    if old is not None:
        results = _decodeCookieValue(old)
    if not message in results:
        results.append(message)

    messages = ''.join([r.encode() for r in results])
    return binascii.b2a_base64(messages).rstrip()

def _decodeCookieValue(string):
    """Decode a cookie value to a list of Messages.
    """
    results = []
    # Return nothing if the cookie is marked as deleted
    if string == 'deleted':
        return results
    # Try to decode the cookie value
    try:
        value = binascii.a2b_base64(string)
        while len(value) > 1: # at least 2 bytes of data
            message, value = decode(value)
            if message is not None:
                results.append(message)
    except (binascii.Error, UnicodeEncodeError):
        logger.log(logging.ERROR, '%s \n%s',
                   'Unexpected value in statusmessages cookie',
                   sys.exc_value
                   )
        return []

    return results

def _utf8(value):
    if isinstance(value, unicode):
        return value.encode('utf-8')
    elif isinstance(value, str):
        return value
    return ''

def _unicode(value):
    return unicode(value, 'utf-8', 'ignore')

def encode(self):
    """
    Encode to a cookie friendly format.
    
    The format consists of a two bytes length header of 11 bits for the
    message length and 5 bits for the type length followed by two values.
    """
    message = _utf8(self.message)[:0x3FF] # we can store 2^11 bytes
    type = _utf8(self.type)[:0x1F]        # we can store 2^5 bytes
    size = (len(message) << 5) + (len(type) & 31) # pack into 16 bits
    
    return struct.pack('!H%ds%ds' % (len(message), len(type)), 
                       size, message, type)

def decode(value):
    """Decode messages from a cookie
    
    We return the decoded message object, and the remainder of the cookie
    value (it can contain further messages).
    
    We expect at least 2 bytes (size information).
    """

    if len(value) > 2:
        size = struct.unpack('!H', value[:2])[0]
        msize, tsize = (size >> 5, size & 31)
        message = Message(_unicode(value[2:msize+2]),
                          _unicode(value[msize+2:msize+tsize+2]))
        return message, value[msize+tsize+2:]
    return None, ''
    
from Products.statusmessages import adapter
from Products.statusmessages import message
Message.encode = encode
message.decode = decode
adapter._encodeCookieValue = _encodeCookieValue
adapter._decodeCookieValue = _decodeCookieValue

# Plone 2.5 specific code
from Products.CMFPlone.utils import getFSVersionTuple
if getFSVersionTuple()[:2] == (2, 5):
    from zope.app.publisher.interfaces.browser import IBrowserView

    HAS_GTS = True
    try:
        from Products.PageTemplates.GlobalTranslationService import \
            getGlobalTranslationService
    except ImportError:
        HAS_GTS = False

    if HAS_GTS:
        original = adapter.StatusMessage.addStatusMessage
        def addStatusMessage(self, text, type=''):
            """Add a status message.
            """
            gts = getGlobalTranslationService()
            parents = getattr(self.context, 'PARENTS', tuple())
            aq_context = None
            for parent in parents:
                # we want the first parent that isn't a view
                if IBrowserView.providedBy(parent):
                    continue
                aq_context = parent
                break
            text = gts.translate(None, text, context=aq_context)
            original(self, text, type)
        
        adapter.StatusMessage.addStatusMessage = addStatusMessage

        def __eq__(self, other):
             if not isinstance(other, Message):
                 return False
             if self.message == other.message and self.type == other.type:
                 return True
             return False
             
        Message.__eq__ = __eq__
