package com.zzsn.orc;


import com.jayway.jsonpath.JsonPath;
import com.zzsn.orc.exception.JsonParserException;
import com.zzsn.orc.exception.NotSupportException;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

import javax.xml.xpath.XPathExpressionException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * Json 页面解析器
 */
public class JsonPageParser {

	/**
	 *  将json转为对象迭代器
	 * 
	 * @param pageStr
	 * @param itemsPath
	 * @return
	 * @throws XPathExpressionException
	 */
	@SuppressWarnings("unchecked")
	public static Iterator<? extends Object> parserJsonObjIterator(String pageStr, String itemsPath)
			throws XPathExpressionException {
		Iterator<? extends Object> rlt = null;
		if (!isJsonpath(itemsPath)) {
			JSONObject jsonObject = new JSONObject();
			jsonObject = JSONObject.fromObject(pageStr);
			JSONObject jObject = jsonObject.getJSONObject(itemsPath);
			if (jObject.isArray()) {
				JSONArray jArray = jsonObject.getJSONArray(itemsPath);
				rlt = jArray.iterator();
			}
		} else {
			List<Object> jsonObjets = JsonPath.read(pageStr, itemsPath);
			rlt = jsonObjets.iterator();
		}

		return rlt;
	}

	/**
	 * 根据语法类型判断是否为Jsonpath
	 * 
	 * @param itemsPath
	 * @return
	 */
	private static boolean isJsonpath(String itemsPath) {
		if (itemsPath == null || itemsPath.isEmpty())
			return false;
		return itemsPath.contains("$");
	}

	/**
	 * 根据path获取对应值,支持以下语法</br>
	 * 1.substring_after(path,splitChar),</br>
	 * eg：substring_after($.body,"#") return "un#hap#py" -> "hap#py"</br>
	 * </br>
	 * 2.substring_before(path,splitChar),</br>
	 * eg：substring_before($.body,"#") return "un#hap#py" -> "un"</br>
	 * </br>
	 * 3.contains(path,char),</br>
	 * eg：contains($.body,"#"),return "un#hap#py" -> 1 "unhappy" -> 0</br>
	 * </br>
	 * 4.remove(path,char),</br>
	 * eg:remove($.body,"#"),return "un#hap#py" -> "unhappy" </br>
	 * </br>
	 * 5.replace(path,char1,char2),</br>
	 * replace($.body,"#","%"),return "un#hap#py" -> "un%hap%py" </br>
	 * </br>
	 * 6.substring(path,startInt,length),substring(path,startInt)</br>
	 * substring($.body,2,3),return "un#hap#py" -> "#ha" </br>
	 * substring($.body,2),return "un#hap#py" -> "#hap#py" </br>
	 * </br>
	 * 7.concat(path,path),concat(path,str)</br>
	 * concat($.body,$.body),return "un#hap#py" -> "un#hap#pyun#hap#py" </br>
	 * concat($.body,"kkkk"),return "un#hap#py" -> "un#hap#pykkkk" </br>
	 * </br>
	 * @param doc JSONObject 或 Object ,建议由{@link #parserJsonObjIterator(String, String)} 提供
	 * @param path
	 * @return 字符串 
	 * @throws JsonParserException
	 */
	public static String parser(Object doc, String path) throws JsonParserException {
		if (path == null || path.isEmpty()) {
			return null;
		}
		JsonOperator curOperator = null;
		try {
			curOperator = new JsonPageParser().new JsonOperator(path);
		} catch (NotSupportException e) {
			throw new JsonParserException(e);
		}
		if (curOperator.isOperable()) {
			String realPath = curOperator.getRealPath();
			String val = parser(doc, realPath);
			String realVal = null;
			try {
				realVal = curOperator.getRealVal(doc,val);
			} catch (NotSupportException e) {
				throw new JsonParserException(e);
			}
			return realVal;
		}
		String nodeVal = null;
		try {
			if (doc instanceof JSONObject) {
				nodeVal = ((JSONObject) doc).getString(path);
			} else if (doc instanceof String) {
				Object obj = JsonPath.read(doc.toString(), path);
				nodeVal = curOperator.getNodeValue(obj);
			}  else {
				Object obj = JsonPath.read(doc, path);
				nodeVal = curOperator.getNodeValue(obj);
			}
		} catch (Exception e) {
			throw new JsonParserException(e);
		}
		return nodeVal;
	}

	private enum OperatorType {
		SUB_AFTER, SUB_BEFORE, CONTAINS,REMOVE_ALL,REPLACE_ALL,SUB_STRING,CONCAT,CONCAT_BEFORE
	}

	private class JsonOperator {

		private OperatorType operatorType = null;

		private String realPath = null;
		private String realVal = null;
		
		private List<String> params = null;
		private List<String> realPaths = null;

		public JsonOperator(String path) throws NotSupportException {
			initOperator(path);
		}

		private void initOperator(String path) throws NotSupportException {
			int opEndPos = path.indexOf("(");
			if (opEndPos <= 0 || !path.endsWith(")")){
				return;
			}
			String op = path.substring(0, opEndPos);
			String restPart = path.substring(opEndPos + 1, path.length() - 1);
			switch (op) {
			case "substring-after":
				if (initPath(restPart))
					operatorType = OperatorType.SUB_AFTER;
				break;
			case "substring-before":
				if (initPath(restPart))
					operatorType = OperatorType.SUB_BEFORE;
				break;
			case "contains":
				if (initPath(restPart))
					operatorType = OperatorType.CONTAINS;
				break;
			case "remove":
				if (initPath(restPart))
					operatorType = OperatorType.REMOVE_ALL;
				break;
			case "replace":
				if (initPath(restPart))
					operatorType = OperatorType.REPLACE_ALL;
				break;
			case "substring":
				if (initPath(restPart))
					operatorType = OperatorType.SUB_STRING;
				break;
			case "concat":
				if (initPath(restPart))
					operatorType = OperatorType.CONCAT;
				break;
			case "concat_before":
				if(initPath(restPart)){
					operatorType = OperatorType.CONCAT_BEFORE;
				}
				break;
			default:
				break;
			}
		}

		private boolean initPath(String inPart) throws NotSupportException {
			if (inPart == null || inPart.isEmpty()){
				throw new NotSupportException(String.format("Not Support Json Operator Param:[%s]", inPart));
			}
			int pathEndPos = inPart.startsWith("$") ? inPart.indexOf(",") : inPart.lastIndexOf(",");
			realPath = inPart.substring(0, pathEndPos).trim();
			String restPart = inPart.substring(pathEndPos);
			List<String> list = new ArrayList<String>();
			
			if(realPath.contains("$")){
				list.add(restPart.substring(1));
				restPart = restPart.substring(1);
			} else {
				while (realPath.contains("\"")) {
					int startPos = restPart.indexOf("\"");
					int endPos = restPart.indexOf("\"", startPos + 1);
					if (endPos <= 0)
						throw new NotSupportException(String.format("Not Support Json Operator Param:[%s]", inPart));
					list.add(restPart.substring(startPos+1, endPos));
					restPart = restPart.substring(endPos + 1);
				}
			}
			if (list.isEmpty()){
				throw new NotSupportException(String.format("Not Support Json Operator Param:[%s]", inPart));
			}
			params = new ArrayList<String>();
			params.addAll(list);
			return true;
		}

		/**
		 * substring取出现的第一个分隔符的位置
		 * 
		 * @param value
		 * @return
		 * @throws NotSupportException
		 */
		public String getRealVal(Object doc,String value) throws NotSupportException {
			if (realVal != null){
				return realVal;
			}
			if (value == null || value.isEmpty()){
				return value;
			}
			if (operatorType == null ){
				throw new NotSupportException(String.format("Not Support Json Operator:[%s,%s]", operatorType, value));
			}

			switch (operatorType) {
			case SUB_AFTER:
				realVal = substringAfter(value);
				break;
			case SUB_BEFORE:
				realVal = substringBefore(value);
				break;
			case CONTAINS:
				realVal = contains(value);
				break;
			case REMOVE_ALL:
				realVal = removeAll(value);
				break;	
			case REPLACE_ALL:
				realVal = replaceAll(value);
				break;
			case SUB_STRING:
				realVal = substring(value);
				break;
			case CONCAT:
				realVal = concat(doc,value);
				break;
			case CONCAT_BEFORE:
				realVal = concat_before(doc, value);
				break;
			default:
				throw new NotSupportException(String.format("Not Support Json Operator:[%s]", operatorType));
			}
			return realVal;
		}

		public String getRealPath() {
			return realPath;
		}

		public boolean isOperable() {
			return operatorType != null;
		}

		private String substringAfter(String value) throws NotSupportException {
			if (value == null || value.isEmpty() || params == null || params.isEmpty()) {
				throw new NotSupportException("Substring after Not Support Empty Value Or Empty Split Char");
			}

			String splitChar = params.get(0);
			if (splitChar == null || splitChar.isEmpty()) {
				throw new NotSupportException("Substring after Not Support Empty Split Char");
			}

			int splitPos = value.indexOf(splitChar);
			if (splitPos < 0 || splitPos == value.length() - 1) {
				return "";
			}
			splitPos += splitChar.length() - 1;
			return value.substring(splitPos + 1);
		}

		private String substringBefore(String value) throws NotSupportException {
			if (value == null || value.isEmpty() || params == null || params.isEmpty()) {
				throw new NotSupportException("Substring before Not Support Empty Value Or Empty Split Char");
			}

			String splitChar = params.get(0);
			if (splitChar == null || splitChar.isEmpty()) {
				throw new NotSupportException("Substring before Not Support Empty Split Char");
			}

			int splitPos = value.indexOf(splitChar);
			if (splitPos <= 0) {
				return "";
			}
			return value.substring(0, splitPos);
		}

		private String contains(String value) throws NotSupportException {
			if (value == null || value.isEmpty() || params == null || params.isEmpty()) {
				throw new NotSupportException("Contains Not Support Empty Value Or Empty Char");
			}

			String str = params.get(0);
			if (str == null || str.isEmpty()) {
				throw new NotSupportException("Contains Not Support Empty Char");
			}
			return value.contains(str) ? "1" : "0";
		}
		
		
		private String removeAll(String value) throws NotSupportException {
			if (value == null || value.isEmpty() || params == null || params.isEmpty()){
				throw new NotSupportException("Remove Not Support Empty Value Or Empty Char");
			}

			String str = params.get(0);
			if (str == null || str.isEmpty()){
				throw new NotSupportException("Remove Not Support Empty Char");
			}
			while(value.contains(str)){
				value = value.replace(str, "");
			}
			return value;
		}
		
		private String replaceAll(String value) throws NotSupportException {
			if (value == null || value.isEmpty() || params == null || params.size() != 2){
				throw new NotSupportException("Replace Not Support Empty Value Or Empty Char");
			}

			String str = params.get(0);
			if (str == null || str.isEmpty()){
				throw new NotSupportException("Replace Not Support Empty Char");
			}
			String replaceStr = params.get(1);
			if (replaceStr == null){
				throw new NotSupportException("Replace Not Support Empty Replace Char");	
			}
			while (value.contains(str)) {
				value = value.replace(str, replaceStr);
			}
			return value;
		}
		
		private String substring(String value) throws NotSupportException {
			if (value == null || value.isEmpty() || params == null){
				throw new NotSupportException("Substring Not Support Empty Value Or Empty Char");
			}

			if(params.size() == 1){
				String str = params.get(0);
				if (str == null || str.isEmpty()){
					throw new NotSupportException("Substring Not Support Empty StartIndex");
				}
				value = value.substring(Integer.parseInt(str));
			} else {
				String str = params.get(0);
				if (str == null || str.isEmpty()){
					throw new NotSupportException("Substring Not Support Empty StartIndex");
				}
				String replaceStr = params.get(1);
				if (replaceStr == null || replaceStr.isEmpty()){
					throw new NotSupportException("Substring Not Support Empty CharLength");
				}
				value = value.substring(Integer.parseInt(str), Integer.parseInt(replaceStr));
			}
			return value;
		}
		
		private String concat(Object doc, String value) throws NotSupportException{
			if(value == null || value.isEmpty() || params == null){
				throw new NotSupportException("Concat Not Support Empty Value Or Empty Char");
			}
			String str = params.get(0);
			if(str == null || str.isEmpty()){
				throw new NotSupportException("Concat Not Support Empty Char");
			}
			String concatChar = str;
			if(isJsonpath(str)){
				try {
					concatChar = parser(doc,str);
				} catch (JsonParserException e) {
					throw new NotSupportException(e.getMessage());
				}
			}
			return value.concat(concatChar);
		}
		
		private String concat_before(Object doc, String value) throws NotSupportException{
			if(params == null){
				throw new NotSupportException("Concat Not Support Empty Char");
			}
			String str = params.get(0);
			String concatChar = str;
			if(isJsonpath(str)){
				try {
					concatChar = parser(doc,str);
				} catch (JsonParserException e) {
					throw new NotSupportException(e.getMessage());
				}
			}
			if(concatChar == null || concatChar.isEmpty()){
				throw new NotSupportException("Concat Not Support Empty Value ");
			}
			return concatChar.concat(value);
		}
		
		private String getNodeValue(Object node){
			String value = null;
			if(node instanceof ArrayList){
				List<Object> listNode = (List)node;
				StringBuffer buffer = new StringBuffer();
				for(Object obj : listNode){
					buffer.append(obj.toString()).append("-");
				}
				value = buffer.length() == 0 ? null : buffer.substring(0, buffer.length()-1).toString();
			} else {
				value = node==null?value:node.toString();
			}
			return value;
		}
	}

}
