from Products.PortalTransforms.interfaces import itransform
from htmlentitydefs import entitydefs
import re

class WebIntelligentPlainTextToHtml:
    """Transform which replaces urls and email into hyperlinks"""

    __implements__ = itransform

    __name__ = "web_intelligent_plain_text_to_html"
    output = "text/html"

    def __init__(self, name=None, inputs=('text/x-web-intelligent',), tab_width = 4):
        self.config = { 'inputs' : inputs, 'tab_width' : 4}
        self.config_metadata = {
            'inputs' : ('list', 'Inputs', 'Input(s) MIME type. Change with care.'),
            'tab_width' : ('string', 'Tab width', 'Number of spaces for a tab in the input')
            }
        if name:
            self.__name__ = name
        
        self.urlRegexp = re.compile(r'((?:ftp|https?)://(localhost|([12]?[0-9]{1,2}.){3}([12]?[0-9]{1,2})|(?:[a-z0-9](?:[-a-z0-9]*[a-z0-9])?\.)+(?:com|edu|biz|org|gov|int|info|mil|net|name|museum|coop|aero|[a-z][a-z]))\b(?::\d+)?(?:\/[^"\'<>()\[\]{}\s\x7f-\xff]*(?:[.,?]+[^"\'<>()\[\]{}\s\x7f-\xff]+)*)?)', re.I|re.S|re.U)
        self.emailRegexp = re.compile(r'["=]?(\b[A-Z0-9._%-]+@[A-Z0-9._%-]+\.[A-Z]{2,4}\b)', re.I|re.S|re.U)
        self.indentRegexp = re.compile(r'^(\s+)', re.M|re.U)
            
    def name(self):
        return self.__name__

    def __getattr__(self, attr):
        if attr in self.config:
            return self.config[attr]
        raise AttributeError(attr)

    def convert(self, orig, data, **kwargs):
        # very long urls are abbreviated to allow nicer layout
        def abbreviateUrl(url, max = 60,  ellipsis = "[&hellip;]"):
            if len(url) < max:
                return url
            protocolend = url.find("//")
            if protocolend == -1:
                protocol = ""
            else:
                protocol = url[0 : protocolend+2]
                url = url[protocolend+2 : ]
            list = url.split("/")
            if len(list) < 3 or len(list[0])+len(list[-1] )>max:
                url = protocol + url
                center = (max-5)/2
                return url[:center] + ellipsis + url[-center:]
            
            return protocol + list[0] +"/" +ellipsis + "/" + list[-1]
        
        text = orig
        if not isinstance(text, unicode):
            text = unicode(text, 'utf-8', 'replace')
        
        # Do &amp; separately, else, it may replace an already-inserted & from
        # an entity with &amp;, so < becomes &lt; becomes &amp;lt;
        text = text.replace('&', '&amp;')
        # Make funny characters into html entity defs
        for entity, letter in entitydefs.items():
            if entity != 'amp':
                text = text.replace(letter.decode('latin-1'), '&' + entity + ';')
            
        # Replace hyperlinks with clickable <a> tags
        def replaceURL(match):
            url = match.groups()[0]
            linktext = abbreviateUrl(url)
            # In urls we need the revert our earlier change to the ampersands.
            # We do not want something like:
            # http://google.com/ask?question=everything&amp;answer=42
            url = url.replace('&amp;', '&')
            # Also with <some link> we should only link to some link, not
            # including the brackets.
            end = ''
            if url.endswith('&gt;'):
                url = url[:-len('&gt;')]
                linktext = linktext[:-len('&gt;')]
                end = '&gt;'

            # rel="nofollow" shall avoid spamming
            return '<a href="%s" rel="nofollow">%s</a>%s' % (url, linktext, end)
        text = self.urlRegexp.subn(replaceURL, text)[0]
        
        # Replace email strings with mailto: links
        def replaceEmail(match):
            url = match.groups()[0]
            # following unicode substitutions shall avoid email spam crawlers to pickup email addresses
            url = url.replace('@', '&#0064;');
            return '<a href="&#0109;ailto&#0058;%s">%s</a>' % (url, url)
        text = self.emailRegexp.subn(replaceEmail, text)[0]
    
        # Make leading whitespace on a line into &nbsp; to preserve indents
        def indentWhitespace(match):
            indent = match.groups()[0]
            indent = indent.replace(' ', '&nbsp;')
            return indent.replace('\t', '&nbsp;' * self.tab_width)
        text = self.indentRegexp.subn(indentWhitespace, text)[0]
        
        # Finally, make \n's into br's
        text = text.replace('\n', '<br />')
            
        data.setData(text.encode('utf-8'))
        return data

def register():
    return WebIntelligentPlainTextToHtml()
