"""$URL: svn+ssh://svn.mems-exchange.org/repos/trunk/dulcinea/lib/util.py $
$Id: util.py 25701 2004-12-02 22:19:47Z rmasse $
"""
import re
import time
from htmllib import HTMLParser
from formatter import AbstractFormatter, DumbWriter
from StringIO import StringIO
from datetime import datetime
from distutils.fancy_getopt import wrap_text
from quixote.html import htmlescape, htmltext

SAFE_TAGS = ['<p>', '</p>', '<b>', '</b>', '<i>', '</i>', '<ul>',
             '</ul>', '<li>', '</li>', '<br>', '<pre>', '</pre>',
             '<strong>', '</strong>']

def htmlescape_except(text, safe_tags=None):
    """(text : str, safe_tags : [str] = None) -> str

    Quotes 'text' for use in an HTML page, except for the tags
    listed in 'safe_tags' which are considered safe (e.g. <p>, <b>).
    Both upper and lower case versions of the tags will be applied
    """
    if safe_tags is None:
        safe_tags = SAFE_TAGS
    text = str(htmlescape(text))
    for tag in safe_tags:
        tag_re = re.compile(str(htmlescape(tag)), re.IGNORECASE)
        text = tag_re.sub(tag, text)
    return htmltext(text)

# re to automatically hyperlink email addresses and URLs
_link_re = re.compile(r"""\b(
    # email address
      [\w.-]+ # local part
      @
      [\w.-]+\.[\w]{2,4} # domain
    |
    # URL
      (?:https?://|www\.) # must start with http or www
      [\w.-]+\.[\w]{2,4} # domain
      (?::\d+)? # optional port
      (?:/[\w#$%&+,-./:;=?@\[\]^_|~]*)? # optional path
   )""", re.VERBOSE)

def activate_links(text):
    """(text:str|None) -> htmltext|None
    Returns an htmlescaped version of text, with things that look like email
    addresses and URLs turned into hyperlinks.
    """
    def _link_replace(m):
        text = url = m.group(0)
        extra = ""
        if text.find("@") == -1:
            if text[-1] in ".,":
                # don't include as part of the URL (easier to handle here
                # than in the regex)
                extra = text[-1]
                text = text[:-1]
            if not text.startswith("http"):
                url = "http://" + text
        else:
            if not text.startswith("mailto"):
                url = "mailto:" + text
        return '<a href="%s">%s</a>%s' % (url, text, extra)

    if text is None:
        return None
    return htmltext(_link_re.sub(_link_replace, str(htmlescape(text))))


def format_text(text, safe_tags=None):
    """(text : string, safe_tags : [str]) -> htmltext

    Convert an ASCII string containing text into HTML.
    The resulting HTML has been reformatted in the following ways:
       * <, &, and > are escaped
       * Things that look like email addresses and URLs will be turned into
         hyperlinks.
       * If safe_tags is defined, it is expected to be a list of tags
         considered to be safe (e.g. <p>, <b>, etc).  These tags will
         not be escaped.
    """
    return activate_links(htmlescape_except(text, safe_tags=safe_tags))

def sanitize_url(url):
    """(url:string) -> string
    Try to ensure a URL is well-formed, by adding http:// if it isn't present.
    """
    if url is None:
        return None
    if '@' in url and not url.startswith('mailto:'):
        # assume it's an e-mail address
        url = "mailto:" + url
    elif url.find(":") == -1:
        # assume http:// is missing
        url = "http://" + url
    return url

_paragraph_re = re.compile('\n\n+')

def split_paragraphs(text):
    return _paragraph_re.split(text)

def wrap_paragraphs(text):
    """(text) -> string
    Wrap a sequence of paragraphs for output as plain text.
    """
    if text is None:
        return ""
    line_length = 70
    return '\n\n'.join(['\n'.join(wrap_text(paragraph, line_length))
                        for paragraph in split_paragraphs(text)])

def insert_paragraph_tags(text):
    """(text:string|None) -> htmltext|None

    Prepare a text field for display as HTML.
    Currently this just HTML quotes the string and then inserts
    <p> tags at blank lines.  
    """
    if text is None:
        return None
    return htmltext('\n<p>' +
                    _paragraph_re.sub('</p>\n<p>', str(htmlescape(text))) +
                    '</p>')

_ms_character_substitutions = [
    ('\x92', "'"),
    ('\x93', '"'),
    ('\x94', '"'),
    ('\x96', '-'),
    ('\x99', '(tm)')]

def replace_characters(s, substitutions=None):
    """(s: str|None) : str
    Turn various characters into their plain ASCII equivalents.
    """
    if s:
        if substitutions is None:
            substitutions = _ms_character_substitutions
        for old, new in substitutions:
            s = s.replace(old, new)
    return s

def datetime_to_int(date_time):
    """(date_time:datetime) -> int
    Returns the number of seconds since the epoch.
    """
    return int(time.mktime(date_time.timetuple()))

def beginning_of_next_month(date_time):
    """(date_time:datetime) -> datetime
    Return a datetime for the exact beginning of the month
    following the given date_time.  
    """
    year = date_time.year
    month = date_time.month + 1
    if month == 13:
        year += 1
        month = 1
    return datetime(year=year, month=month, day=1, hour=0, minute=0,
                    second=0, microsecond=0)

def html2txt(text):
    """(any) -> str
    """
    sio = StringIO()
    parser = HTMLParser(AbstractFormatter(DumbWriter(sio)))
    parser.feed(str(text))
    return sio.getvalue()
