#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# XHTMLのbody部を受け取り
# いろんな処理を行う．
# 
# (2006,11/05)とりあえず、今のところ
# idとclass属性の処理だけ入れている。
# 
# (2008,01/18) <p>要素中のテキストノードで改行があり，
# かつ改行の前後が日本語ならば改行を削除する処理を入れた．
# 
# (2008,08/28) <h?>要素があったらそのヘッダのスコープを
# <div>で囲む処理を追加．ついかした<div>には自動で
# id="mm-div-2-3"などのid属性が付く．("mm-div-2-3"は
# 2つめの<h1>から数えて3つめの<h2>要素のスコープを
# 囲む<div>の場合のid)
# ただし，この処理は以前からあるid付加の処理の前に行う
# ので@(id1,overwrite)などで上書き可能である．
# 
# (2008,08/28の2) まず属性追加の書式を@(abc:n,xyz)と
# 拡張することにしてabcのところには任意の属性名を指定可能
# にする。そしてその後の:nは何段外の要素に属性を付けるかの
# 数字を指定することにして、省略可能で無い時は:0と見なされる。
# さらに、mmPostProcess3としてmixedmark="1"の属性がある要素の
# 中身は再帰的にmm2xhtmlを呼び出して中身を処理するようにした。
# (markdown extraのmarkdown="1"のまね)
# だいたいは上手くいくようになった。ただ、確認したところたぶん
# markdown2.pyのバグらしき挙動で上手くいかないことがある。
# それでもmixedmark="1"の要素の中の半角スペースを工夫すると上手く
# いくことがある。
# 
#現在、エンティティの処理方法が上手くコントロール
#できないので、ヘナチョコなプログラムになっている。
#つまり、エンティティをエンティティでなくしてしまって
#処理して、後で戻している。ヘナチョコな所にヘナチョコ印
#を付けておく。

from xml.dom.minidom import *
from make_mm_dom import make_mm_dom
import mm2xhtml.MixedMark
import re

#他でも使うのでDOMのDocumentオブジェクトをグローバル
#変数に入れとくことにする。
#doc = None

#外から呼び出すのはこのメソッドだけということに
#なると思う。XHTMLのbodyの文字列を受けとり
#さまざまなDOM処理を行う。
#idとclass属性の処理と<p>要素中の改行削除の処理と
#<div>要素追加の処理に分けている．
def mmPostProcess(body):

    ret = mmPostProcess1(body)
    ret = mmPostProcess2(ret)
    ret = mmPostProcess3(ret)

    return ret

# -------------------------------------------------------------------------

# nodeが<h1>とか<h2>とかのヘッダ要素かどうか判定する
# ヘッダでない要素の場合には0を返し
# <h1>の時は1を返し<h2>の時は2を返す．以下<h6>まで同様
def isHn(node):
    if node.nodeType != xml.dom.minidom.Node.ELEMENT_NODE:
        return 0
    if node.tagName[0]!='h':
        return 0
    if node.tagName=='h1':
        return 1
    if node.tagName=='h2':
        return 2
    if node.tagName=='h3':
        return 3
    if node.tagName=='h4':
        return 4
    if node.tagName=='h5':
        return 5
    if node.tagName=='h6':
        return 6
    return 0


#ヘッダ要素が何回出てきているかカウントするための変数
#hnCount[0]はダミーでhnCount[1]がh1のカウンタ，
#hnCount[1]がh2のカウンタ，とh6まで続く
hnCount = [0,0,0,0,0,0,0]
#各レベルのヘッダのカレントを保存する
div = [None,None,None,None,None,None,None]

#レベル(lev)の<div>要素を作成
def makeDiv(lev,doc):
    global hnCount
    global div

    id = 'mm-div'
    for i in range(1,lev+1):
        id = id + '-' + str(hnCount[i])
    newDiv = doc.createElement('div')
    newDiv.setAttribute('id',id)
    div[lev] = newDiv
    div[lev-1].appendChild(doc.createTextNode("\n"))
    div[lev-1].appendChild(newDiv)
    div[lev-1].appendChild(doc.createTextNode("\n"))

#<div>要素追加の処理．
def mmPostProcess1(body):
    global hnCount
    global div
    hnCount = [0,0,0,0,0,0,0]
    div = [None,None,None,None,None,None,None]

    #変換元DOM
    body = body.replace('&',u'＆') #ヘナチョコ印
    doc = make_mm_dom(body.encode("utf-8"))
    #doc.documentElement.firstChild.normalize()

    #変換先DOM
    domImpl = xml.dom.minidom.getDOMImplementation()
    retDoc = domImpl.createDocument(None,"retBody",None)

    div[0] = retDoc.documentElement

    level = 1

    for node in doc.documentElement.firstChild.childNodes:
        #if node.nodeType ==xml.dom.minidom.Node.TEXT_NODE:
        #    if node.data.strip() == "":
        #        continue

        nType = isHn(node)
        if nType==0:
            if div[level]==None:
                div[level-1].appendChild(node.cloneNode(-1))
            else:
                div[level].appendChild(node.cloneNode(-1))
        else:
            newLevel = nType

            hnCount[newLevel] = hnCount[newLevel] + 1
            for i in range(newLevel+1,6):
                hnCount[i] = 0
            for i in range(newLevel,6):
                div[i] = None

            #しょっぱなが<h2>とか<h3>の時だけどうしても必要？
            if level==1 and div[level]==None and newLevel>1:
                hnCount[1] = hnCount[1] + 1
                makeDiv(1,retDoc)

            if (newLevel>(level+1)):
                for i in range(level+1,newLevel):
                    hnCount[i] = hnCount[i] + 1
                    makeDiv(i,retDoc)

            makeDiv(newLevel,retDoc)
            div[newLevel].appendChild(retDoc.createTextNode("\n"))
            div[newLevel].appendChild(node.cloneNode(-1))

            level = newLevel

    ret = ''
    topLevelNode = retDoc.documentElement.firstChild
    while (topLevelNode):
        ret = ret + topLevelNode.toxml('UTF-8')
        topLevelNode = topLevelNode.nextSibling

    ret = ret.replace('＆','&')         #ヘナチョコ印
    ret = ret.replace('&amp;gt;','>')   #ヘナチョコ印
    ret = ret.replace('&amp;quot;','"') #ヘナチョコ印
    #ret = ret.replace('&amp;apos;',"'") #ヘナチョコ印

    doc.unlink()
    retDoc.unlink()

    return ret

# -------------------------------------------------------------------------

#idとclass属性の処理と<p>要素中の改行削除の処理．
def mmPostProcess2(body):
    body = body.replace('&',u'＆') #ヘナチョコ印
    doc = make_mm_dom(body.encode("utf-8"))
    #doc.documentElement.firstChild.normalize()

    nodes = doc.documentElement.firstChild.childNodes
    for n in nodes:
        traverse(n,doc)

    ret = ''
    topLevelNode = doc.documentElement.firstChild.firstChild
    while (topLevelNode):
        ret = ret + topLevelNode.toxml('UTF-8')
        topLevelNode = topLevelNode.nextSibling

    doc.unlink()

    ret = ret.replace('＆','&')         #ヘナチョコ印
    ret = ret.replace('&amp;gt;','>')   #ヘナチョコ印
    ret = ret.replace('&amp;quot;','"') #ヘナチョコ印
    #ret = ret.replace('&amp;apos;',"'") #ヘナチョコ印

    return ret


#「@(...)」の形式にマッチしたもが見付かった時に
#呼び出される。グローバル変数のdocとcurrentElement
#を利用しつつ処理を行う。
def atMatch(m):
    global currentElement
    s = m.group(1),
    ss = s[0].split(',')
    sss = ss[0].split(':')
    if len(sss)==1:
        level = 0
    else:
        level = int(sss[1])
    attributeName = sss[0]
    attributeValue = ss[1]

    n = currentElement
    for i in range(level):
        n = n.parentNode

    n.setAttribute(attributeName,attributeValue)
    return ''

p = re.compile(u"@\\((.*?)\\)")
j=u"([\u3000-\u30ff\u4e00-\u9fff\u3400-\u4dbf\uff0c\uff0e])"
pp = re.compile(j+u"\n"+j)

currentElement = None

#再帰的に木を探索し処理するためのメソッド。
#今のところ、テキストノードが見つかったら
#その中の「@(...)」を処理させているだけ。
#ただ、例外的に<img>要素のalt属性も
#処理する。
def traverse(n,doc):
    global currentElement
    if n.nodeType == Node.ELEMENT_NODE:
        currentElement = n
        if n.localName=="img":
            s = n.getAttribute('alt')
            s = p.sub(atMatch,s)
            n.setAttribute('alt',s)
        else:
            for nn in n.childNodes:
                traverse(nn,doc)
    elif n.nodeType == Node.TEXT_NODE:
        #s = n.toxml('UTF-8')
        s = n.toxml()
        s = p.sub(atMatch,s)

        #<p>要素中のテキストノードで改行があり，
        #かつ改行の前後が日本語ならば改行を削除する
        if n.parentNode.nodeType == Node.ELEMENT_NODE:
            if n.parentNode.localName=="p":
                s = pp.sub(u"\\1\\2",s)

        nn = doc.createTextNode(s)
        n.parentNode.replaceChild(nn,n)

# -------------------------------------------------------------------------

# 属性にmixedmark="1"がある要素の中身を
# 再帰的に処理する
# 原因は不明だが、現在のところmixedmark="1"の
# 要素を入れ子にするのはだめっぽい
def mmPostProcess3(body):
    body = body.replace('&',u'＆') #ヘナチョコ印
    doc = make_mm_dom(body.encode("utf-8"))

    nodes = doc.documentElement.firstChild.childNodes
    for n in nodes:
        traverse3(n,doc)

    ret = ''
    topLevelNode = doc.documentElement.firstChild.firstChild
    while (topLevelNode):
        ret = ret + topLevelNode.toxml('UTF-8')
        topLevelNode = topLevelNode.nextSibling

    doc.unlink()

    ret = ret.replace('＆','&')         #ヘナチョコ印
    ret = ret.replace('&amp;gt;','>')   #ヘナチョコ印
    ret = ret.replace('&amp;quot;','"') #ヘナチョコ印
    #ret = ret.replace('&amp;apos;',"'") #ヘナチョコ印

    return ret

#再帰的に木を探索しmixedmark="1"を見付けて
#処理するためのメソッド。実際の処理はreplaceMMでやる
def traverse3(n,doc):
    if n.nodeType == Node.ELEMENT_NODE:
        s = n.getAttribute('mixedmark')
        if s == '1':
            replaceMM(n,doc)
        else:
            for nn in n.childNodes:
                traverse3(nn,doc)

# 与えられたnodeの中身をmm2xhtml処理して
# 置き換える
def replaceMM(n,doc):
    org = ''
    topLevelNode = n.firstChild
    while (topLevelNode):
        org = org + topLevelNode.toxml('UTF-8')
        nextSibling = topLevelNode.nextSibling
        n.removeChild(topLevelNode)
        topLevelNode.unlink()
        topLevelNode = nextSibling

    gen = mm2xhtml.MixedMark.mm2xhtml(org)
    genxml = make_mm_dom(gen.encode("utf-8"))

    topLevelNode = genxml.documentElement.firstChild.firstChild
    while (topLevelNode):
        n.appendChild(topLevelNode.cloneNode(-1))
        topLevelNode = topLevelNode.nextSibling

    genxml.unlink()

