package com.eactive.eai.util.json;

import java.util.Map;

import com.eactive.eai.common.util.Logger;
import com.eactive.eai.util.json.transformer.ValueTransformer;
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.ObjectNode;

public class JsonSimplePathsTransform {
	static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
	private static final ObjectMapper objectMapper = new ObjectMapper();

	public static <T, R> String modifyValuesAtPaths(String jsonString,
			Map<String, ValueTransformer<T, R>> pathTransformerMap, boolean isPretty) {
		try {
			JsonNode rootNode = objectMapper.readTree(jsonString);

			for (Map.Entry<String, ValueTransformer<T, R>> entry : pathTransformerMap.entrySet()) {
				String path = entry.getKey();
				ValueTransformer<T, R> transformer = entry.getValue();

				JsonNode currentNode = getNodeAtPath(rootNode, path);
				if (currentNode != null && currentNode.isValueNode()) {
					T currentValue = extractValue(currentNode);
					R newValue = transformer.transform(currentValue);
					
					if(logger.isDebug()) {
						// FIXME : kbank - 로그내용 수정
						String encLog = transformer.getClass().getName() +" : "+ String.format("path=%s, orgText[%s] plain[%s] newText[%s]", path, currentValue, transformer.getProperty("PLAIN_VALUE"), newValue);
						// System.out.println(encLog);
						logger.debug(encLog);
					}
					setNodeValue(getParentNode(rootNode, getParentPath(path)), getFieldName(path), newValue);
				}
			}
			if(isPretty) {
				return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(rootNode);
			}
			else {
				return objectMapper.writeValueAsString(rootNode);
			}			
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	private static <T> T extractValue(JsonNode node) {
		if (node.isTextual()) {
			return (T) node.asText();
		} else if (node.isInt()) {
			return (T) (Integer) node.asInt();
		} else if (node.isDouble()) {
			return (T) (Double) node.asDouble();
		} else if (node.isBoolean()) {
			return (T) (Boolean) node.asBoolean();
		} else {
			return (T) node.asText();
		}
	}

	private static <R> void setNodeValue(JsonNode parentNode, String fieldNameOrIndex, R newValue) {
		if (parentNode.isArray()) {
			((ArrayNode) parentNode).set(Integer.parseInt(fieldNameOrIndex),
					objectMapper.convertValue(newValue, JsonNode.class));
		} else if (parentNode.isObject()) {
			((ObjectNode) parentNode).set(fieldNameOrIndex, objectMapper.convertValue(newValue, JsonNode.class));
		}
	}

	private static String getParentPath(String path) {
		int lastSlashIndex = path.lastIndexOf('/');
		return lastSlashIndex == 0 ? "/" : path.substring(0, lastSlashIndex);
	}

	private static String getFieldName(String path) {
		int lastSlashIndex = path.lastIndexOf('/');
		return path.substring(lastSlashIndex + 1);
	}

	private static JsonNode getParentNode(JsonNode rootNode, String parentPath) {
		return getNodeAtPath(rootNode, parentPath);
	}

	private static JsonNode getNodeAtPath(JsonNode rootNode, String path) {
		String[] tokens = path.split("/");
		JsonNode currentNode = rootNode;

		for (String token : tokens) {
			if (token.isEmpty())
				continue;

			if (token.matches("\\d+")) {
				currentNode = currentNode.get(Integer.parseInt(token));
			} else {
				currentNode = currentNode.get(token);
			}

			if (currentNode == null) {
				return null;
			}
		}
		return currentNode;
	}	
}
