package com.eactive.eai.message;

import java.util.ArrayList;
import java.util.Properties;
import java.util.Stack;

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

import com.eactive.eai.common.message.EAIMessage;
import com.eactive.eai.common.stdmessage.STDMessageManager;
import com.eactive.eai.common.util.ApplicationContextProvider;
import com.eactive.eai.inbound.processor.ProcessVO;
import com.eactive.eai.message.layout.CsvFileReader;
import com.eactive.eai.message.layout.DatabaseReader;
import com.eactive.eai.message.layout.LayoutReader;
import com.eactive.eai.message.manager.StandardMessageManager;

public class StandardMessageUtil {
	static Logger logger = LoggerFactory.getLogger(StandardMessageUtil.class);
	
	public static String requestProcessorClass = "com.eactive.eai.inbound.processor.RequestProcessor";
	
	public static String REST_URL_ACTION0 = "com.eactive.eai.inbound.action.RestAdapterMethodUrlRequestAction";
	public static String REST_URL_ACTION1 = "com.eactive.eai.inbound.action.RestUrlAdapterGroupParseRequestAction";
	public static String REST_URL_ACTION2 = "com.eactive.eai.inbound.action.RestUrlParseRequestAction";

	public StandardMessageUtil() {		
	}
	
	public static String getBizData(EAIMessage eaiMessage) throws Exception {
//		InterfaceMapper mapper = eaiMessage.getMapper();
		StandardMessage standardMessage = eaiMessage.getStandardMessage();
		return standardMessage.getBizData();		
	}
	
	public static void setBizData(EAIMessage eaiMessage, String bizData, String charset) throws Exception {
//		InterfaceMapper mapper = eaiMessage.getMapper();
		StandardMessage standardMessage = eaiMessage.getStandardMessage();
		standardMessage.setBizData(bizData, charset);
	}
	
	public static StandardMessage getStandardMessageFromRule(Object message, ProcessVO vo, String charset, Properties prop) throws Exception {
		StandardMessageManager standardManager = StandardMessageManager.getInstance();
		StandardMessage standardMessage = standardManager.getStandardMessage();

		STDMessageManager stdMessageManager = STDMessageManager.getInstance();
		standardMessage = stdMessageManager.getSTDMessage(vo.getSelectedKey());
		if (standardMessage == null) {
			// API 서비스일경우 PathVariable 지원 추가
			// ex) /api/account/{no}
			if (StringUtils.equalsAny(vo.getActionName(), REST_URL_ACTION0, REST_URL_ACTION1, REST_URL_ACTION2)) {
				logger.debug("getSTDMessageForPathVariable call");
				standardMessage = stdMessageManager.getSTDMessageForPathVariable(vo.getSelectedKey());
			}

			if (standardMessage == null) {
				return null;
			}
		}
		
		standardMessage.setBizData(message, charset);
		standardManager.getMessageCoordinator().coordinateAfterCreateMessageByRule(standardMessage, vo, prop);
		logger.debug("coordinateAfterCreateMessageByRule called");
		return standardMessage;
	}
	
	/**
	 * API 서비스에서 실제 요청된 path를 설정된 PathVariable 형태의 key를 찾는다
	 * ex) /api/bank/account/12345 --> /api/bank/account/{accountNo}
	 * @param key
	 * @param actionName
	 * @return
	 */
	public static String getMatchedKey(String key, String actionName) {
		STDMessageManager stdMessageManager = STDMessageManager.getInstance();
		String[] keys = stdMessageManager.getAllSTDMessageKeys();
		for (String keyStr : keys) {
			if (StringUtils.equals(key, keyStr)) {
				return keyStr;
			}
		}

		if (StringUtils.equalsAny(actionName, REST_URL_ACTION0, REST_URL_ACTION1, REST_URL_ACTION2)) {
			return stdMessageManager.getMatchedPathVariable(key);
		}

		return null;
	}
	
	public static StandardMessage generateMessageFromCsvFile() {
		String filePath = "./resources/standard-layout.csv";
		return generateMessageFromCsvFile(filePath);
	}
	public static StandardMessage generateMessageFromCsvFile(String filePath) {
		ArrayList<StandardItem> list = null;

		try {
			LayoutReader reader = new CsvFileReader();
			list = reader.parse(filePath);
		}
		catch(Exception ex) {
			ex.printStackTrace();
			return null;
		}
		StandardMessage message = null;
		try {
			message = generate(list);			
			// message = generate_backup(list);
		} catch (Exception e) {
			e.printStackTrace();			
		}
		return message;
	}

	public static StandardMessage generateMessageFromDb(String layoutName) {
		ArrayList<StandardItem> list = null;
		try {
			LayoutReader reader = ApplicationContextProvider.getContext().getBean(DatabaseReader.class);
			list = reader.parse(layoutName);
		}
		catch(Exception ex) {
			logger.error("", ex);
			ex.printStackTrace();
			return null;
		}
		StandardMessage message = null;
		try {
			message = generate(list);
			// message = generate_backup(list);
		} catch (Exception e) {
			logger.error("", e);
			e.printStackTrace();
		}
		return message;
	}
	
	
	// TODO : if Fixed Array, clone list item to length ?
	public static StandardMessage generate_backup(ArrayList<StandardItem> itemList) throws Exception {
		StandardMessage message = new StandardMessage();
		Stack<StandardItem> stack = new Stack<StandardItem>();
		Stack<String> stackPath = new Stack<String>();
		int pLevel = 0;
		StandardItem parent = message;
		String parentPath = null;
//		logger.debug("======================================================");
		for(int i=0; i< itemList.size(); i++) {
			StandardItem item = itemList.get(i);
			logger.debug("{} : item - {}", i, item);
			// top level
			if(item.getLevel() == 0) {
				message.setName(item.getName());
				continue;
			}
			
			if(item.getLevel() == 1) {
				message.addItem(item);
			}
			else {
				// get parent & add
				int levelDiff = (item.getLevel() - pLevel);
				if( levelDiff == 0) {
					// do nothing					
				}
				else if( levelDiff == 1) {
					parent = stack.peek();
					parentPath = stackPath.peek();
//					logger.debug("<< PEEK = {}", parent.getName());
				}
				else if(levelDiff < 0) {
					while(item.getLevel() > parent.getLevel()
							&& !stack.isEmpty()) {
						parent = stack.pop();
						parentPath = stackPath.pop();
//						logger.debug("<< POP = {}", parent.getName());
					}
				}
				else {
					throw new Exception("item level error - "+ item.getName() +" level diff=" + levelDiff);
				}
				
				if(parent.getType() == StandardType.GROUP) {
//					logger.debug("add group child item - {}", item);
					parent.addItem(item);
				}
				else if(parent.getType() == StandardType.GRID) {
					parent.addArrayItem(0, item);
				}
				else {
					throw new Exception("item parent is not GROUP/ARRAY : item - " + item);
				}
			}
			
			if(item.getType() == StandardType.BIZDATA) {
				if(item.getLevel() == 1) {
					message.setBizDataPath(item.getName());
				}
				else {
					message.setBizDataPath(getFullPath(parentPath , item.getName()));
				}
				logger.debug("@@@ BIZ DATA PATH = {}", message.getBizDataPath());
			}
			
			// if group/array,push to stack, pLevel
			if(item.getType() > 1) {
				logger.debug(">> PUSH = {}", item.getName());
				
				if(parent.getLevel() == item.getLevel()) {
//					logger.debug(">> POP revious sibling = {}", parent.getName());
					stack.pop();
					stackPath.pop();
				}
				stackPath.push( getFullPath(parentPath , item.getName()) );
				stack.push(item);
			}
			else {
				if(item.getDataType() == StandardDataType.LL_NUMBER) {
					if(item.getLevel() == 1) {
						message.setLlDataPath(item.getName());
					}
					else {
						message.setLlDataPath(getFullPath(parentPath , item.getName()));
					}
					logger.debug("@@@ LL_NUMBER PATH = {}", message.getLlDataPath());
					
				}
				else if(item.getDataType() == StandardDataType.ZZ_STRING) {
					if(item.getLevel() == 1) {
						message.setZzDataPath(item.getName());
					}
					else {
						message.setZzDataPath(getFullPath(parentPath , item.getName()));
					}
					logger.debug("@@@ ZZ_STRING PATH = {}", message.getZzDataPath());
					
				}
			}
			pLevel = item.getLevel();
		}
		return message;
	}
	
	public static StandardMessage generate(ArrayList<StandardItem> itemList) throws Exception {
		int itemIdx = 0;
		String parentPath = "";

		if (itemList == null || itemList.size() == 0) {
			return null;
		}

		// MESSAGE 항목이 없다면 추가
		if (itemList.get(0).getType() != StandardType.MESSAGE) {
			StandardItem rootItem = new StandardItem("StandardRoot", 0, StandardType.MESSAGE, 0,
					StandardDataType.STRING, null);
			itemList.add(0, rootItem);
		}

		StandardMessage message = new StandardMessage();
		return (StandardMessage) setRecursiveItems(message, itemList, itemIdx, parentPath);
	}

	private static StandardItem setRecursiveItems(StandardMessage message, ArrayList<StandardItem> itemList,
			int itemIdx, String parentPath) {
		StandardItem curItem = itemList.get(itemIdx);
		if (curItem.getType() == StandardType.MESSAGE) {
			message.setName(curItem.getName());
			curItem = message;
		}

		if (curItem.getLevel() <= 1) {
			parentPath = "";
		}

		if (curItem.getType() == StandardType.MESSAGE || curItem.getType() == StandardType.GROUP
				|| curItem.getType() == StandardType.GRID) {
			for (int i = itemIdx + 1; i < itemList.size(); i++) { // sub item 처리
				StandardItem item = itemList.get(i);
				if (item.getLevel() <= curItem.getLevel()) {
					break;
				}

				if (item.getLevel() == curItem.getLevel() + 1) {
					if (curItem.getType() == StandardType.GRID) {
						curItem.addArrayItem(0,
								setRecursiveItems(message, itemList, i, getFullPath(parentPath, curItem.getName() + "[0]")));
					} else {
						curItem.addItem(
								setRecursiveItems(message, itemList, i, getFullPath(parentPath, curItem.getName())));
					}
				}
			}
		} else {
			if (curItem.getType() == StandardType.BIZDATA) {
				message.setBizDataPath(getFullPath(parentPath, curItem.getName()));
				logger.debug("@@@ BIZ DATA PATH = {}", message.getBizDataPath());
			}

			if (curItem.getDataType() == StandardDataType.LL_NUMBER) {
				message.setLlDataPath(getFullPath(parentPath, curItem.getName()));
				logger.debug("@@@ LL_NUMBER PATH = {}", message.getLlDataPath());

			} else if (curItem.getDataType() == StandardDataType.ZZ_STRING) {
				message.setZzDataPath(getFullPath(parentPath, curItem.getName()));
				logger.debug("@@@ ZZ_STRING PATH = {}", message.getZzDataPath());

			}
		}

		return curItem;
	}
	
	private static String getFullPath(String parentPath, String itemName) {
		if(parentPath == null || parentPath.length() == 0) {
			return itemName;
		}
		return parentPath + "." + itemName;
	}
	
	public static int getArrayIndex(String path) {
		int index = -1;
		
		if(path.charAt(path.length()-1) != ']') {
			return index;
		}
		int start = path.indexOf("[");
		if(start > 0) {
			String p = path.substring(start + 1, path.length()-1);
			try {
				index = Integer.parseInt(p);
			}
			catch(NumberFormatException e) {
				;
			}
		}
		return index;
	}
	
	public static String getArrayName(String path) {
		if(path.charAt(path.length()-1) != ']') {
			return path;
		}
		int start = path.indexOf("[");
		if(start > 0) {
			String p = path.substring(0, start);
			return p;
		}
		else {
			return path;
		}
	}
	
	public static void main(String[] args) {
//		logger.debug("{}", getArrayIndex("group[10]"));
//		logger.debug("{}", getArrayName("group[10]"));
//		logger.debug("{}", getArrayIndex("group"));
//		logger.debug("{}", getArrayName("group"));
		
//		String basePath = "D:\\Projects\\workspaces\\current_elink4.5_online_multi\\elink-online-multi\\";
		String basePath = "D:\\SOURCE\\v4.5-beta-multi\\elink-online-multi\\";
		
		String filePath = basePath + "src\\main\\resources\\standard-layout.csv";
//		String filePath = basePath + "src\\main\\resources\\standard-layout-sbi.csv";
		
		
		StandardMessage stdmsg = generateMessageFromCsvFile(filePath);
		// System.out.println("===========================================================================");
		// System.out.println(stdmsg.getChilds().size());
		// System.out.println(stdmsg);
		// System.out.println("===========================================================================");
		// System.out.println(stdmsg.getLlDataPath());
		// System.out.println(stdmsg.getBizDataPath());
		// System.out.println(stdmsg.getZzDataPath());
		// System.out.println("===========================================================================");
	}
}
