from creoleparser import parse_args
from creoleparser.core import Parser, fragmentize
from creoleparser.dialects import create_dialect, creole11_base
from creoleparser.elements import InlineElement, WikiElement

from genshi import HTML
from genshi.filters import HTMLSanitizer

import genshi.builder as bldr
import logging
import re, os
import urllib, urlparse

def label_for_user(name):
    return name

def render_mbed_time(time):
    return time


sanitizer = HTMLSanitizer()
logger = logging.getLogger(__name__)

class EmbeddedMedia(InlineElement):
    """A modification of creole's Image class
    """

    def __init__(self, tag, token, delimiter):
        super(EmbeddedMedia, self).__init__(tag, token)
        self.regexp = re.compile(self.re_string())
        self.delimiter = delimiter
        self.src_regexp = re.compile(r'^\s*(\S+)\s*$')

    def _build(self, mo, element_store, environ):
        body = mo.group(1).split(self.delimiter, 3)
        body[0] = body[0].replace(' ', '%20')
        src_mo = self.src_regexp.search(body[0])

        if not src_mo:
            return bldr.tag.span('Bad Image src')
        if sanitizer.is_safe_uri(src_mo.group(1)):
            link = src_mo.group(1)
        else:
            link = ""
        if len(body) == 1:
            alias = link
        else:
            alias = body[1].strip()


        scalingw = ""
        if len(body) >= 3:
            scalingw = body[2].strip()


        scalingh = ""
        if len(body) == 4:
            scalingh = body[3].strip()
        #check for youtube
        youtube_re = re.compile(r'https?:\/\/www\.youtu[^"]+/?v=([\w-]+)')
        result = youtube_re.search(link)
        if result:
            if len(result.groups()) == 1:
                html = HTML("""<iframe width="420" height="315" src="https://www.youtube.com/embed/%s" frameborder="0" allowfullscreen></iframe>""" % result.groups()[0])
                return bldr.tag.div(html, class_="flex-video")

        # check for youtu.be
        youtube_re = re.compile(r'https?:\/\/youtu[^"]+/([\w-]+)')
        result = youtube_re.search(link)
        if result:
            if len(result.groups()) == 1:
                html = HTML("""<iframe width="420" height="315" src="https://www.youtube.com/embed/%s" frameborder="0" allowfullscreen></iframe>""" % result.groups()[0])
                return bldr.tag.div(html, class_="flex-video")

        if scalingw and scalingh:
            return bldr.tag.__getattr__(self.tag)(src=link, alt=alias, width=scalingw, height=scalingh)

        if scalingh:
            return bldr.tag.__getattr__(self.tag)(src=link, alt=alias, width=scalingw, height=scalingh)

        if scalingw:
            return bldr.tag.__getattr__(self.tag)(src=link, alt=alias, width=scalingw)

        return bldr.tag.__getattr__(self.tag)(src=link, alt=alias)




class MbedWikiLink(WikiElement):

    """Used to match wiki links inside a link.

    The search scope for these is only inside links.

    >>> wiki_link = WikiLink('a','|',base_url='http://somewiki.org/',
    ...                      space_char='_',class_func=None, path_func=None)
    >>> mo = wiki_link.regexp.search(" Home Page |Home")
    >>> wiki_link.href(mo)
    'http://somewiki.org/Home_Page'
    >>> wiki_link.alias(mo,{},None)
    ['Home']

    """

    def __init__(self, tag, delimiter,
                 base_url,space_char,class_func,path_func,wikislug):
        super(MbedWikiLink, self).__init__(tag, '')
        self.delimiter = delimiter
        self.base_url = base_url
        self.space_char = space_char
        self.class_func = class_func
        self.path_func = path_func
        self.regexp = re.compile(self.re_string())
        self.wikislug = wikislug

    def re_string(self):
        optional_spaces = ' *'
        page_name = r'(\S+?( \S+?)*?)' #allows any number of single spaces
        alias = r'(' + re.escape(self.delimiter) + r' *(.*?))? *$'
        return '^' + optional_spaces + page_name + optional_spaces + \
               alias

    def page_name(self,mo):
        return mo.group(1).replace(' ', self.space_char)

    def href(self,mo):
        if self.path_func:
            the_path = self.path_func(self.page_name(mo))
        else:
            the_path = urllib.quote(self.page_name(mo).encode('utf-8'))
        return urlparse.urljoin(self.base_url, the_path)

    def _build(self,mo,element_store, environ):
        #find if the page exists
        the_class = ''
        if environ:
            if environ.has_key('wikislug'):
                the_class="wiki-page"

        return bldr.tag.__getattr__(self.tag)(self.alias(mo, element_store, environ),
                                              href=self.href(mo),
                                              class_=the_class)

    def alias(self,mo,element_store, environ):
        """Returns the string for the content of the Element."""
        if not mo.group(3):
            return mo.group(1)
        else:
            return fragmentize(mo.group(4), self.child_elements, element_store, environ)



def creole_mbed_base(macro_func=None, **kwargs):
    """Our version of creole, inherits from 11_base"""
    wikislug = kwargs.pop('wikislug')

    Creole11Base = creole11_base(macro_func=macro_func, **kwargs)
    class Base(Creole11Base):
        def __init__(self):
            super(Base, self).__init__()

        base = ''

        from creoleparser.elements import Image
        img = EmbeddedMedia('img', ('{{','}}') ,delimiter='|')
        wiki_link = MbedWikiLink('a', delimiter='|',
                              base_url=base,
                              space_char="-",
                              class_func=None,
                              path_func=None,
                              wikislug=wikislug)

    return Base



class MbedParser:
    def __init__(self, wikislug=''):
        self.wikislug = wikislug
        self.parser = Parser(dialect=create_dialect(creole_mbed_base, macro_func=macro_func, wikislug=wikislug, add_heading_ids=True, wiki_links_space_char='-'), method='xhtml')

    def get_parser(self):
        return self.parser

    def parse(self, source):
        try:
            return self.parser(source, environ={'wikislug': self.wikislug})
        except Exception, e:
            logger.error(e)
            return "An error occured displaying this content."

class MbedTextParser:
    def __init__(self, wikislug=''):
        self.wikislug = wikislug
        self.parser = Parser(dialect=create_dialect(creole_mbed_base, macro_func=macro_func,wikislug=wikislug, add_heading_ids=True, wiki_links_space_char='-'), method='text')

    def get_parser(self):
        return self.parser

    def parse(self, source):
        return self.parser(source, environ={'wikislug':self.wikislug})

def macro_func(macro_name, arg_string, body, isblock, environ):
    """Matches a macro call in the wikitext to a real macro function"""
    try:
        pos, kw = parse_args(arg_string)

        kw['macro_name'] = macro_name
        kw['environ'] = environ
        if macro_name == 'float' and isblock and body:
            return macro_float_div(body,*pos,**kw)

        if macro_name == 'warning' and isblock and body:
            return macro_box(body,*pos,**kw)

        if macro_name == 'error' and isblock and body:
            return macro_box(body,*pos,**kw)

        if macro_name == 'info' and isblock and body:
            return macro_box(body,*pos,**kw)

        if macro_name == 'note' and isblock and body:
            return macro_box(body,*pos,**kw)

        if macro_name == 'pre' and isblock and body:
            return macro_box(body,*pos,**kw)

        if macro_name == 'code' and isblock and body:
            return macro_box(body,*pos,**kw)

        if macro_name == 'success' and isblock and body:
            return macro_box(body,*pos,**kw)

        if macro_name == 'help' and isblock and body:
            return macro_box(body,*pos,**kw)

        if macro_name == 'quote' and isblock and body:
            return macro_quote(body,*pos,**kw)

        if macro_name == 'comment' and isblock and body:
            return macro_comment(body,*pos,**kw)

        if macro_name == 'clear' and isblock and body:
            return macro_clear_div(body, *pos, **kw)

        if macro_name == 'program':
            return macro_program(body, *pos, **kw)

        if macro_name == 'library':
            return macro_program(body, *pos, libmode=True, **kw)

        if macro_name == 'repo':
            return macro_mercurial_repo(body, *pos, **kw)

        if macro_name == 'component':
            return macro_component(body, *pos, **kw)

        if macro_name == 'team':
            return macro_team(body, *pos, **kw)

        if macro_name == 'user':
            return macro_user(body,*pos,**kw)

        if macro_name == 'legacyapidocs':
            return macro_apidocs_legacy(body, *pos, **kw)
        if macro_name == 'gallery' and isblock and body:
            return macro_gallery(body, *pos, **kw)
        if macro_name == 'upverter':
            return macro_upverter(body, *pos, **kw)
        if macro_name == 'notoc':
            return macro_notoc(environ)
    except Exception, e:
        #don't throw an exception for macro bugs!
        raise e
        logging.exception(e)
        return '[An error occured while processing this macro]'





#=============== Macros

def macro_user(body, *pos, **kw):
    link = '[User_Macro_Error]'

    return bldr.tag.span(link)

def macro_notoc(environ):
    environ.setdefault('notoc', True)
    return ''

def macro_gallery(body,size="default", *pos, **kw):
    """
    [[http://mbed.org/cookbook/GlobalSat-EM-406-GPS-Module|{{http://mbed.org/media/uploads/simon/gpsproduct.jpg}}]]
    [[http://mbed.org/cookbook/XBee|{{http://mbed.org/media/uploads/simon/_scaled_xbee.png|XBee}}]]
    {{http://mbed.org/media/uploads/simon/_scaled_xbee.png|XBee}}
    {{http://mbed.org/media/uploads/simon/_scaled_xbee.png}}
    """
    args = body.split('\n')
    logging.debug(args)
    ul_list = bldr.tag.ul(class_="polaroids "+size)
    # import ipdb; ipdb.set_trace()
    try:
        for row in args[:-1]:
            if row.find("[[") != -1:
                #find anchor link
                row = row[row.find("[[") + 2:row.find("]]")].split('|',1)
                logging.debug("anchor %s" % row)
                row_link = row[0]
                row = row[1]
                logging.debug(row)
                image = row[row.find("{{") + 2:row.find("}}")].split('|')
                logging.debug(image)
                image_url = image[0]
            else:
                #find image link

                image = row[row.find("{{") + 2:row.find("}}")].split('|',1)
                logging.debug("image %s" % image)
                row_link = None
                image_url = image[0]
            if len(image) < 2:
                title = urlparse.urlsplit(image[0]).path.split('/')[-1]
            else:
                title = image[1]
            if row_link:
                content = bldr.tag.a(bldr.tag.img(src=image_url), title=title, href=row_link)
            else:
                content = bldr.tag.a(bldr.tag.img(src=image_url),title=title)
            ul_list.append(bldr.tag.li(content))
    except Exception, e:
        logging.exception(e)
        return '[An error occured while processing this macro]'
    return ul_list



def macro_upverter(body, *pos, **kw):
    src = 'https://upverter.com/eda/embed/'+body
    return bldr.tag.iframe(src=src,frameborder="0",scrolling="no",width="99%",height="460")


def macro_comment(body, *pos,**kw):

    return ''

def macro_quote(body,username='',*pos,**kw):
    contents = MbedParser().get_parser().generate(body, environ=kw['environ'], final=False)

    if username:
        link = '%s wrote:'%username
    else:
        link = 'Quote:'

    newcontents = bldr.tag.h4(link , class_='ftitle') + bldr.tag.p(contents)
    return bldr.tag.div(newcontents, class_='flashbox fquote')

def macro_code(body,*pos,**kw):
    contents = body
    return bldr.tag.pre('test', style='font-weight: bold;')

def macro_pre(body,*pos,**kw):

    contents = body
    return bldr.tag.pre(contents, style='none')

def macro_box(body,title=None,*pos,**kw):
    if not title:
        if kw['macro_name'] == 'warning':
            title = 'Warning'
        if kw['macro_name'] == 'info':
            title = 'Information'
        if kw['macro_name'] == 'error':
            title = 'Error'
        if kw['macro_name'] == 'note':
            title = 'Note'
        if kw['macro_name'] == 'code':
            title = ' '
        if kw['macro_name'] == 'success':
            title = 'Success'
        if kw['macro_name'] == 'help':
            title = 'Help'


    if kw['macro_name'] == 'code' or kw['macro_name'] == 'pre':
        offset = kw.get('offset', "0")
        contents = bldr.tag.pre(bldr.tag.code(body.strip()), class_="mbed-code", offset=offset)
    else:
        contents = MbedParser().get_parser().generate(body, environ=kw['environ'],final=False)

    if title:
        contents = bldr.tag.h4(title, class_='ftitle')+ contents

    return bldr.tag.p(contents, class_='flashbox f%s'%kw['macro_name'])

def macro_float_div(body,side='right', *pos,**kw):
    if side not in ('left','right'):
        return None
    contents = MbedParser().get_parser().generate(body, environ=kw['environ'],final=False)
    return bldr.tag.div(contents, style='padding: 10px; float:'+side)

def macro_clear_div(body,*pos,**kw):
    contents = MbedParser().get_parser().generate(body, environ=kw['environ'],final=False)
    return bldr.tag.div(contents, style='clear:left')

def macro_apidocs_legacy(body, *pos, **kw):
    """Link to include a snippet of the legacy apidocs."""
    from urlparse import urlparse
    import os

    path = pos[0]
    #path will be something like '/mbed/trunk/Ethernet.h'

    output = '[error loading apiddocs_legacy api]'

    return bldr.tag.div(HTML(output))


def create_import_link(repo):
    style = "font-weight: bold; position: relative; float: right;"

    if repo.is_library:
        importlink = bldr.tag.a(HTML('Import library'), style=style, class_="button small radius" , target="compiler", href=repo.get_import_url())
    else:
        importlink = bldr.tag.a(HTML('Import program'), style=style, class_="button small radius", target="compiler", href=repo.get_import_url())
    return importlink


def create_repo_container(repo, header, content, show_footer=True):
    importlink = create_import_link(repo)

    container = bldr.tag.h4(importlink, header, class_='ftitle')
    container += content

    if show_footer:
        user = bldr.tag.span(HTML((label_for_user(repo.user, icon=True))))
        container += bldr.tag.p(' Last commit ', HTML((render_mbed_time(repo.lastchange_details['datetime'] ))), ' by ', user, class_="wiki-api-footer")

    if repo.is_library:
        container_classes = "flashbox flibrary"
    else:
        container_classes = "flashbox fprogram"

    return bldr.tag.div(container, class_=container_classes)


def macro_program(body, *pos, **kw):
    """Link to a program, rendering some description and other info"""
    try:
        apimode = False
        latestmode = False
        progurl = urlparse.urlparse(pos[0]).path

        if progurl.endswith('.html'):
            apimode = True

            #logging.debug("Docs path: %s"%docspath)
            #logging.debug("Prog url: %s"%progurl)

        resolved = progurl
        logging.debug("Resolved url %s as %s, NAME: %s" % (progurl, resolved, resolved.url_name))
        #attempt to get db object of pub proj
        rev = 'tip'
        res = []
        if resolved.url_name in ['repo_docs', 'repo_detail', 'repo_docs_page']:
            (repo, res) = (progurl, {})
            if "rev" in res:
                rev = res['rev']
                #TODO do stuff with rev

        else:
            if not len(res):
                logging.error("Old style published program: not found. URL was %s"%progurl)
                return bldr.tag.p('[Not found]')
            else:
                logging.error("%s is not converted yet, so cant display docs"%res[0])
                return bldr.tag.p('[Not converted]')

        if apimode:
            from mbed.repositories.docmaker import get_repo_docs
            import os
            path = get_repo_docs(repo=repo, rev=rev)

            docspath = '/'.join(progurl.split('/')[7:])
            logging.debug("Docspath: %s" % docspath)
            snippet = 'No documentation found.'
            try:
                snippet = open(os.path.join(path, 'html', docspath + '.snippet'), 'r').read()
                #TODO get .title too
            except IOError:
                pass
                #logging.debug("Could not find api snippet")
            header = repo.name
            try:
                header = open(os.path.join(path, 'html', docspath + '.header'), 'r').read()

            except IOError:
                pass
                #logging.debug("Could not find api header")

            return create_repo_container(repo, bldr.tag.a(bldr.tag.span(HTML((header))), href=pos[0]), bldr.tag.div(HTML((snippet))), show_footer=False)
        else:
            link = bldr.tag.a(repo.name, href=repo.get_absolute_url())
            return create_repo_container(repo, link, bldr.tag.p(repo.description))

    except Exception, e:
        logging.exception(e)
        return bldr.tag.p('[Not found]')


def macro_mercurial_repo(body, *pos, **kw):
    from mercurial.hgweb import hgweb
    from mbed.repositories.proxy import HgRequestWrapper
    from mbed.repositories import settings as hgwebproxy_settings
    from mbed.repositories.views import show_repo_embed
    from django.utils.encoding import smart_unicode
    from mercurial import templater
    import os.path

    progurl = urlparse.urlparse(pos[0]).path

    return bldr.tag.div('[Cannot do macro_mercurial_repo]')

def macro_component(body, *pos, **kw):
    try:
        path = urlparse.urlparse(pos[0]).path
        resolved  = path
        slug = resolved[2]['slug']
        com = "NoComponentsSupported"
        thumb = ''
        #FIXME
        #get_thumbnail(os.path.join(settings.MEDIA_ROOT,com.photo.name), '60x60', quality=99, crop="center").url
        html = """

        <div style="margin-left: 10px;">
            <div style="font-size: 1.3em; padding-bottom: 4px;">
                <div style="float: left; padding-right: 5px;"> <img src="%s"/></div>
                <b>
                    <a href="%s">Components / %s</a></br>

                </b>
            </div>
            <div class="margin-top: 0px;">
                %s
            </div>
        </div>
        <div style="clear: left"></div>
"""%(thumb, com.get_absolute_url(), com.name, com.short_description)

        content = HTML(html)
    except Exception, e:
        logging.exception(e)
        content = '[error]'

    return bldr.tag.span(content)


def macro_team(body, *pos, **kw):
    try:
        path = urlparse.urlparse(pos[0]).path
        resolved  = path
        slug = resolved[2]['group_slug']
        team = "TeamNotSupported"
        thumb = "error.gif"
        html = """

        <div style="margin-left: 10px;">
            <div style="font-size: 1.3em; padding-bottom: 4px;">
                <div style="float: left; padding-right: 5px;"> <img src="%s"/></div>
                <b>
                    <a href="%s">Team / %s</a></br>

                </b>
            </div>
            <div class="margin-top: 0px;">
                %s
            </div>
        </div>
        <div style="clear: left"></div>
"""%(thumb, team.get_absolute_url(), team.name, team.description)

        content = HTML(html)
    except Exception, e:
        logging.exception(e)
        content = '[error]'

    return bldr.tag.span(content)


