package com.eactive.eai.transformer;

import java.util.Iterator;

import com.eactive.eai.transformer.engine.TransformEngine;
import com.eactive.eai.transformer.engine.TransformException;
import com.eactive.eai.transformer.engine.TransformResult;
import com.eactive.eai.transformer.engine.Transformer;
import com.eactive.eai.transformer.layout.Item;
import com.eactive.eai.transformer.layout.Layout;
import com.eactive.eai.transformer.layout.LayoutManager;
import com.eactive.eai.transformer.layout.LayoutTypeKey;
import com.eactive.eai.transformer.message.Message;
import com.eactive.eai.transformer.message.MessageFactory;
import com.eactive.eai.transformer.message.XMLMessage;
import com.eactive.eai.transformer.transform.Transform;
import com.eactive.eai.transformer.transform.TransformManager;
import com.eactive.eai.transformer.util.Logger;
import com.eactive.eai.transformer.util.MemoryLogger;
import com.eactive.eai.transformer.util.XmlUtil;

public class TransformService {
    protected static Logger logger = Logger.getLogger("transformer");
    
    private TransformService() {
    	
    }
    
	@SuppressWarnings("unchecked")
	public static Object transform(String infoKey, boolean isReq, String transformName, Object[] sources, boolean isMass) throws java.lang.Exception {

		Transform transform = null;
		Message[] sourceMessages = null;
		Message targetMessage = null;
		
		try {
			if (transformName == null)
				throw new RuntimeException("전문레이아웃매핑(변환)이름을 정의하지 않았습니다. - " + transformName);
			
			if (sources == null)
				throw new RuntimeException("전문레이아웃매핑(변환) 소스 데이터가  NULL 입니다.");

			TransformEngine engine = TransformEngine.getInstance();
			Transformer transformer = engine.getTransformer();

			sourceMessages = new Message[sources.length];

			TransformManager manager = TransformManager.getManager();
			transform = manager.getTransform(transformName);
			if (transform == null) {
				throw new Exception("전문레이아웃매핑(변환)정보를 찾을 수 없음 - " + transformName);
			}

			for (int i = 0; i < sources.length; i++) {
				Layout l = null;
				try {
					l = transform.getSourceLayout(i);
				}
				catch(Exception ex) {
					throw new Exception("전문레이아웃매핑(변환) 중  변환등록오류 - 소스레이아웃 미등록");
				}
				
				if(l == null || l.getName() == null || l.getName().length() == 0) {
					throw new Exception("전문레이아웃매핑(변환) 중 소스레이아웃 생성 오류 - 변환정보에 소스레이아웃 명이 없음");
				}
				try {
					sourceMessages[i] = MessageFactory.getFactory().getMessage(l.getName());
					
					String sourceType = l.getLayoutType().getName();
					
					if(LayoutTypeKey.JSON.equals(sourceType)) {
//						sourceMessages[i].setData((String) sources[i]);
						// FIX-ME : Socket으로  JSON이 송수신되는 경우, json bytes가 올 수 있음
						String jsonString = "";
						if (sources[i] instanceof byte[]) {
							jsonString = new String((byte[]) sources[i]);
						} else {
							jsonString = (String) sources[i];
						}
						sourceMessages[i].setData(jsonString);
					}
					else if(LayoutTypeKey.XML.equals(sourceType)) {
						String xmlString = "";
						if (sources[i] instanceof byte[]) {
							xmlString = new String((byte[]) sources[i]);
						} else {
							xmlString = (String) sources[i];
						}

						String rootItemName = ((XMLMessage) sourceMessages[i]).getLayout().getRootItem().getName();

						// 비표준 XML의 경우를 고려하여 XML Header 제거 후 Root 추가 
						String xmlAddString = "";
						xmlString = XmlUtil.removeXMLHeader(xmlString);
						xmlAddString = "<" + rootItemName + ">" + xmlString + "</" + rootItemName + ">";
						sourceMessages[i].setData(xmlAddString);
					} 
//					else if (LayoutTypeKey.BYTES.equals(sourceType) || LayoutTypeKey.EBCDIC.equals(sourceType)) {
//						sourceMessages[i].setData(sources[i]);
//					}
					else {
						sourceMessages[i].setData(sources[i]);
					}
				} catch (Exception e) {
					throw new TransformerException("전문레이아웃매핑(변환) 중 소스레이아웃 생성 오류 - ", e);
				}
			}

			try {
				Object resultObj =null;
				if (isMass){
					Layout t = transform.getTargetLayout();
					targetMessage = MessageFactory.getFactory().getMessage(t.getName());
            		//참조정보 제거
            		for (Iterator<Item> iter = targetMessage.fullIterator(); iter.hasNext(); ) {
            			Item x = iter.next();
            			if (x.getOccRef() !=null && x.getOccRef().trim().length() > 0 ){
            				x.setOccRef("");
            				logger.info(infoKey+"] Mass 거래 OccRef 제거 target "+ x.getPathName());
            			}
            		}
					TransformResult result = transformer.transform(transformName, targetMessage, sourceMessages, null);
					resultObj = result.getResultMessage().getData();
				}else{
					targetMessage = transformer.transform(transformName, sourceMessages);
					resultObj = targetMessage.getData();
				}
				/**
				 * memory logging
				 */
				memoryLog(infoKey, isReq, transformName, sourceMessages, targetMessage);
				
				String targetType = transform.getTargetLayout().getLayoutType().getName();

				if(LayoutTypeKey.XML.equals(targetType)) {
					String resultXml = (String) resultObj;
					String rootItemName = ((XMLMessage) targetMessage).getLayout().getRootItem().getName();
					return removeXMLRootItem(resultXml, rootItemName);
				} else {
					return resultObj;
				}
			} catch (Exception e) {
				throw new TransformException("전문레이아웃매핑(변환) 중 오류 - " + transformName,
	                    transformName, 
	                    targetMessage, 
	                    sourceMessages,
	                    null,
	                    e);
			}
		} catch (Exception e) {
			logger.error(getErrorSummary(transform, sourceMessages, targetMessage, e, false), e);
			MemoryLogger.error(getErrorSummary(transform, sourceMessages, targetMessage, e, true), e);
			throw e;
		}
	}

	private static String removeXMLRootItem(String xmlString, String rootItemName) {
		String prefix = "<" + rootItemName + ">";
		String suffix = "</" + rootItemName + ">";

		int s_idx = xmlString.indexOf(prefix);
		int e_idx = xmlString.indexOf(suffix);
		if (s_idx == -1 || e_idx == -1) {
			return xmlString;
		} else {
			return xmlString.substring(s_idx + prefix.length(), e_idx);
		}
	}
	
	public static Object transformSingle(String infoKey, boolean isReq, String transformName,
			Object sources) throws java.lang.Exception {
		return transform(infoKey, isReq, transformName, new Object[] { sources },false);
	}

	public static Object transformComp(String infoKey, boolean isReq, String transformName,
			Object[] sources, String[] layoutNames) throws Exception {

		Transform transform = null;
		Message[] sourceMessages = null;
		Message targetMessage = null;

		try {
			if (transformName == null)
				throw new RuntimeException("전문레이아웃매핑(변환)이름을 정의하지 않았습니다. - "
						+ transformName);
			if (sources == null)
				throw new RuntimeException("전문레이아웃매핑(변환) 소스 데이터가  NULL 입니다.");
			if (layoutNames == null)
				throw new RuntimeException("복합 거래의 결과 레이아웃이  NULL 입니다.");

			TransformEngine engine = TransformEngine.getInstance();
			Transformer transformer = engine.getTransformer();

			sourceMessages = new Message[sources.length];

			TransformManager manager = TransformManager.getManager();
			transform = manager.getTransform(transformName);
			if (transform == null) {
				throw new Exception("전문레이아웃매핑(변환)규칙을 찾을 수 없습니다. - " + transformName);
			}

			boolean exsist = false;
			// 전문레이아웃매핑(변환)규칙의 소스 레이아웃 정보안에 파라미터로 받은 레이아웃이 있는지 확인함
			for (int i = 0; i < layoutNames.length; i++) {
				if (layoutNames[i] != null) {
					exsist = false;
					for (int j = 0; j < transform.getSourceLayoutCount(); j++) {
						Layout layout = transform.getSourceLayout(j);
						if (layout != null 
								&& layoutNames[i].equals(layout.getName())) {
							exsist = true;
							break;
						}
					}
					if (!exsist) {
						throw new RuntimeException(transformName
								+ " 전문레이아웃매핑(변환)규칙에서 복합거래의 결과 레이아웃 [" + layoutNames[i] + "] 이 존재하지 않습니다.");
					}
				}
			}

			for (int i = 0; i < layoutNames.length; i++) {
				Layout l = null;
				try {
					l = LayoutManager.getManager().getLayoutByName(layoutNames[i]);
					if (l != null)
						sourceMessages[i] = MessageFactory.getFactory().getMessage(l.getName());

					if (l != null && sources[i] != null) {
						String sourceType = l.getLayoutType().getName();
						if (LayoutTypeKey.XML.equals(sourceType)) {
							String xmlString = null;
							if (sources[i] instanceof byte[]) {
								xmlString = new String((byte[]) sources[i]);
							} else {
								xmlString = (String) sources[i];
							}
							String rootItemName = ((XMLMessage) sourceMessages[i]).getLayout()
										.getRootItem().getName();
							xmlString = XmlUtil.removeXMLHeader(xmlString);
							xmlString = "<" + rootItemName + ">" + xmlString + "</" + rootItemName + ">";
							sourceMessages[i].setData(xmlString);
						} 
						else if (LayoutTypeKey.JSON.equals(sourceType)) {
							sourceMessages[i].setData((String) sources[i]);							
						} 
//						else if (LayoutTypeKey.BYTES.equals(sourceType) || LayoutTypeKey.EBCDIC.equals(sourceType)) {
//							sourceMessages[i].setData(sources[i]);
//						} 
						else {
							sourceMessages[i].setData(sources[i]);
						}
					}
				} catch (Exception e) {
					throw new TransformerException("전문레이아웃매핑(변환) 소스 레이아웃을 생성할 수 없습니다",  e);
				}
			}
			try {
				targetMessage = transformer.transform(transformName, sourceMessages);
				Object resultObj = targetMessage.getData();

				/**
				 * memory logging
				 */
				memoryLog(infoKey, isReq, transformName, sourceMessages, targetMessage);
				String targetType = transform.getTargetLayout().getLayoutType().getName();
//				if (targetType.equals("XML") || targetType.equals("UXML")) {
				if (LayoutTypeKey.XML.equals(targetType)) {
					String resultXml = (String) resultObj;
					String rootItemName = ((XMLMessage) targetMessage).getLayout().getRootItem().getName();
					return removeXMLRootItem(resultXml, rootItemName);
				} else {
					return resultObj;
				}
			} catch (Exception e) {
				throw new TransformException("전문레이아웃매핑(변환) 중 오류 - " + transformName,
	                    transformName, 
	                    targetMessage, 
	                    sourceMessages,
	                    null,
	                    e);
			}
		} catch (Exception e) {
			logger.error(getErrorSummary(transform, sourceMessages, targetMessage, e, false), e);
			MemoryLogger.error(getErrorSummary(transform, sourceMessages, targetMessage, e, true), e);
			throw e;
		}
	}

	private static String getErrorSummary(Transform transform, Message[] sourceMessages, Message targetMessage, Exception exception, boolean isHtml) {
		StringBuilder sb = new StringBuilder();
		sb.append("전문레이아웃매핑(변환) 중 오류 - " + exception.getMessage());
		try {
			if (transform != null) {
				sb.append("transform=");
				if (isHtml)
					sb.append("<a href='/transform/" + transform.getName() + "'>");
				sb.append(transform.getName());
				if (isHtml)
					sb.append("</a>");
			}
			
			if (sourceMessages != null) {
				sb.append(", sources=(");
				for (int i=0; i<sourceMessages.length; i++) {
					if (sourceMessages[i] == null)
						continue;
					
					if (i>0)
						sb.append(",");
					if (isHtml)
						sb.append("<a href='/message/" + sourceMessages[i].getName() + "'>");
					sb.append(sourceMessages[i].getName());
					if (isHtml)
						sb.append("</a>");
				}
				sb.append(")");
			}
			
			if (targetMessage != null && transform != null && transform.getTargetLayout() != null) {
				if (isHtml)
					sb.append("<a href='/message/" + transform.getTargetLayout().getName() + "'>");
				sb.append(", target=(" + transform.getTargetLayout().getName() + ")");
				if (isHtml)
					sb.append("</a>");
			}
			sb.append("\n");
			return sb.toString();
		} catch(Exception e) {
			logger.warn(e.getMessage(), e);
			return sb.toString();
		}
	}
	
	private static void memoryLog(String infoKey, boolean isReq, String transformName,
			Message[] sources, Message target) {
		if (!MemoryLogger.isMemoryLogEnabled()) return;
		try {
			StringBuilder sb = new StringBuilder();
			sb.append(String.format("infoKey=%s,  %-5s,  transformName=%s\n",
					(infoKey==null ? "" : infoKey.trim()),
					isReq ? "REQ" : "RES",
					transformName));
			sb.append("<b>transform = " + transformName + "</b>\n");
			sb.append(TransformManager.getManager().getTransform(transformName).toString());
	    	for (int i=0; i<sources.length; i++) {
	    		sb.append("\n<b>sources[" + i + "] = " + sources[i].getName() + "</b>");
	    		sb.append("\n" + sources[i].toString());
	    	}
			sb.append("\n<b>target = " + target.getName() + "</b>");
			sb.append("\n" + target.toString());
	    	MemoryLogger.txlog(sb.toString());
		} catch(Exception e) {
			logger.error(transformName + " | " + e.getMessage(), e);
		}
	}
}
