package com.eactive.eai.message.parser;

import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedHashMap;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.eactive.eai.message.EncodingVar;
import com.eactive.eai.message.StandardItem;
import com.eactive.eai.message.StandardMessage;
import com.eactive.eai.message.StandardType;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeType;

public class JsonReader implements StandardReader {
	static Logger logger = LoggerFactory.getLogger(JsonReader.class);
	private char FIELD_SEPARATOR = '.';
	private boolean ZERO_BASE_INDEX = true;
	
	public JsonReader() {
		
	}

	public StandardMessage parse(StandardMessage message, Object obj) throws Exception {
		String jsonString;
		if (obj instanceof byte[]) {
			jsonString = new String((byte[]) obj, EncodingVar.jsonEncoding);
		} else {
			jsonString = (String) obj;
		}
		
		JsonNode jsonNode = null;
	    ObjectMapper mapper =  null;
	    JsonFactory factory = null;
	    JsonParser parser = null;
	    mapper = new ObjectMapper();
	    mapper.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
		factory = mapper.getFactory();
		try {
			parser = factory.createParser(jsonString);
		} catch (IOException e) {
			e.printStackTrace();
			throw e;
		}
		try {
			jsonNode = mapper.readTree(parser);
		} catch (IOException e) {
			e.printStackTrace();
			throw e;
		}
		
//		ArrayList<StandardItem> list = message.toList();
//		for(int i=0; i<list.size(); i++) {
//			StandardItem item = list.get(i);
//			logger.debug( i + " - "+ item.getLevel() + " : " +item.getName());
//		}
//		ArrayList<StandardItem> childList = message.getChildsList();
//		int i=0;
//		for(StandardItem item:childList) {
//			logger.debug( i++ + " - "+ item.getLevel() + " : " +item.getName());
//			jsonNode.get(item.getName());
//		}
		
		// make json map(path, value)
		LinkedHashMap<String, String> itemMap = new LinkedHashMap<String, String>();
		traverse(message, jsonNode, null, itemMap);

//		for(String key:itemMap.keySet()) {
//			logger.debug("~ itemMap : " + key + " => " + itemMap.get(key));
//		}
		return message;		
	}
	
	private void traverse(StandardMessage message, JsonNode currentNode, String parentName, LinkedHashMap<String, String> itemMap){
		StandardItem currentItem = null;
		String fieldName = null;
		if(parentName != null) {
			if(logger.isDebugEnabled()) logger.debug("$ findItem :: findItem="+ parentName);
			currentItem = message.findItem(parentName, FIELD_SEPARATOR, ZERO_BASE_INDEX, true);
			if(currentItem == null) {
				if(logger.isDebugEnabled()) logger.debug("$ SKIP :: undefined path in StandardMessage node name="+ parentName);
				return;
			}			
		}
		if(currentNode == null) {
			if(logger.isDebugEnabled()) logger.debug("$ SKIP :: node is NULL name="+ parentName);
			return;
		}
		
		if( currentItem!=null && currentItem.getType() == StandardType.BIZDATA) {
			String bizData = null;
			if(currentNode.getNodeType() == JsonNodeType.STRING) {
				bizData = currentNode.asText();
			}
			else {
				bizData = currentNode.toString();
			}
			itemMap.put(parentName , bizData);
	        currentItem.setValue(bizData);
	        return;
		}
		
		switch(currentNode.getNodeType()) {
			case OBJECT:
				if( currentItem != null && currentItem.getSize() == 0 
				&& StringUtils.isNotBlank(currentItem.getRefPath()) 
				&& StringUtils.isNotBlank(currentItem.getRefValue()) ) {
					String refItemValue = itemMap.get(currentItem.getRefPath());

					if (logger.isDebugEnabled())
						logger.debug("@GROUP name={}, getRefPath={}, refItemValue=[{}], getRefValue=[{}]",
								currentItem.getName(), currentItem.getRefPath(), refItemValue,
								currentItem.getRefValue());

					if (refItemValue != null) refItemValue = refItemValue.trim();
					String refValue = currentItem.getRefValue();
					if (refValue.startsWith("!")) { // 느낌표가 들어가면 다음에 나오는게 아닌경우
						refValue = refValue.substring(1);
						if (refValue.equals(refItemValue)) {
							logger.warn("@GROUP name={} SKIPPED.", currentItem.getName());
							currentItem.setHidden(true);
							break;
						} else {
							currentItem.setHidden(false);
						}
					} else {
						if (!currentItem.getRefValue().equals(refItemValue)) {
							if (logger.isDebugEnabled())
								logger.warn("@GROUP name={} SKIPPED.", currentItem.getName());
							currentItem.setHidden(true);
							break;
						} else {
							currentItem.setHidden(false);
						}
					}
				}
				Iterator<String> fieldNames = currentNode.fieldNames();
		        while(fieldNames.hasNext()) {
		            fieldName = fieldNames.next();
		            JsonNode fieldValue = currentNode.get(fieldName);
		            fieldName = genPath(parentName, fieldName);
		            traverse(message, fieldValue, fieldName, itemMap);
		        }
				if(currentItem != null &&  currentItem.getSize() == 0) {
					currentItem.setSize(1);
				}
				break;
			case ARRAY:
				if(logger.isDebugEnabled()) logger.debug("@ARRAY name={}, getLength={}, getRefPath={}, getRefValue=[{}]"
						, currentItem.getName(), currentItem.getLength(), currentItem.getRefPath(), currentItem.getRefValue());
				if( currentItem != null && currentItem.getSize() == 0 
					&& StringUtils.isNotBlank(currentItem.getRefPath()) 
					&& StringUtils.isNotBlank(currentItem.getRefValue()) ) {
					String refItemValue = itemMap.get(currentItem.getRefPath());
				
					if(logger.isDebugEnabled()) logger.debug("@ARRAY name={}, getRefPath={}, refItemValue=[{}], getRefValue=[{}]"
							, currentItem.getName(), currentItem.getRefPath(), refItemValue, currentItem.getRefValue());
					
					if( !currentItem.getRefValue().equals(refItemValue) ) {
						if(logger.isDebugEnabled()) logger.warn("@ARRAY name={} SKIPPED.", currentItem.getName());
							break;
					}
				}
				ArrayNode arrayNode = (ArrayNode) currentNode;
		        for(int i = 0; i < arrayNode.size(); i++) {
		            JsonNode arrayElement = arrayNode.get(i);
		            traverse(message, arrayElement, parentName+"["+ i+"]", itemMap);
		        }
				break;
			default:
				if(logger.isDebugEnabled()) logger.debug("parsing : " + parentName + "=" +currentNode.asText());
		        itemMap.put(parentName , currentNode.asText());
		        if(currentItem.getType() == StandardType.FARRAY) {
		        	currentItem.addValue(currentNode.asText());
		        }
		        else {
		        	currentItem.setValue(currentNode.asText());
		        }
				
		        break;				
		}
	}

	private String genPath(String parent, String nodeName) {
		if(parent == null || parent.length() < 1) {
			return nodeName;
		}
		return parent +FIELD_SEPARATOR + nodeName;
	}

//	public static void main(String[] args) {
//	}

}
