package com.qmth.qrzk.repository.utils.docx;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

import com.qmth.qrzk.repository.utils.General;

public class MathMlDeal {
	private static ThreadLocal<Boolean> isAllMi = new ThreadLocal<Boolean>();
	private static ThreadLocal<Boolean> isOpEmu = new ThreadLocal<Boolean>();
	public static StringBuffer toMathMlStr(Element oMath){
		Document document = DocumentHelper.createDocument();
		Element root = document.addElement("math","http://www.w3.org/1998/Math/MathML");
		root.addAttribute("display", "block");
		isAllMi.set(false);
		isOpEmu.set(false);
		dealWordMathNode(root,oMath);
		return new StringBuffer(document.asXML());
	}
	
	private static void dealWordMathNode(Element mathEle,Element wordEle){
		Attribute argSzVal = (Attribute) wordEle.selectSingleNode("m:argPr/m:argSz/attribute::m:val");
		if(argSzVal != null){
			String argSzValStr = argSzVal.getText();
			if(argSzValStr != null && !argSzValStr.equals("")){
				int size = Integer.parseInt(argSzValStr);
				if(size < 0){
					double ratio = 1.5 / (1.0 - size);
					if(ratio > 0.75){
						ratio = 0.75;
					}
					String mathsize = ratio * 100 + "%";
					mathEle.addAttribute("mathsize", mathsize);
				}
			}
		}
		for(Object childNode:wordEle.elements()){
			Element child = (Element)childNode;
			String childName = child.getName();
			if(childName.equals("r")){
				dealR(mathEle,child);
			}else if(childName.equals("f")){
				dealF(mathEle,child);
			}else if(childName.equals("sSup")){
				dealSSup(mathEle,child);
			}else if(childName.equals("sSub")){
				dealSSub(mathEle,child);
			}else if(childName.equals("sSubSup")){
				dealSSubSup(mathEle,child);
			}else if(childName.equals("sPre")){
				dealSPre(mathEle,child);
			}else if(childName.equals("rad")){
				dealRad(mathEle,child);
			}else if(childName.equals("d")){
				dealD(mathEle,child);
			}else if(childName.equals("nary")){
				dealNary(mathEle,child);
			}else if(childName.equals("eqArr")){
				dealEqArr(mathEle,child);
			}else if(childName.equals("m")){
				dealM(mathEle,child);
			}else if(childName.equals("func")){
				dealFunc(mathEle,child);
			}else if(childName.equals("limLow")){
				dealLimLow(mathEle,child);
			}else if(childName.equals("limUpp")){
				dealLimUpp(mathEle,child);
			}else if(childName.equals("acc")){
				dealAcc(mathEle,child);
			}else if(childName.equals("bar")){
				dealBar(mathEle,child);
			}else if(childName.equals("groupChr")){
				dealGroupChr(mathEle,child);
			}else if(childName.equals("box")){
				dealBox(mathEle,child);
			}else if(childName.equals("borderBox")){
				dealBorderBox(mathEle,child);
			}
			
		}
	}
	
	private static void dealBorderBox(Element mathEle,Element wordEle){
		Element table = mathEle.addElement("mtable");
		table.addAttribute("frame", "solid");
		Element tr = table.addElement("mtr");
		Element td = tr.addElement("mtd");
		dealWordMathNode(td.addElement("mrow"),wordEle.element("e"));
	}
	
	private static void dealBox(Element mathEle,Element wordEle){
		Attribute opEmuVal = (Attribute) wordEle.selectSingleNode("m:boxPr/m:opEmu/attribute::m:val");
		if(opEmuVal != null && "1".equals(opEmuVal.getText())){
			isOpEmu.set(true);
		}
		dealWordMathNode(mathEle.addElement("mrow"),wordEle.element("e"));
	}
	
	/**
	 * 处理带有上标的文字上，例如线段的表达方式。//TODO 某些符号无法正常拉伸开
	 * @param mathEle
	 * @param wordEle
	 */
	private static void dealGroupChr(Element mathEle,Element wordEle){
		Attribute chrVal = (Attribute) wordEle.selectSingleNode("m:groupChrPr/m:chr/attribute::m:val");
		Attribute posVal = (Attribute) wordEle.selectSingleNode("m:groupChrPr/m:pos/attribute::m:val");
		String groupChr = (chrVal != null)?chrVal.getText():"︸";
		String posChr = (posVal != null)?posVal.getText():"bottom";
		if(isOpEmu.get()){
			isOpEmu.set(false);
			Element munderover = mathEle.addElement("munderover");
			munderover.addAttribute("accent", "true");
			munderover.addAttribute("accentunder", "true");
			{
				Element mo = munderover.addElement("mo");
//				mo.addAttribute("stretchy", "true");
				mo.setText(groupChr);
			}
			if("top".equals(posChr)){
				Element mrow = munderover.addElement("mrow");
				dealWordMathNode(mrow,wordEle.element("e"));
				
				munderover.addElement("mrow");
			}else{
				munderover.addElement("mrow");
				
				Element mrow = munderover.addElement("mrow");
				dealWordMathNode(mrow,wordEle.element("e"));
			}
		}else{
			Element mover = mathEle.addElement("top".equals(posChr)?"mover":"munder");
			mover.addAttribute("top".equals(posChr)?"accent":"accentunder", "true");
			{
	//			isAllMi.set(true);
				Element mrow = mover.addElement("mrow");
				dealWordMathNode(mrow,wordEle.element("e"));
	//			isAllMi.set(false);
			}
			{
				Element mo = mover.addElement("mo");
				mo.addAttribute("stretchy", "true");
				mo.setText(groupChr);
			}
		}
	}
	
	/**
	 * 处理上标是横线的文字
	 * @param mathEle
	 * @param wordEle
	 */
	private static void dealBar(Element mathEle,Element wordEle){
		Attribute posVal = (Attribute) wordEle.selectSingleNode("m:barPr/m:pos/attribute::m:val");
		String posChr = (posVal != null)?posVal.getText():"bottom";
		Element mover = mathEle.addElement("top".equals(posChr)?"mover":"munder");
		mover.addAttribute("top".equals(posChr)?"accent":"accentunder", "true");
		{
//			isAllMi.set(true);
			Element mrow = mover.addElement("mrow");
			dealWordMathNode(mrow,wordEle.element("e"));
//			isAllMi.set(false);
		}
		{
			Element mo = mover.addElement("mo");
			mo.addAttribute("stretchy", "true");
			mo.setText("¯");
		}
	}
	
	/**
	 * 处理带有上标的文字上，例如线段的表达方式。//TODO 某些符号无法正常拉伸开
	 * @param mathEle
	 * @param wordEle
	 */
	private static void dealAcc(Element mathEle,Element wordEle){
		Attribute chrVal = (Attribute) wordEle.selectSingleNode("m:accPr/m:chr/attribute::m:val");
		String overChr = (chrVal != null)?chrVal.getText():"^";
		Element mover = mathEle.addElement("mover");
		mover.addAttribute("accent", "true");
		{
//			isAllMi.set(true);
			Element mrow = mover.addElement("mrow");
			dealWordMathNode(mrow,wordEle.element("e"));
//			isAllMi.set(false);
		}
		{
			Element mo = mover.addElement("mo");
//			System.out.println(overChr + "\t" + (overChr.charAt(0) - 0) + "\t" + Tools.toUnicode(overChr));
			if(!overChr.equals(String.valueOf((char)775)) && !overChr.equals(String.valueOf((char)776)) && !overChr.equals(String.valueOf((char)8411))){
				mo.addAttribute("stretchy", "true");
			}
			mo.setText(overChr);
		}
	}
	
	/**
	 * 处理有上标的lim函数及类似的函数
	 * @param mathEle
	 * @param wordEle
	 */
	private static void dealLimUpp(Element mathEle,Element wordEle){
		Element munderover = mathEle.addElement("mover");
		{
//			isAllMi.set(true);
			Element mrow = munderover.addElement("mrow");
			dealWordMathNode(mrow,wordEle.element("e"));
//			isAllMi.set(false);
		}
		{
			Element mrow = munderover.addElement("mrow");
			dealWordMathNode(mrow,wordEle.element("lim"));
		}
	}
	
	/**
	 * 处理有下标的lim函数及类似的函数
	 * @param mathEle
	 * @param wordEle
	 */
	private static void dealLimLow(Element mathEle,Element wordEle){
		Element munderover = mathEle.addElement("munder");
		{
//			isAllMi.set(true);
			Element mrow = munderover.addElement("mrow");
			dealWordMathNode(mrow,wordEle.element("e"));
//			isAllMi.set(false);
		}
		{
			Element mrow = munderover.addElement("mrow");
			dealWordMathNode(mrow,wordEle.element("lim"));
		}
	}
	
	/**
	 * 处理函数
	 * @param mathEle
	 * @param wordEle
	 */
	private static void dealFunc(Element mathEle,Element wordEle){
		Element func = mathEle.addElement("mrow");
		{
			isAllMi.set(true);
			Element fName = func.addElement("mrow");
			dealWordMathNode(fName,wordEle.element("fName"));
			isAllMi.set(false);
		}
		{
			Element e = func.addElement("mrow");
			dealWordMathNode(e,wordEle.element("e"));
		}
	}
	
	/**
	 * 处理矩阵
	 * @param mathEle
	 * @param wordEle
	 */
	private static void dealM(Element mathEle,Element wordEle){
		Element mtable = mathEle.addElement("mtable");
		for(Object mr:wordEle.elements("mr")){
			Element tr = mtable.addElement("mtr");
			for(Object e:((Element)mr).elements()){
				Element td = tr.addElement("mtd");
				dealWordMathNode(td,(Element)e);
			}
		}
	}
	
	/**
	 * 处理单列矩阵
	 * @param mathEle
	 * @param wordEle
	 */
	private static void dealEqArr(Element mathEle,Element wordEle){
		Element mtable = mathEle.addElement("mtable");
		mtable.addAttribute("columnalign", "left");
		for(Object e:wordEle.elements("e")){
			Element td = mtable.addElement("mtr").addElement("mtd");
			dealWordMathNode(td,(Element)e);
		}
	}
	
	private static void dealNary(Element mathEle,Element wordEle){
		String naryChr = null;
		Element chr = wordEle.element("naryPr").element("chr");
		if(chr != null){
			naryChr = chr.attributeValue("val");
		}
		if(naryChr == null || naryChr.equals("")){
			naryChr = "∫";
		}
		Element limLoc = wordEle.element("naryPr").element("limLoc");
		String lable = (limLoc != null && "subSup".equals(limLoc.attributeValue("val")))?"msubsup":"munderover";
		Element lableEle = mathEle.addElement(lable);
		{
			Element mrow1 = lableEle.addElement("mrow");
			addText(mrow1,naryChr);
		}
		{
			Element mrow2 = lableEle.addElement("mrow");
			dealWordMathNode(mrow2,wordEle.element("sub"));
		}
		{
			Element mrow3 = lableEle.addElement("mrow");
			dealWordMathNode(mrow3,wordEle.element("sup"));
		}
		{
			dealWordMathNode(mathEle.addElement("mrow"),wordEle.element("e"));
		}
	}
	
	/**
	 * 处理括号
	 * @param mathEle
	 * @param wordEle
	 */
	private static void dealD(Element mathEle,Element wordEle){
		Element mfenced = mathEle.addElement("mfenced");
		{
			Attribute begChrVal = (Attribute) wordEle.selectSingleNode("m:dPr/m:begChr/attribute::m:val");
			Attribute endChrVal = (Attribute) wordEle.selectSingleNode("m:dPr/m:endChr/attribute::m:val");
			if(begChrVal != null){
				mfenced.addAttribute("open", begChrVal.getText());
			}
			if(endChrVal != null){
				mfenced.addAttribute("close", endChrVal.getText());
			}
		}
		{
		Element mrow = mfenced.addElement("mrow");
		dealWordMathNode(mrow,wordEle.element("e"));
		}
	}
	
	/**
	 * 处理根式
	 * @param mathEle
	 * @param wordEle
	 */
	private static void dealRad(Element mathEle,Element wordEle){
		Element r = (Element) wordEle.selectSingleNode("m:deg/m:r");
		if(r == null){
			Element msqrt = mathEle.addElement("msqrt");
			dealWordMathNode(msqrt.addElement("mrow"),wordEle.element("e"));
		}else{
			Element mroot = mathEle.addElement("mroot");
			dealWordMathNode(mroot.addElement("mrow"),wordEle.element("e"));
			dealWordMathNode(mroot.addElement("mrow"),wordEle.element("deg"));
		}
	}
	
	/**
	 * 处理上下标在前面的情况
	 * @param mathEle
	 * @param wordEle
	 */
	
	private static void dealSPre(Element mathEle,Element wordEle){
		Element mrow = mathEle.addElement("mrow");
		
		Element msub = mrow.addElement("msubsup");
		Element mrow1 = msub.addElement("mrow");
		mrow1.setText("");
		Element mrow2 = msub.addElement("mrow");
		dealWordMathNode(mrow2,wordEle.element("sub"));
		Element mrow3 = msub.addElement("mrow");
		dealWordMathNode(mrow3,wordEle.element("sup"));
		
		Element mrow4 = mrow.addElement("mrow");
		dealWordMathNode(mrow4,wordEle.element("e"));
	}
	
	/**
	 * 带有上下标的公式
	 * @param mathEle
	 * @param wordEle
	 */
	
	private static void dealSSubSup(Element mathEle,Element wordEle){
		Element msub = mathEle.addElement("msubsup");
		Element mrow1 = msub.addElement("mrow");
		dealWordMathNode(mrow1,wordEle.element("e"));
		Element mrow2 = msub.addElement("mrow");
		dealWordMathNode(mrow2,wordEle.element("sub"));
		Element mrow3 = msub.addElement("mrow");
		dealWordMathNode(mrow3,wordEle.element("sup"));
	}
	
	private static void dealSSub(Element mathEle,Element wordEle){
		Element msub = mathEle.addElement("msub");
		Element mrow1 = msub.addElement("mrow");
		dealWordMathNode(mrow1,wordEle.element("e"));
		Element mrow2 = msub.addElement("mrow");
		dealWordMathNode(mrow2,wordEle.element("sub"));
	}
	
	private static void dealSSup(Element mathEle,Element wordEle){
		Element msup = mathEle.addElement("msup");
		Element mrow1 = msup.addElement("mrow");
		dealWordMathNode(mrow1,wordEle.element("e"));
		Element mrow2 = msup.addElement("mrow");
		dealWordMathNode(mrow2,wordEle.element("sup"));
	}
	
	/**
	 * 处理分式
	 * @param mathEle
	 * @param wordEle
	 */
	
	private static void dealF(Element mathEle,Element wordEle){
		Attribute typeVal = (Attribute) wordEle.selectSingleNode("m:fPr/m:type/attribute::m:val");
		String typeValStr = (typeVal == null)?"":typeVal.getText();
		if("lin".equals(typeValStr)){
			Element mrow0 = mathEle.addElement("mrow");
			{
				Element mrow = mrow0.addElement("mrow");
				dealWordMathNode(mrow,wordEle.element("num"));
			}
			mrow0.addElement("mn").setText("/");
			{
				Element mrow = mrow0.addElement("mrow");
				dealWordMathNode(mrow,wordEle.element("den"));
			}
		}else{
			Element mfrac = mathEle.addElement("mfrac");
			if("noBar".equals(typeValStr)){
				mfrac.addAttribute("linethickness", "0");
			}
			if("skw".equals(typeValStr)){
				mfrac.addAttribute("bevelled", "true");
			}
			{
				Element mrow = mfrac.addElement("mrow");
				dealWordMathNode(mrow,wordEle.element("num"));
			}
			{
				Element mrow = mfrac.addElement("mrow");
				dealWordMathNode(mrow,wordEle.element("den"));
			}
		}
	}
	
	private static void dealR(Element mathEle,Element wordEle){
		Attribute pVal = (Attribute) wordEle.selectSingleNode("m:rPr/m:sty/attribute::m:val");
		boolean isMo = pVal != null && "p".equals(pVal.getText());
		if(isMo){
			isAllMi.set(true);	
		}
		String text = wordEle.elementText("t");
//		System.out.println(text + "\t" + Tools.toUnicode(text));
//		System.out.println(text + "\tisMo=" + isMo);
		addText(mathEle,text);
		if(isMo){
			isAllMi.set(false);	
		}
	}
	
	private static void addText(Element mathEle,String text){
		if(General.isEmpty(text)){
			return;
		}
		text = text.replace("  &", "    ").replace('-', '−');
		if(isAllMi.get()){
			mathEle.addElement("mo").setText(text);
		}else{
			for(int i=0;i<text.length();i++){
				char c = text.charAt(i);
				if(c >= '0' && c <= '9'){
					mathEle.addElement("mn").setText(String.valueOf(c));
				}else if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '⊕'){
					mathEle.addElement("mi").setText(String.valueOf(c));
				}else{
//					if(c == '-'){
//						c = '−';
//					}
					mathEle.addElement("mo").setText(String.valueOf(c));
				}
			}
		}
	}
}
