package com.eactive.eai.transformer.message;


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

import org.apache.commons.lang3.StringUtils;

import com.eactive.eai.transformer.layout.Item;
import com.eactive.eai.transformer.layout.LayoutTypeKey;
import com.eactive.eai.transformer.layout.Types;
import com.eactive.eai.transformer.util.ByteUtil;
import com.eactive.eai.transformer.util.CommonLib;
import com.eactive.eai.transformer.util.Constants;
import com.eactive.eai.transformer.util.IConv;


/**
 * <pre>
 * BYTE ARRAY(전문) 형식의 데이터를 변환하기 위한 메시지.
 * 전문의 길이는 데이터의 길이
 *
 * [제약 사항]
 * 필드가 1 개인 통전문
 *
 * [PATH 정의]
 * PATH           = <ITEM_TYPE> + <STATIC_PATH> + <DYNAMIC_PATH>
 * ITEM_TYPE      = 'F' | 'G' | 'M'
 * STATIC_PATH    = OFFSET
 * </pre>
 */
public class FullBytesMessage extends Message {
	private static final long serialVersionUID = -6300557731836672170L;
	public static final String NAME = LayoutTypeKey.FULLBYTES;
    protected byte[] data;
    protected boolean bStaled = false;

    public FullBytesMessage() {
        super();
    }

    public void createObject() {
        data = new byte[0];
    }
    
    @Override
    public int getInt(String itemPath) { 	
        try {
            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 byte[]) {
            	return Integer.parseInt(new String(((byte[]) value)).trim());
            } else {
                throw new InvalidAccessException("레이아웃항목의 값이 숫자가 아님", itemPath);
            }
        } catch (Exception e) {
            throw new InvalidAccessException(e.getMessage(), itemPath, this, e);
        }
    }    
    
    @Override
    public void setInt(String itemPath, int value) {
    	int newValue =  value; 
        super.setInt(itemPath, newValue);
        
        Item item = getOrCreateItem(itemPath, true);
        try {
        	setBytes(itemPath, StringUtils.leftPad(String.valueOf(value), item.getLength(), "0").getBytes());
        } catch(Exception e) {
        	throw new MessageException(e.getMessage(), e);
        }
    }
    
    @Override
    public void setObject(String itemPath, Object value) {
    	if (itemPath == null) {
    		throw new InvalidAccessException("cannot set object - itemPath is null - message : " + getName(), itemPath);
    	}
    	
    	if (!itemPath.startsWith(getName())) {
    		itemPath = getName() + Types.FIELD_SEPARATOR + itemPath;
    	}
    	
        Item item = getOrCreateItem(itemPath, true);

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

        item.setValue(value);
        int length =0;
        if (value instanceof byte[]){
        	length = ((byte[])value).length;
        }else if (value instanceof String){
        	length = ((String)value).getBytes().length;
        }else {
        	throw new InvalidDataTypeException ("지원하지 않는 데이터 타입입니다." +value.getClass(),value);
        }
        item.setLength(length);

        composeData(item, value);
    }
    
    protected void composeData(Item item, Object value) {
        if (item.getIndex() < 0)
            return;
        
        if (!bStaled) {
            bStaled = true;
        }
    }

    public Object getData() throws InvalidDataTypeException {
        if (bStaled) {
            try {
                data = toByteArray();
            } catch(Exception e) {
                throw new InvalidDataTypeException(e.getMessage(),"FullBytesMessage.getData() method", e);   
            }
            bStaled = false;
        }

        return getDataBytes();
    }
    
    protected byte[] toByteArray() {
    	try (
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
		){
			for (Iterator iter = iterator(); iter.hasNext();) {
				Item item = (Item) iter.next();
	
				/**
				 * index == -1 인 경우는 무시한다
				 */
				if (item == null || item.getIndex() == -1) {
					continue;
				}
	
		        if(item.getNodeTypeId() == NODE_FIELD) {
				    if (item.getValue() == null) {
		            	bos.write( new byte[item.getLength()] );
		            } else if (item.getValue() instanceof String) {
		            	bos.write( ByteUtil.padding((String) item.getValue(), item.getLength()) );
		            } else if (item.getValue() instanceof Number) {
		            	bos.write( ByteUtil.padding(((Number) item.getValue()).toString(), item.getLength()) );
		            } else {
		            	bos.write( ByteUtil.padding((byte[]) item.getValue(), item.getLength()) );
		            }	            
				}
			}
			return  bos.toByteArray();			 
    	} catch(Exception e) {
    		logger.error("retrun zero-bytes.", e);
    		return new byte[0];
    	}
    }    

    protected void doSetData(Object data) {
        this.data = (byte[]) data;
        byte[] byteData = ByteUtil.copy(this.data);
        if(byteData == null) {
        	byteData = new byte[0];
        }
        setDataBytes(byteData);
    }
    
    // only for FullBytesMessage & CommonFullBytesMessage
    protected byte[] setDataBytes(byte[] data) {
    	int originalLength = data.length;
    	Item item = null;
    	
		for (Iterator iter = iterator(); iter.hasNext();) {
			item = (Item) iter.next();
			
			if(item == null) {
				continue;
			}
	        
			/**
			 * index == -1 인 경우는 무시한다
			 */
			if (item.getIndex() == -1) {		
				continue;
			}

	        if (item.getNodeTypeId() == NODE_FIELD) {
				try {
	            	item.setValue(ByteUtil.cut(data, data.length));
	            	item.setLength(data.length);
				} catch(Exception e) {
					StringBuilder sb = new StringBuilder();
		        	sb.append("전문레이아웃 매핑 중 데이터 사이즈 오류 ("+e.getMessage()+")");
		        	sb.append("\n");
		        	sb.append("전문레이아웃="+item.getItemPath());
		           throw new MessageException(sb.toString());
				}
				data = new byte[0];
			}
		}		
        if (data.length > 0) {
        	StringBuilder sb = new StringBuilder();
        	sb.append("전문레이아웃 매핑 중 데이터 사이즈 오류 (정의한 길이=" + (originalLength-data.length) +", 받은 길이="+ originalLength + ")");
        	sb.append("\n");
        	sb.append("전문레이아웃="+(item==null?"":item.getItemPath()));
            throw new MessageSetDataOverflowException(sb.toString());
        }
        return data;
    }        
    

    //--------------------------------------------------------------------------
    // Additional Getter/Setter 

    public byte[] getDataBytes() throws InvalidDataTypeException {
        return data;
    }

    public int getDataLength() {
        if (data == null) {
            return -1;
        } else {
            return data.length;
        }
    }

    public void clearData() {
        clearData((byte) 0x00);
        clearValue();
    }

    public void clearData(byte fillByte) {
        if (data != null) {
            for (int i = 0; i < data.length; i++) {
                data[i] = fillByte;
            }
        }
    }
    
    public String toLogString() {
        return toLogString(false);
    }

    public String toLogString(boolean ebcdic) {
        if (ebcdic) {
            byte[] buff = makeBytesLogString();
            return IConv.ebcdicToASCIIWithSOSIEX(buff);
        } else {
            byte[] buff = makeBytesLogString();
            return new String(buff);
        }
    }    
    
    protected byte[] makeBytesLogString() {
    	try (
    		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		){
			for (Iterator iter = iterator(); iter.hasNext();) {
				Item item = (Item) iter.next();
	
				/**
				 * index == -1 인 경우는 무시한다
				 */
				if (item == null || item.getIndex() < 0) {
					continue;
				}
	
		        if(item.getNodeTypeId() == NODE_FIELD) {
					if ("Y".equals(item.getMask())) {
						String mString = maskString(item.getLength());
						if(mString == null) {
							bos.write( "".getBytes() );
						}
						else {
							bos.write( mString.getBytes() );
						}
		            } else {
			            if (item.getValue() == null) {
			            	bos.write( new byte[item.getLength()] );
			            } else if (item.getValue() instanceof String) {
			            	bos.write( ByteUtil.padding((String) item.getValue(), item.getLength()) );
			            } else if (item.getValue() instanceof Number) {
			            	bos.write( ByteUtil.padding(((Number) item.getValue()).toString(), item.getLength()) );
			            } else {
			            	bos.write( ByteUtil.padding((byte[]) item.getValue(), item.getLength()) );
			            }
		            }
		        }
			}
			return  bos.toByteArray();			 
    	} catch(Exception e) {
    		logger.error("retrun zero-bytes.", e);
    		return new byte[0];
    	}
    }    

    @Override
    public String toString() {
        try {
            getData();
        } catch (InvalidDataTypeException e) {
            logger.error("[FullBytesMessage] " + e.getMessage(), e);
        }

        StringBuilder sb = new StringBuilder();
        if ("FullBytesMessage".equals(getClass().getSimpleName())) {
        	sb.append(getName() + " (FullBytesMessage)");
        }
        sb.append(super.toString());
        if (data != null) {
            sb.append("\n" + Constants.TAB + " | data = " + CommonLib.hexDumpTab(data));
        }
        sb.append("\n" + Constants.TAB + " +--------------------------------------------------------------------------------------------------+");

        return sb.toString();
    }
}
