package com.ejlchina.searcher.virtual;

import com.ejlchina.searcher.SearcherException;
import com.ejlchina.searcher.beanmap.SearchBeanMap;
import com.ejlchina.searcher.util.StringUtils;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


public class DefaultVirtualParamProcessor implements VirtualParamProcessor {

	
	private String virtualParamPrefix = ":";
	
	private String[] vertualParamEndFlags = new String[] {" ", "\t", "\n", "\r", "+", "-", "*", "/", "=", "!", ">", "<", ",", ")", "'", "%"};
	
	private final char[] quotations = new char[] {'\'', '"'};
	
	private final Map<SearchBeanMap, Object> processed = new ConcurrentHashMap<>();
	
	
	@Override
	public SearchBeanMap process(SearchBeanMap searchBeanMap) {
		if (processed.get(searchBeanMap) != null) {
			return searchBeanMap;
		}
		processed.put(searchBeanMap, new Object());
		return doProcess(searchBeanMap);
	}
	
	
	private SearchBeanMap doProcess(SearchBeanMap searchBeanMap) {
		
		VirtualSolution solution = resolveVirtualParams(searchBeanMap.getTalbes());
		searchBeanMap.setTalbes(solution.getSqlSnippet());
		searchBeanMap.setTableVirtualParams(solution.getVirtualParams());
		
		solution = resolveVirtualParams(searchBeanMap.getJoinCond());
		searchBeanMap.setJoinCond(solution.getSqlSnippet());
		searchBeanMap.setJoinCondVirtualParams(solution.getVirtualParams());
		
		Map<String, String> fieldDbMap = searchBeanMap.getFieldDbMap();
		for (String field : searchBeanMap.getFieldList()) {
			solution = resolveVirtualParams(fieldDbMap.get(field));
			fieldDbMap.put(field, solution.getSqlSnippet());
			searchBeanMap.putFieldVirtualParam(field, solution.getVirtualParams());
		}
		
		return searchBeanMap;
	}
	
	
	private VirtualSolution resolveVirtualParams(String sqlSnippet) {
		VirtualSolution solution = new VirtualSolution();
		int index1 = sqlSnippet.indexOf(virtualParamPrefix);
		while (index1 >= 0) {
			int index2 = findVitualParamEndIndex(sqlSnippet, index1);
			String paramName;
			if (index2 > 0) {
				paramName = sqlSnippet.substring(index1, index2);
			} else {
				paramName = sqlSnippet.substring(index1);
			}
			if (StringUtils.isBlank(paramName) || paramName.length() < 2) {
				throw new SearcherException("这里有一个语法错误（虚拟参数名）：" + sqlSnippet);
			}
			VirtualParam virtualParam = new VirtualParam(paramName);
			boolean endWithPrefix = paramName.endsWith(virtualParamPrefix);
			if (endWithPrefix) {
				virtualParam.setName(paramName.substring(1, paramName.length() - virtualParamPrefix.length()));
			} else {
				virtualParam.setName(paramName.substring(1));
			}
			int quotationCount1 = StringUtils.containCount(sqlSnippet, 0, index1, quotations);
			int quotationCount2 = StringUtils.containCount(sqlSnippet, Math.max(index1, index2), sqlSnippet.length(), quotations);
			if ((quotationCount1 + quotationCount2) % 2 != 0) {
				throw new SearcherException("这里有一个语法错误（引号不匹配）：" + sqlSnippet);
			}
			// 判断虚拟参数是否不在引号内部，并且不是以 :name: 的形式
			if (quotationCount1 % 2 == 0 && !endWithPrefix) {
				virtualParam.setParameterized(true);
				sqlSnippet = sqlSnippet.replaceFirst(paramName, "?");
			}
			solution.addVirtualParam(virtualParam);
			index1 = sqlSnippet.indexOf(virtualParamPrefix, Math.max(index1, index2) + 1);
		}
		solution.setSqlSnippet(sqlSnippet);
		return solution;
	}
	

	private int findVitualParamEndIndex(String sqlSnippet, int fromIndex) {
		int index = -1;
		for (String flag : vertualParamEndFlags) {
			int index0 = sqlSnippet.indexOf(flag, fromIndex);
			if (index < 0) {
				index = index0;
			} else if (index0 > 0) {
				index = Math.min(index, index0);
			}
		}
		return index;
	}


	public String getVirtualParamPrefix() {
		return virtualParamPrefix;
	}


	public void setVirtualParamPrefix(String virtualParamPrefix) {
		this.virtualParamPrefix = virtualParamPrefix;
	}


	public String[] getVertualParamEndFlags() {
		return vertualParamEndFlags;
	}


	public void setVertualParamEndFlags(String[] vertualParamEndFlags) {
		this.vertualParamEndFlags = vertualParamEndFlags;
	}
	
}

@@ -2993,16 +2993,66 @@
 %0D%0A%09%09%09%7D%0D%0A
+%09%09%09int nextIndex = Math.max(index1, index2) + 1;%0D%0A
 %09%09%09// %E5%88%A4%E6%96%AD
@@ -3233,24 +3233,112 @@
 ame, %22?%22);%0D%0A
+%09%09%09%09// sqlSnippet %E9%95%BF%E5%BA%A6%E5%8F%98%E7%9F%AD%EF%BC%8C%E5%AF%BB%E6%89%BE%E4%B8%8B%E6%A0%87%E4%B9%9F%E8%AF%A5%E7%9B%B8%E5%BA%94%E6%8F%90%E5%89%8D%0D%0A%09%09%09%09nextIndex = nextIndex - paramName.length() + 1;%0D%0A
 %09%09%09%7D%0D%0A%09%09%09sol
@@ -3430,36 +3430,17 @@
 ix, 
-Math.max(index1, index2) + 1
+nextIndex
 );%0D%0A

修复当两个虚拟参数相距很近时，后一个虚拟参数可能无法正常解析的问题