package com.eactive.eai.transformer.message;

import java.math.BigDecimal;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;

import com.eactive.eai.transformer.layout.Composite;
import com.eactive.eai.transformer.layout.Item;
import com.eactive.eai.transformer.layout.ItemFullIterator;
import com.eactive.eai.transformer.layout.ItemIterator;
import com.eactive.eai.transformer.layout.Layout;
import com.eactive.eai.transformer.layout.Types;
import com.eactive.eai.transformer.util.ByteUtil;
import com.eactive.eai.transformer.util.Constants;
import com.eactive.eai.transformer.util.Logger;

/**
 * 메시지 추상화 클래스
 */
abstract public class Message extends Composite {
	
    private static final long serialVersionUID = 1L;
	protected static Logger logger = Logger.getLogger("transformer");
    protected Layout layout;

    protected Message() {
    	super();
    	setNodeTypeId(Item.NODE_MESSAGE);
    }

    public String getType() {
        return getLayout().getLayoutType().getName();
    }

    // [DEPENDENCY] Types
    public int getInt(String itemPath) {
    	if (itemPath != null && !itemPath.startsWith(getName())) {
    		itemPath = getName() + Types.FIELD_SEPARATOR + itemPath;
    	}

        Item item = getItem(itemPath, true);

        if (item == null) {
        	throw new InvalidAccessException("레이아웃항목이 존재하지 않음", itemPath);
        }

        Object value = item.getValue();

        if (value == null) {
            return 0;
        } else if (value instanceof Number) {
            return ((Number) value).intValue();
        } else if (value instanceof String) {
            return Integer.parseInt((String) value);
        } else if (value instanceof byte[]) {
            return Integer.parseInt(new String((byte[]) value).trim());
        } else {
        	throw new InvalidAccessException("레이아웃항목의 값이 숫자가 아님", itemPath);
        }
    }

    public long getLong(String itemPath) {
    	if (itemPath != null && !itemPath.startsWith(getName())) {
    		itemPath = getName() + Types.FIELD_SEPARATOR + itemPath;
    	}

        Item item = getItem(itemPath, true);

        if (item == null) {
        	throw new InvalidAccessException("레이아웃항목이 존재하지 않음", itemPath);
        }

        Object value = item.getValue();

        if (value == null) {
            return 0L;
        } else if (value instanceof Number) {
            return ((Number) value).longValue();
        } else if (value instanceof String) {
            return Long.parseLong((String) value);
        } else if (value instanceof byte[]) {
            return Long.parseLong(new String((byte[]) value).trim());
        } else {
        	throw new InvalidAccessException("레이아웃항목의 값이 숫자가 아님", itemPath);
        }
    }

    public short getShort(String itemPath) {
    	if (itemPath != null && !itemPath.startsWith(getName())) {
    		itemPath = getName() + Types.FIELD_SEPARATOR + itemPath;
    	}

        Item item = getItem(itemPath, true);

        if (item == null) {
        	throw new InvalidAccessException("레이아웃항목이 존재하지 않음", itemPath);
        }

        Object value = item.getValue();

        if (value == null) {
            return 0;
        } else if (value instanceof Number) {
            return ((Number) value).shortValue();
        } else if (value instanceof String) {
            return Short.parseShort((String) value);
        } else if (value instanceof byte[]) {
            return Short.parseShort(new String((byte[]) value).trim());
        } else {
        	throw new InvalidAccessException("레이아웃항목의 값이 숫자가 아님", itemPath);
        }
    }

    public double getDouble(String itemPath) {
    	if (itemPath != null && !itemPath.startsWith(getName())) {
    		itemPath = getName() + Types.FIELD_SEPARATOR + itemPath;
    	}

        Item item = getItem(itemPath, true);

        if (item == null) {
        	throw new InvalidAccessException("레이아웃항목이 존재하지 않음", itemPath);
        }

        Object value = item.getValue();

        if (value == null) {
            return 0.0;
        } else if (value instanceof Number) {
            return ((Number) value).doubleValue();
        } else if (value instanceof String) {
            return Double.parseDouble((String) value);
        } else if (value instanceof byte[]) {
            return Double.parseDouble(new String((byte[]) value).trim());
        } else {
        	throw new InvalidAccessException("레이아웃항목의 값이 숫자가 아님", itemPath);
        }
    }

    public float getFloat(String itemPath) {
    	if (itemPath != null && !itemPath.startsWith(getName())) {
    		itemPath = getName() + Types.FIELD_SEPARATOR + itemPath;
    	}

        Item item = getItem(itemPath, true);

        if (item == null) {
        	throw new InvalidAccessException("레이아웃항목이 존재하지 않음", itemPath);
        }

        Object value = item.getValue();

        if (value == null) {
            return 0.0f;
        } else if (value instanceof Number) {
            return ((Number) value).floatValue();
        } else if (value instanceof String) {
            return Float.parseFloat((String) value);
        } else if (value instanceof byte[]) {
            return Float.parseFloat(new String((byte[]) value).trim());
        } else {
        	throw new InvalidAccessException("레이아웃항목의 값이 숫자가 아님", itemPath);
        }
    }

    public byte[] getByteArray(String itemPath) {
    	if (itemPath != null && !itemPath.startsWith(getName())) {
    		itemPath = getName() + Types.FIELD_SEPARATOR + itemPath;
    	}

        Item item = getItem(itemPath, true);

        if (item == null) {
        	throw new InvalidAccessException("레이아웃항목이 존재하지 않음", itemPath);
        }

        Object value = item.getValue();

        if (value == null) {
            return ByteUtil.padding(new byte[0], item.getLength());
        } else if (value instanceof Number) {
            return (((Number) value).toString()).getBytes();
        } else if (value instanceof String) {
            return ((String) value).getBytes();
        } else if (value instanceof byte[]) {
            return (byte[]) value;
        } else {
            return (byte[]) value;
        }
    }

    public String getString(String itemPath) {
    	if (itemPath != null && !itemPath.startsWith(getName())) {
    		itemPath = getName() + Types.FIELD_SEPARATOR + itemPath;
    	}

        Item item = getItem(itemPath, true);

        if (item == null) {
        	throw new InvalidAccessException("레이아웃항목이 존재하지 않음", itemPath);
        }

        Object value = item.getValue();

        if (value == null) {
            return null;
        } else if (value instanceof Number) {
            return ((Number) value).toString();
        } else if (value instanceof byte[]) {
            return new String((byte[]) value);
        } else if (value instanceof String) {
            return (String) value;
        } else {
        	throw new InvalidAccessException("레이아웃항목의 값이 문자가 아님", itemPath);
        }
    }

    public Object getObject(String itemPath) {
    	if (itemPath != null && !itemPath.startsWith(getName())) {
    		itemPath = getName() + Types.FIELD_SEPARATOR + itemPath;
    	}

        Item item = getItem(itemPath, true);

        if (item == null) {
        	throw new InvalidAccessException("레이아웃항목이 존재하지 않음", itemPath);
        }

        Object value = item.getValue();

        return value;
    }

    public void setInt(String itemPath, int value) {
    	if (itemPath != null && !itemPath.startsWith(getName())) {
    		itemPath = getName() + Types.FIELD_SEPARATOR + itemPath;
    	}

        try {
        	Item item = getOrCreateItem(itemPath, true);

            if (item == null) {
            	throw new InvalidAccessException("레이아웃항목이 존재하지 않음", itemPath);
            }

            item.setValue(new Integer(value));

            composeData(item, new Integer(value));
        } catch (Exception e) {
            throw new MessageSetDataException("Cannot setInt '" + value + "' to '" + itemPath + "'", this, new Integer(value), e);
        }
    }

    public void setLong(String itemPath, long value) {
    	if (itemPath != null && !itemPath.startsWith(getName())) {
    		itemPath = getName() + Types.FIELD_SEPARATOR + itemPath;
    	}

        Item item = getOrCreateItem(itemPath, true);

        if (item == null) {
        	throw new InvalidAccessException("레이아웃항목이 존재하지 않음", itemPath);
        }

        item.setValue(new Long(value));

        composeData(item, new Long(value));
    }

    public void setShort(String itemPath, short value) {
    	if (itemPath != null && !itemPath.startsWith(getName())) {
    		itemPath = getName() + Types.FIELD_SEPARATOR + itemPath;
    	}

        Item item = getOrCreateItem(itemPath, true);

        if (item == null) {
        	throw new InvalidAccessException("레이아웃항목이 존재하지 않음", itemPath);
        }

        item.setValue(new Short(value));

        composeData(item, new Short(value));
    }

    public void setDouble(String itemPath, double value) {
    	if (itemPath != null && !itemPath.startsWith(getName())) {
    		itemPath = getName() + Types.FIELD_SEPARATOR + itemPath;
    	}

        Item item = getOrCreateItem(itemPath, true);

        if (item == null) {
        	throw new InvalidAccessException("레이아웃항목이 존재하지 않음", itemPath);
        }

        item.setValue(new Double(value));

        composeData(item, new Double(value));
    }

    public void setFloat(String itemPath, float value) {
    	if (itemPath != null && !itemPath.startsWith(getName())) {
    		itemPath = getName() + Types.FIELD_SEPARATOR + itemPath;
    	}

        Item item = getOrCreateItem(itemPath, true);

        if (item == null) {
        	throw new InvalidAccessException("레이아웃항목이 존재하지 않음", itemPath);
        }

        item.setValue(new Float(value));

        composeData(item, new Float(value));
    }

    public void setBigDecimal(String itemPath, BigDecimal value) {
    	if (itemPath != null && !itemPath.startsWith(getName())) {
    		itemPath = getName() + Types.FIELD_SEPARATOR + itemPath;
    	}

        Item item = getOrCreateItem(itemPath, true);

        if (item == null) {
        	throw new InvalidAccessException("레이아웃항목이 존재하지 않음", itemPath);
        }
        
        item.setValue(value);

        composeData(item, value);
    }
    
    public void setString(String itemPath, String value) throws InvalidAccessException {
        	setString(itemPath, value, false);
    }
    public void setString(String itemPath, String value,boolean isConvertPass) throws InvalidAccessException {    	
    	if (itemPath != null && !itemPath.startsWith(getName())) {
    		itemPath = getName() + Types.FIELD_SEPARATOR + itemPath;
    	}

        Item item = getOrCreateItem(itemPath, true);
        if (item == null) {
        	throw new InvalidAccessException("레이아웃항목이 존재하지 않음", itemPath);
        }
        
        item.setValue(value,isConvertPass);
        
        composeData(item, value);      
    }

    public void setBytes(String itemPath, byte[] value) {
    	setByteArray(itemPath, value);
    }

    public void setByteArray(String itemPath, byte[] value) {
    	if (itemPath != null && !itemPath.startsWith(getName())) {
    		itemPath = getName() + Types.FIELD_SEPARATOR + itemPath;
    	}

        Item item = getOrCreateItem(itemPath, true);

        if (item == null) {
        	throw new InvalidAccessException("레이아웃항목이 존재하지 않음", itemPath);
        }

        item.setValue(value);

        composeData(item, value);
    }
    
    public void setDefaultByteArray(String itemPath, byte[] value) {
    	if (itemPath != null && !itemPath.startsWith(getName())) {
    		itemPath = getName() + Types.FIELD_SEPARATOR + itemPath;
    	}

        Item item = getOrCreateItem(itemPath, true);

        if (item == null) {
        	throw new InvalidAccessException("레이아웃항목이 존재하지 않음", itemPath);
        }

        item.setValue(value);

        composeData(item, value);
    }
    
    public void setObject(String itemPath, Object value) {
    	if (itemPath == null) {
    		throw new InvalidAccessException("cannot set object - itemPath is null - message : " + getName(), itemPath);
    	}
    	
    	if (itemPath != null && !itemPath.startsWith(getName())) {
    		itemPath = getName() + Types.FIELD_SEPARATOR + itemPath;
    	}
    	
        Item item = getOrCreateItem(itemPath, true);

        if (item == null) {
        	throw new InvalidAccessException("레이아웃항목이 존재하지 않음", itemPath);
        }

        item.setValue(value);

        composeData(item, value);
    }
        
    public void setData(Object data) throws MessageSetDataException {
     	try {
    		doSetData(data);
    	} catch(Exception e) {
    		MessageSetDataException ex = new MessageSetDataException("메시지 데이터 설정 중 오류 "+e.getMessage(), this, data, e);
    		throw ex;
    	}
    }

    abstract public void createObject();
    
    abstract protected void composeData(Item item, Object value);

    abstract protected void doSetData(Object data);
    
    abstract public Object getData();

    abstract public String toLogString();

    //abstract public String toLogString(boolean ebcdic);

    public Layout getLayout() {
        return layout;
    }

    public void setLayout(Layout layout) {
        this.layout = layout;
    }
    
    /**
     * iterator 
     */
    @Override
    public Iterator iterator() {
    	if (isTop()) {
    		return new ItemIterator(childList.iterator());
    	} else {
    		return childList.iterator();
    	}
    }      

    public Iterator fullIterator() {
    	if (isTop()) {
    		return new ItemFullIterator(childList.iterator());
    	} else {
    		return childList.iterator();
    	}
    }  
    /**
     * 2Depth 위에 있는 참조를 찾기 위해 추가
     * 2020-04-02
     * @param item
     * @return
     */
    public String getOccRefItem(Item item) {
    	String itemPath =item.getParent().getItemPath() + Types.FIELD_SEPARATOR + item.getOccRef();
    	if (itemPath != null && !"".equals(itemPath)){
    		return itemPath;
    	}
//		String itemPath = "";
//		Item occRefItem = null;
//		Item tempItem  =  null;
//		tempItem = item.getParent();
//		do{
//			itemPath = tempItem.getItemPath() + Types.FIELD_SEPARATOR + item.getOccRef() + "[0]";
//			occRefItem = item.getTop().getItem(itemPath);
//			if (occRefItem != null){
//				return occRefItem.getItemPath();
//			}
//			
//			if (item.getTop() == tempItem){
//				break;
//			}
//			tempItem = tempItem.getParent();
//		}while(true);   
//		
//		tempItem  = item.getParent();
//		do{
//			itemPath = tempItem.getItemPath() + Types.FIELD_SEPARATOR + item.getOccRef() + "[-1]";
//			occRefItem = item.getTop().getItem(itemPath);
//			if (occRefItem != null)
//				return occRefItem.getItemPath();
//			
//			if (getTop() == tempItem){
//				break;
//			}
//			tempItem = tempItem.getParent();
//		}while(true);
		
		return itemPath;
    }

    public String toString() {
        StringBuffer sb = new StringBuffer();

        sb.append("\n" + Constants.TAB + " +--------------------------------------------------------------------------------------------------+");
        if (layout != null) {
        	sb.append(layout.toString(Constants.TAB));
        }
        sb.append("\n" + Constants.TAB + " +--------------------------------------------------------------------------------------------------+");
        sb.append("\n" + Constants.TAB + " | " + super.toString());

        return sb.toString();
    }
    
    public String toXMLString() {
        StringBuffer sb = new StringBuffer();

//        sb.append("<?xml version=\"1.0\" encoding=\"euc-kr\"?>");
//        if (layout != null) {
//        	sb.append(layout.toXMLString(Constants.TAB));
//        }

        sb.append("\n" + Constants.TAB + "   " + super.toXMLString());

//        sb.append("\n" + Constants.TAB + "</layout>");

        return sb.toString();
    } 

    public void addItemToStack(Stack<Item> itemStack, List itemList) {
    	if(itemList == null || itemList.size() < 1) return;
    	for(int p=itemList.size()-1; p>=0; p--) {
    		itemStack.push( (Item)itemList.get(p) ); 
    	}
    }
    
    /**
     * 그룹 변환에서 사용되는 API.
     * FIXME : 아래의 3 함수는 사용하는 부분이 없으므로 제거.
     */
//    abstract public MessageData getMessageData(String itemPath) throws Exception;
//    
//    abstract public void setMessageData(String itemPath, MessageData messageData);
//
//    abstract public void copy(Message message) throws Exception;
    //--------------------------------------------------------------------------
}
