package com.ql.util.express.match;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;

import com.ql.util.express.exception.QLCompileException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class QLPattern {

	private static final Log log = LogFactory.getLog(QLPattern.class);
    
    public static boolean optimizeStackDepth = true;//优化栈的递归深度
	public static boolean printStackDepth = false;//打印栈的最大深度

	public static QLPatternNode createPattern(INodeTypeManager nodeTypeManager,String name,String pattern) throws Exception{
		return new QLPatternNode(nodeTypeManager,name,pattern);		
	}
	public static QLMatchResult findMatchStatement(INodeTypeManager aManager,QLPatternNode pattern ,List<? extends IDataNode> nodes,int point) throws Exception{
		AtomicLong maxMatchPoint = new AtomicLong();
		AtomicLong maxDeep = new AtomicLong(1);
		QLMatchResultCache resultCache =new QLMatchResultCache(5);
		ArrayListCache<QLMatchResultTree> arrayListCache = new ArrayListCache<QLMatchResultTree>(50);
        MatchParamsPack staticParams = new MatchParamsPack(aManager, nodes, maxDeep, maxMatchPoint,resultCache,arrayListCache);
		QLMatchResult result  = findMatchStatementWithAddRootOptimizeStack(staticParams, pattern, point, true, 1);
		if(printStackDepth) {
            log.warn("递归堆栈深度:" + maxDeep.longValue() + "  重用QLMatchResult次数:" + resultCache.fetchCount
					+ "  新建QLMatchResult次数:" + resultCache.newCount + "  新建ArrayList数量:" + arrayListCache.newCount);

        }
		if(result == null || result.getMatchSize() == 0){
			throw new QLCompileException("程序错误,不满足语法规范,没有匹配到合适的语法,最大匹配致[0:" + (maxMatchPoint.longValue()-1) +"]");
		}else if(result != null && result.getMatchSize() != 1){
			throw new QLCompileException("程序错误,不满足语法规范,必须有一个根节点:" + pattern + ",最大匹配致[0:" + (maxMatchPoint.longValue()-1) +"]");
		}
		return result;
	}
	private  static QLMatchResult findMatchStatementWithAddRootOptimizeStack(MatchParamsPack staticParams,QLPatternNode pattern ,int point,boolean isRoot,int deep) throws Exception{
        
        INodeTypeManager aManager = staticParams.aManager;
        List<? extends IDataNode> nodes = staticParams.nodes;
        AtomicLong maxMatchPoint = staticParams.maxMatchPoint;
        AtomicLong maxDeep = staticParams.maxDeep;
        
	    //mark maxDeep
	    deep++;
		if (deep > maxDeep.longValue()){
			maxDeep.set(deep);
		}
		
		QLMatchResult result = null;
		List<QLMatchResultTree> tempList = null;
		int count = 0;
		int lastPoint = point;
		while(true){
			QLMatchResult tempResult = null;
			if (pattern.matchMode == MatchMode.DETAIL) {
				//tempResult = matchDetailOneTime(aManager,pattern,nodes, lastPoint,maxMatchPoint,deep,maxDeep);

				int pointDetail = lastPoint;
				QLMatchResult resultDetail = null;
				if(pattern.nodeType == aManager.findNodeType("EOF") && pointDetail == nodes.size()){
					resultDetail = staticParams.resultCache.fetch().setMatchLastIndex(pointDetail + 1);
				}else if(pattern.nodeType == aManager.findNodeType("EOF") && pointDetail < nodes.size() && nodes.get(pointDetail).getValue().equals("}") ){
					resultDetail = staticParams.resultCache.fetch().setMatchLastIndex(pointDetail);
				}else if(pointDetail == nodes.size() && pattern.nodeType.getPatternNode() != null){
					resultDetail = findMatchStatementWithAddRootOptimizeStack(staticParams,pattern.nodeType.getPatternNode(),pointDetail,false,deep);
				}else if( pointDetail < nodes.size()){
					INodeType tempNodeType = null;
					if(pattern.nodeType.equals(nodes.get(pointDetail).getTreeType())){
						tempNodeType = nodes.get(pointDetail).getTreeType();
					}else if(pattern.nodeType.equals(nodes.get(pointDetail).getNodeType())){
						tempNodeType = nodes.get(pointDetail).getNodeType();
					}

					if(tempNodeType != null){
						resultDetail = staticParams.resultCache.fetch();
						resultDetail.addQLMatchResultTree(new QLMatchResultTree(tempNodeType,nodes.get(pointDetail),pattern.targetNodeType));
						pointDetail = pointDetail + 1;
						resultDetail.setMatchLastIndex(pointDetail);

						traceLog(pattern,resultDetail,nodes,pointDetail - 1,1);
					}else if(pattern.nodeType.getPatternNode() != null){
						resultDetail = findMatchStatementWithAddRootOptimizeStack(staticParams,pattern.nodeType.getPatternNode(),pointDetail,false,deep);
						if(pattern.targetNodeType != null && resultDetail != null && resultDetail.getMatchSize() >0){
							if(resultDetail.getMatchSize() > 1){
								throw new QLCompileException("设置了类型转换的语法,只能有一个根节点");
							}
							resultDetail.getMatchs().get(0).targetNodeType = pattern.targetNodeType;
						}
					}
					if(pattern.blame == true){//取返处理
						if( resultDetail == null){
							resultDetail = staticParams.resultCache.fetch();
							resultDetail.addQLMatchResultTree(new QLMatchResultTree(tempNodeType,nodes.get(pointDetail),null));
							pointDetail = pointDetail + 1;
							resultDetail.setMatchLastIndex(pointDetail);
						}else{
							resultDetail = null;
						}
					}
				}
				if(resultDetail != null && resultDetail.getMatchLastIndex() > maxMatchPoint.longValue()){
					maxMatchPoint.set(resultDetail.getMatchLastIndex());
				}

				tempResult = resultDetail;


			}else if (pattern.matchMode == MatchMode.AND) {
				//tempResult = matchAndOneTime(aManager,pattern,nodes, lastPoint,maxMatchPoint,deep,maxDeep);

				int orgiPoint = lastPoint;
				int pointAnd = lastPoint;

				QLMatchResultTree root = null;
				int matchCount =0;//用于调试日志的输出
				List<QLMatchResultTree> tempListAnd =null;
				boolean isBreak = false;
				for (QLPatternNode item : pattern.children) {
					if(pointAnd > nodes.size()){
						isBreak = true;
						break;
					}
					QLMatchResult tempResultAnd = findMatchStatementWithAddRootOptimizeStack(staticParams,item,
							pointAnd,false,deep);
					if (tempResultAnd != null) {
						if(tempResultAnd.getMatchSize() > 0){
							matchCount = matchCount + 1;
						}
						if(tempListAnd == null){
							tempListAnd = staticParams.arrayListCache.fetch();
						}
						pointAnd = tempResultAnd.getMatchLastIndex();
						if (item.isTreeRoot == true && tempResultAnd.getMatchSize() >0) {
							if (tempResultAnd.getMatchSize() > 1) {
                                throw new QLCompileException("根节点的数量必须是1");
                            }
							if (root == null) {
								QLMatchResultTree tempTree = tempResultAnd.getMatchs().get(0);
								while(tempTree.getLeft()!= null && tempTree.getLeft().size()>0){
									tempTree = tempTree.getLeft().get(0);
								}
								tempTree.addLeftAll(tempListAnd);
								tempListAnd.clear();
							} else {
								tempResultAnd.getMatchs().get(0).addLeft(root);
							}
							root = tempResultAnd.getMatchs().get(0);
						} else if (root != null) {
							root.addRightAll(tempResultAnd.getMatchs());
						} else {
							tempListAnd.addAll(tempResultAnd.getMatchs());
						}
						//归还QLMatchResult对象到对象池
						if (tempResultAnd!= null) {
							staticParams.resultCache.sendBack(tempResultAnd);
							tempResultAnd = null;
						}
					}else{
						isBreak = true;
						break;
					}
				}
				if(root != null){
					tempListAnd.add(root);
				}

				if(isBreak == false){
					tempResult = staticParams.resultCache.fetch().addQLMatchResultTreeList(tempListAnd);
					tempResult.setMatchLastIndex(pointAnd);
					traceLog(pattern,tempResult,nodes,orgiPoint,matchCount);
				}else{
					tempResult = null;
				}

				if (tempListAnd != null){
					staticParams.arrayListCache.sendBack(tempListAnd);
				}



			}else if (pattern.matchMode == MatchMode.OR) {
				//tempResult = matchOrOneTime(aManager,pattern,nodes, lastPoint,maxMatchPoint,deep,maxDeep);

				for (QLPatternNode item : pattern.children) {
					tempResult = findMatchStatementWithAddRootOptimizeStack(staticParams,item, lastPoint, false, deep);
					if (tempResult != null) {
						break;
					}
				}

			}else{
				throw new QLCompileException("不正确的类型:" + pattern.matchMode.toString());
			}

			if(tempResult == null){
				if(count >= pattern.minMatchNum && count <=pattern.maxMatchNum){
					//正确匹配
					result = staticParams.resultCache.fetch();
					if(tempList != null){
						result.addQLMatchResultTreeList(tempList);
					}
					result.setMatchLastIndex(lastPoint);
				}else{
					result = null;
				}
				break;
			}else{
				if(tempList == null){
					tempList =staticParams.arrayListCache.fetch();
				}
				lastPoint = tempResult.getMatchLastIndex();
				if(pattern.isTreeRoot == true){
					if(tempResult.getMatchSize() > 1){
						throw new QLCompileException("根节点的数量必须是1");
					}
					if(tempList.size() == 0){
						tempList.addAll(tempResult.getMatchs());
					}else{	
						tempResult.getMatchs().get(0).addLeftAll(tempList);
						//为了能回收QLMatchResult对象,这个地方必须进行数组拷贝
						tempList = staticParams.arrayListCache.fetch();
						tempList.addAll(tempResult.getMatchs());
					}
				}else{
				   tempList.addAll(tempResult.getMatchs());
				}
			}

			/**  归还QLMatchResult  */
			if(tempResult != null){
				staticParams.resultCache.sendBack(tempResult);
				tempResult = null;
			}

			count = count + 1;			
			if(count == pattern.maxMatchNum){
				result = staticParams.resultCache.fetch();
				if(tempList != null){
					result.addQLMatchResultTreeList(tempList);
				}
				result.setMatchLastIndex(lastPoint);
				break;
			}
		}
		if(result != null && pattern.isSkip == true){
			//忽略跳过所有匹配到的节点
			result.getMatchs().clear();
		}

		if(result != null && result.getMatchSize() >0 && pattern.rootNodeType != null){
			QLMatchResultTree tempTree = new QLMatchResultTree(pattern.rootNodeType,nodes.get(0).createExpressNode(pattern.rootNodeType,null));
			tempTree.addLeftAll(result.getMatchs());
			result.getMatchs().clear();
			result.getMatchs().add(tempTree);
		}
		if (tempList != null){
				staticParams.arrayListCache.sendBack(tempList);
			}
		return result;
	}

	public static void traceLog(QLPatternNode pattern, QLMatchResult result,
			List<? extends IDataNode> nodes, int point,int matchCount) {
		if (log.isTraceEnabled() && (pattern.matchMode ==MatchMode.DETAIL || pattern.matchMode == MatchMode.AND && matchCount > 1 && pattern.name.equals("ANONY_PATTERN") == false )) {
			log.trace("匹配--" + pattern.name +"[" + point  + ":" + (result.getMatchLastIndex() -1)+ "]:" + pattern);
		}
	}
	
	public static class MatchParamsPack
    {
        INodeTypeManager aManager;
        List<? extends IDataNode> nodes;
        AtomicLong maxDeep;
        AtomicLong maxMatchPoint;
		QLMatchResultCache resultCache;
		ArrayListCache arrayListCache;
        public MatchParamsPack(INodeTypeManager aManager, List<? extends IDataNode> nodes, AtomicLong maxDeep, AtomicLong maxMatchPoint,QLMatchResultCache aResultCache,ArrayListCache aArrayListCache) {
            this.aManager = aManager;
            this.nodes = nodes;
            this.maxDeep = maxDeep;
            this.maxMatchPoint = maxMatchPoint;
			this.resultCache = aResultCache;
			this.arrayListCache = aArrayListCache;
        }
    }

	public  static class QLMatchResultCache {
		public  int newCount =0;
		public  int fetchCount =0;

		private QLMatchResult[] cache ;
		private int len= 10;
		private int point =9;
		public QLMatchResultCache(int aLen){
			this.len = aLen;
			this.point = this.len -1;
			cache = new QLMatchResult[this.len ];
			for (int i=0;i<this.len;i++){
				cache[i] = new QLMatchResult();
			}
		}

		public QLMatchResult fetch() {
			QLMatchResult result = null;
			if (point >=0) {
				result = cache[point];
				cache[point] = null;
				point = point - 1;
				fetchCount++;
			} else {
				result = new QLMatchResult();
				newCount++;
			}
			return result;
		}
		public void sendBack(QLMatchResult result){
			if (this.point <this.len -1){
				this.point = this.point + 1;
				cache[this.point] = result;
				cache[this.point].clear();
			}
		}

	}

	public  static class ArrayListCache<T> {

		public  int newCount =0;
		public  int fetchCount =0;

		private List<T>[] cache ;
		private int len= 50;
		private int point =49;
		public ArrayListCache(int aLen){
			this.len = aLen;
			this.point = this.len -1;
			cache = new List[this.len];
			for (int i=0;i<this.len;i++){
				cache[i] = new ArrayList<T>();
			}
		}

		public List<T> fetch() {
			List<T> result = null;
			if (point >=0) {
				result = cache[point];
				cache[point] = null;
				point = point - 1;
				fetchCount++;
			} else {
				result = new ArrayList<T>();
				newCount++;
			}
			return result;
		}
		public void sendBack(List<T> result){
			if (this.point <this.len -1){
				this.point = this.point + 1;
				cache[this.point] = result;
				cache[this.point].clear();
			}
		}
	}
}