# -*- coding: utf-8 -*-

import os
import sys
import re
import StringIO


from genshi.input import HTMLParser,HTML
from genshi.input import XMLParser,XML
from genshi.template import  MarkupTemplate
from genshi.core import Stream,Attrs, QName, escape,unescape,Markup
from genshi.core import END, START, TEXT, COMMENT
from genshi.filters import HTMLSanitizer

from trac.wiki.formatter import Formatter
from trac.mimeview.api import Context
from trac.resource import *
from trac.wiki.parser import WikiParser

def bodytoxhtml(xbody):
    return u"""<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<body>
""" + xbody +"""
</body>
</html>
"""

def bodytowiki(xbody):
    return xhtmltowiki(bodytoxhtml(xbody))
    
def last_filter(stream):
    dataline = False
    lasttype = 'Start'
    preMode = False
    level = 0
    for kind, data, pos in stream:
        if kind is START:
            tag, attrs = data
            if data[0] in('L',):
                if lasttype in ('PE',):
                    yield TEXT,os.linesep,None
                    lasttype = 'RET'
                    dataline = False


            elif data[0] in('W',):
                if lasttype in('TEXT','WE',):
                    yield TEXT,' ',None
                lasttype = 'WS'
            elif data[0] in('LSpace',):
                yield TEXT,'  ',None
                lasttype = 'LSpace'
            elif data[0] in('WBar',):
                yield TEXT,'||',None
                lasttype = 'WBar'
            elif data[0] in('p',):
                if dataline == True:
                    yield TEXT,os.linesep,None
                    dataline = False
                if lasttype in ('RET','PE','TEXT','WE'):
                    for i in range(0,level):
                        yield TEXT, ">", None
                    if level >0:
                        yield TEXT, " ", None
                    yield TEXT,os.linesep,None
                    lasttype = 'PS'
                else:
                    yield TEXT,os.linesep,None
                    lasttype = 'PS'
                for i in range(0,level):
                    yield TEXT, ">", None
                if level >0:
                    yield TEXT, " ", None
            elif data[0] in('PreL',):
                preMode = True
                lasttype = 'PreLS'
                
                yield TEXT, attrs.get('data'), None               
            elif tag in('blockquote',) and attrs.get('class')=='citation':
                level += 1
        elif kind is TEXT:
            #if preMode == True:
            #    yield kind, data, pos               
            #else:
            if preMode == False:
                t = data
                t = t.replace('\r','')
                t = t.replace('\n','')
                if dataline == False:
                    t = t.lstrip()
                
                if t.rstrip()!='':
                    if lasttype in ('PE',):
                        yield TEXT,os.linesep,None
                    elif lasttype in('WE',):
                        if t[0] != ' ':
                            yield TEXT,' ',None
    
                    dataline = True
                    lasttype = 'TEXT'
                    yield kind, t.rstrip(), pos
                
        elif kind is END:
            tag = data
            if data in('L',):
                if dataline == True:
                    yield TEXT,os.linesep,None
                    lasttype = 'RET'
                    dataline = False
            if data in('p',):
                if dataline == True:
                    yield TEXT,os.linesep,None
                    lasttype = 'PE'
                    dataline = False
            elif data in('W',):
                lasttype = 'WE'

            elif data in('WBar',):
                pass
            elif data in('PreL',):
                yield TEXT,os.linesep,None
                preMode = False
                lasttype = 'PreLE'
            elif tag in('blockquote',) and level >0:
                level -= 1
        else:
            yield kind, data, pos

def pre_filter(stream):
    flag = False
    #pretext = ''
    for kind, data, pos in stream:
        if kind is START:
            if data[0] in ('pre',):
                pretext = ''
                flag = True
                yield START, (QName('p'),Attrs([])),None
                yield START, (QName('PreL'),Attrs([('data','{{{')])),None
                #yield TEXT, "{{{", pos
                yield END, QName('PreL'), None
            elif flag == True:
                pass
            else:
                yield kind, data, pos
        elif kind is TEXT:
            if flag == True :
                lines  = data.split('\n')
                for line in lines:
                    yield START, (QName('PreL'),Attrs([('data',line)])),None
                    #yield TEXT, line, None
                    yield END, QName('PreL'), None
                #pretext = pretext + data.replace('\r','@\r').replace('\n','@\n') +'@' 
            else:
                yield kind, data, pos
        elif kind is END:
            if flag==True and data in('pre',):
                yield START, (QName('PreL'),Attrs([('data','}}}')])),None
                #yield TEXT, "}}}", pos
                yield END, QName('PreL'), None
                yield END, QName('p'), None
                flag = False
                #pretext = ''
            elif flag == True:
                pass
            else:
                yield kind, data, pos
        else:
            yield kind, data, pos


def data_to_attr(data):
    return Attrs([('data',unescape(data.rstrip()))])

def pre_filter2(stream):
    start = False
    level = 0
    pretext = ''
    blocktags = ['p','table','div','h1','h2','h3','h4','h5','h6']
    wordtags = ['td','li','pre']
    
    for kind, data, pos in stream:
        if kind is START:
            tag, attrs = data
            if level == 0 and tag in ('blockquote',) and attrs.get('class') == None:
                pretext = ''
                level += 1
                yield START, (QName('p'),Attrs([])),None
                yield START, (QName('PreL'),data_to_attr('{{{')),None
                #yield TEXT, "{{{", pos
                yield END, QName('PreL'), None
            elif level > 0:
                if tag in ('blockquote',):
                    level +=1
                elif tag in wordtags:
                    if pretext.rstrip() != '':
                        yield START, (QName('PreL'),data_to_attr(pretext)),None
                        yield END, QName('PreL'), None
                    pretext = ''
                elif tag in blocktags:
                    if start == False:
                        start = True
                    else:
                        yield START, (QName('PreL'),data_to_attr('')),None
                        yield END, QName('PreL'), None
                    if pretext.rstrip() != '':
                        yield START, (QName('PreL'),data_to_attr(pretext)),None
                        yield END, QName('PreL'), None
                    pretext = ''

                elif tag in ('br',):
                    yield START, (QName('PreL'),data_to_attr(pretext)),None
                    yield END, QName('PreL'), None
                    pretext = ''
            else:
                yield kind, data, pos
        elif kind is TEXT:
            if level > 0 :
                pretext = pretext + data.replace('\r','').replace('\n','')  
            else:
                yield kind, data, pos
        elif kind is END:
            tag = data
            if level >0 :
                if tag in wordtags or tag in blocktags:
                    if pretext.rstrip() != '':
                        yield START, (QName('PreL'),data_to_attr(pretext)),None
                        yield END, QName('PreL'), None
                    pretext = ''
            if level > 1 and tag in('blockquote',):
                level -=1
            elif level == 1 and tag in('blockquote',):
                level = 0
                yield START, (QName('PreL'),data_to_attr('}}}')),None
                #yield TEXT, "}}}", pos
                yield END, QName('PreL'), None
                yield END, QName('p'), None
                pretext = ''
            else:
                yield kind, data, pos
        else:
            yield kind, data, pos

    
def h_filter(stream):
    flag = False 
    wikistr = {'h1':'=','h2':'==','h3':'===','h4':'====','h5':'=====','h6':'======','hr':'----'}  
    title = ''
    for kind, data, pos in stream:
        if kind is START:
            if flag==False:
                if data[0] in('h1','h2','h3','h4','h5','h6'):
                    flag = True
                elif data[0] in('hr'):
                    yield START, (QName('p'),Attrs([])),None
                    yield TEXT, "%s"%(wikistr[data[0]]), None
                    yield END, QName('p'), None
                else:
                    yield kind, data, pos
        elif kind is TEXT:
            if flag == True :
                title = title + data
            else:
                yield kind, data, pos
        elif kind is END:
            if flag==False:
                yield kind, data, pos
            elif data in('h1','h2','h3','h4','h5','h6'):
                title = title.rstrip()
                if title != '':
                    yield START, (QName('p'),Attrs([])),None
                    yield TEXT, "%s %s %s"%(wikistr[data],title,wikistr[data]), None
                    yield END, QName('p'), None
                title = ''
                flag = False
        else:
            yield kind, data, pos



def fontstyle_filter(stream):
    lasttext = ''
    trans = {'b':"\'\'\'",
             'strong':"\'\'\'",
             'i':"''",
             'em':"''",
             'del':"~~",
             'sup':"^",
             'sub':",,",
             'tt':"`",
             }
    p = re.compile( r"('''|''|'''''|\{\{\{|\}\}\}|__|\^|`)")
    for kind, data, pos in stream:
        if kind is START:
            if data[0] in trans.keys():
                yield START, (QName('W'),Attrs([])), None
                yield TEXT, trans[data[0]], None
            else:
                yield kind, data, pos
        elif kind is TEXT:
            if data.rstrip() != '':
                lasttext = data.rstrip()
            data = p.sub(r"!\1",data)
            yield kind, data, pos
        elif kind is END:
            if data in trans.keys():
                if lasttext[-1] == '!':
                    yield TEXT, " "+trans[data], None
                else:
                    yield TEXT, trans[data], None
                yield END, QName('W'), None
            else:
                yield kind, data, pos
        else:
            yield kind, data, pos

def br_filter(stream):
    for kind, data, pos in stream:
        if kind is START and data[0] in('br',):
            yield TEXT, "[[BR]]", None
        else:
            yield kind, data, pos

def table_filter(stream):
    tableLevel=0
    for kind, data, pos in stream:
        if kind is START:
            if data[0] in('table',):
                if tableLevel==0:
                    yield START, (QName('p'),Attrs([])), None
                tableLevel += 1
            elif tableLevel==0:
                yield kind, data, pos                   
            elif tableLevel==1:
                if data[0] in ('td',):
                    yield START, (QName('WBar'),Attrs([])), None
                    yield END, QName('WBar'), None
                elif data[0] in ('tr',):
                    yield START, (QName('L'),Attrs([])), None
            elif tableLevel >=2:
                pass
                
        elif kind is END:
            if tableLevel==0:
                yield kind, data, pos
            elif data in('table',):
                tableLevel -= 1
                if tableLevel==0:
                    yield END, QName('p'), None
            elif tableLevel==1:
                if data in ('td',):
                    pass
                elif data in ('tr',):
                    yield START, (QName('WBar'),Attrs([])), None
                    yield END, QName('WBar'), None
                    yield END, QName('L'), None
            elif tableLevel >=2:
                pass
        else:
            yield kind, data, pos

def discussion_filter(stream):
    level=0
    for kind, data, pos in stream:
        if kind is START:
            tag, attrs = data
            if tag in('blockquote',) and attrs.get('class')=='citation':
                level += 1
            
            elif tag in('L',) and level > 0:
                yield START, (QName('L'),Attrs([])), None
                for i in range(0,level):
                    yield TEXT, ">", None
                yield TEXT, " ", None
                
            else:
                yield kind, data, pos                   
        elif kind is END:
            tag = data
            if tag in('blockquote',) and level >0:
                level -= 1
            else:
                yield kind, data, pos
        else:
            yield kind, data, pos

def dl_filter(stream):
    level=0
    for kind, data, pos in stream:
        if kind is START:
            if data[0] in('dl',):
                if level==0:
                    yield START, (QName('p'),Attrs([])), None
                level += 1
            elif level==0:
                yield kind, data, pos                   
            elif level==1:
                if data[0] in ('dt',):
                    yield START, (QName('L'),Attrs([])), None
                    yield START, (QName('LSpace'),Attrs([])), None
                    yield END, QName('LSpace'), None
                elif data[0] in ('dd',):
                    yield START, (QName('L'),Attrs([])), None
                    yield START, (QName('LSpace'),Attrs([])), None
                    yield END, QName('LSpace'), None
                    yield START, (QName('LSpace'),Attrs([])), None
                    yield END, QName('LSpace'), None
                    yield START, (QName('LSpace'),Attrs([])), None
                    yield END, QName('LSpace'), None
            elif level >=2:
                pass
                
        elif kind is END:
            if level==0:
                yield kind, data, pos
            elif data in('dl',):
                level -= 1
                if level==0:
                    yield END, QName('p'), None
            elif level==1:
                if data in ('dt',):
                    yield TEXT, "::", None
                    yield END, QName('L'), None
                elif data in ('dd',):
                    yield END, QName('L'), None
            elif level >=2:
                pass
        else:
            yield kind, data, pos

def list_filter(stream):
    flag = False
    tagtype = ''
    olclass ={ None:'1','':'1','loweralpha':'a','lowerroman':'i'}
    classAtLevel = []
    endOfLevel = False
    for kind, data, pos in stream:
        if kind is START:
            tag, attrs = data
            endOfLevel = False
            if tag in('W',):
                yield kind, data, pos
            if tag in('ul','ol',):
                tagtype = tag
                classAtLevel.append(olclass[attrs.get('class')])
                if flag==True:
                    yield END, QName('L'), None
                flag = True
            elif flag==True and tag in('li',):
                yield kind, (QName('L'),Attrs([])), None
                for i in range(1,len(classAtLevel)):
                    #yield TEXT, "@@", None
                    
                    yield START, (QName('LSpace'),Attrs([])), None
                    yield END, QName('LSpace'), None
                yield START, (QName('LSpace'),Attrs([])), None
                yield END, QName('LSpace'), None
                yield START, (QName('W'),Attrs([])), None
                if tagtype == 'ol':
                    yield TEXT, "%s."%(classAtLevel[-1]), None
                else:
                    yield TEXT, "*", None
                yield END, QName('W'), None
            else:
                if flag==False:
                    yield kind, data, pos
                
                    
        elif kind is END:
            tag = data
            if tag in('W',):
                yield kind, data, pos
            elif flag==False:
                yield kind, data, pos
            else:
                if tag in('ul','ol',):
                    endOfLevel = True
                    classAtLevel.pop()
                    if len(classAtLevel)<=0:
                        flag = False

                elif data in('li',):
                    if endOfLevel == True:
                        endOfLevel = False
                    else:
                        yield kind, QName('L'), None
                else:
                    endOfLevel = False
        else:
            yield kind, data, pos
            
class WikiCancelFormatter(Formatter):
    def _bolditalic_formatter(self, match, fullmatch):
        return "!'''''%s!'''''"%match
    pass

class XhtmlToWikiFormatter(Formatter):
    
    
    
    req = None
    caller = None
    context = None
    temp_page = None
    
    def __init(self,env,context):
        self.wikiparser = WikiParser(env)        
        self.context = context
    
    def setReq(self,req):
        self.req = req
    def setCaller(self,comp):
        self.caller = comp
    def setTempPage(self,page):
        self.temp_page = page
    def xhtmltowiki(self,xhtml):
        stream = Stream(HTML(xhtml))
        sanitizer = HTMLSanitizer(
            safe_attrs=HTMLSanitizer.SAFE_ATTRS | set(['style'])
        )
        tradata= (stream | sanitizer|pre_filter|pre_filter2 | h_filter |self.a_filter|self.img_filter|
                  br_filter|fontstyle_filter | table_filter | list_filter |dl_filter| 
                  last_filter).render('xhtml', encoding=None)
        #tradata=tradata.replace('<P>','\n')
        #tradata=tradata.replace('</P>','\n')
    
        return Markup(tradata).unescape()

    def wiki_cancel_filter(self,stream):        
        preflag = False
        ttflag = False
        p = re.compile( r"('''|''|'''''|\{\{\{|\}\}\}|__|\^|`)")
        for kind, data, pos in stream:
            if kind is START:
                if data[0] in ('PreL',):
                    preflag = True
                elif data[0] in ('tt',):
                    ttflag = True
            elif kind is TEXT:
                if preflag == False and ttflag == False:
                    data = p.sub(r"!\1",data)
            elif kind is END:
                if preflag==True and data in('PreL',):
                    preflag = False
                elif ttflag ==True and data in('tt',):
                    ttflag = False
            yield kind, data, pos


    def url_to_traclink(self,url,title=None):
        #if title.find(' ') >0 :
        #    title = '"%s"'%title
        
        baseurl = self.req.abs_href()
        #hoge = url[:len(baseurl)]
        if url == None:
            return url
        
        if url[:len(baseurl)] == baseurl:
            afterurl = url[len(baseurl):]
            if afterurl[0] == '/':
                afterurl = afterurl[1:]
            sp = afterurl.split('/')
            if len(sp) >= 2:
                realm = sp[0]
                desc = '/'.join(sp[1:])
                if realm=='wiki' and title == desc:
                    return title
                elif realm=='ticket' and title == '#%s'%desc :
                    return title
                elif realm=='report' and title == '{%s}'%desc :
                    return title
                elif realm=='changeset' and title == 'r%s'%desc :
                    return title
                elif realm=='changeset' and title == '[%s]'%desc :
                    return title
                elif realm=='attachment':
                    return '%s:%s:%s:%s' %(realm,sp[1],'/'.join(sp[2:-1]),sp[-1]) 
                elif realm=='raw-attachment':
                    return '%s:%s:%s:%s' %('attachment',sp[1],'/'.join(sp[2:-1]),sp[-1]) 

                return "%s:%s"%(realm,desc)
                    
        return url


    def attachmenturl_to_traclink(self,url):
        filename = url.split('/')[-1]
        realm = self.context.resource.realm
        pageurl = self.req.abs_href ('/raw-attachment/%s/%s/%s'%(realm,self.context.resource.id,filename))
        if url == pageurl:
            # attachment in this page 
            return filename  ,'filename'      
        elif url == self.req.abs_href ('/raw-attachment/%s/%s/%s'%(realm,self.temp_page,filename)):
            # attachment in temp page 
            return filename ,'filename'
        
        return url,'url'

    def img_filter(self,stream):
        url = ''
        for kind, data, pos in stream:
            if kind is START and data[0] in ('img',):
                tag, attrs = data
                args = []
                src = attrs.get('src')
                args.append(self.attachmenturl_to_traclink(src)[0])
                if attrs.get('href') != None:
                    href = attrs.get('href')
                    srclink = self.url_to_traclink(src)
                    hreflink = self.url_to_traclink(href)
                    if href == src: 
                        # at new image with no samnaile
                        pass
                    elif hreflink == srclink:
                        # at edit wiki image with no samnaile
                        pass
                    else:
                        link,type = self.attachmenturl_to_traclink(href)
                        if type =='filename': 
                            # at new image and link to original image
                            link = 'attachment:%s:%s:%s'%(self.context.resource.realm,self.context.resource.id,link)
                        else: 
                            # at edit image with samneile or other link
                            link = hreflink
                        args.append('link=%s'%link)
                    
                yield START, (QName('W'),Attrs([])), None
                yield TEXT, '[[Image(%s)]]'%(', '.join(args)), None
                yield END, QName('W'), None
                
            else:
                yield kind, data, pos

    def a_filter(self,stream):
        url = ''
        titles = []
        flag = False
        ignore = False
        for kind, data, pos in stream:
            if kind is START:
                tag, attrs = data
                original = True
                if tag in('a',):
                    original = False
                    url = attrs.get('href')
                    flag = True
                elif flag == True: 
                    original = False
                    if tag in ('L','W','img',):
                        ignore = True
                        original = True
                    if tag in ('img',):
                        # add image to link url
                        data = (tag, attrs | [('href',url)] )
                else:
                    original = True                    
            elif kind is TEXT:
                original = True
                if flag == True and ignore == False:
                    titles.append(data)
                    original = False
            elif kind is END:
                tag = data
                original = True
                if tag in('a',):
                    if flag == True and ignore == False:
                        title = ''.join(titles)
                        traclink = self.url_to_traclink(url,title)
                        yield START, (QName('W'),Attrs([])), None
                        if traclink == title:
                            yield TEXT,title , None
                        else:    
                            yield TEXT,"[%s %s]"%(traclink,title) , None
                        yield END, QName('W'), None
                    flag = False
                    ignore = False
                    titles =[]
                    url=''
                    original = False
                elif flag == True:
                    original = False
            if original==True:
                yield kind, data, pos
