package com.eactive.eai.transformer.message;

import java.util.Iterator;

import com.eactive.eai.transformer.layout.Item;
import com.eactive.eai.transformer.layout.Types;
import com.eactive.eai.transformer.util.ByteUtil;


/**
 * <pre>
 * BYTE ARRAY(전문) 형식의 데이터를 변환하기 위한 메시지.
 * 전체전문의 특정필드값이 참조값에 의한 가변길이일 경우 필드값 length를 참조값으로 치환하여 처리한다.
 * 기본적인 로직은 VariableBytesMessage 와 동일하며 배열부분 로직을 수정하였으므로 이 특성은 배열을 지원하지 않는다.
 *
 * KSH 2014.04.15
 * </pre>
 */
public class VariableFieldMessage extends BytesMessage {
    public static final String NAME = "VFIELD";

    public VariableFieldMessage() {
        super();
    }

    
    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;
			/**
			 * occType = * | + | (occMax>occMin) 일 경우
			 * occRef를 참조하여 그 갯수만큼 item을 생성한다.
			 */
	        if (item.getIndex()==-1 && ("*".equals(item.getOccType()) || "+".equals(item.getOccType()) || item.getOccMax()>item.getOccMin())) {
	        	if (item.getNodeTypeId() == item.NODE_FIELD && item.getLength() == 0) {
	        		int occCount = 0;
	        		String path = "";
	        		try {
	        			path = getOccRefItem(item);
	        			occCount = item.getRoot().getInt(path);
	        		}
	        		catch(Exception oe) {
	        			//throw new InvalidAccessException(item.getName() +"의 반복참조정보 값이 숫자가 아닙니다 - " + " : " + oe.getMessage() , path);
		            	logger.warn("반복참조정보 값 에러 : "+occCount , oe);
	        		}
	        		
	        		if (occCount < item.getOccMin() 
	        				|| (!"*".equals(item.getOccType()) && !"+".equals(item.getOccType()) && occCount > item.getOccMax())) { 
	        			throw new InvalidAccessException("반복횟수가 OccMin~OccMax 의 범위를 벗어납니다. , " + item.getName(), item.getItemPath());
	        		}
	        		//가변길이 셋팅
	        		item.setLength(occCount); 
	        		
	        		/**
	        		 * 반복횟수 참조정보가 비정상적으로 클때 방어로직
	        		 * 오류처리로직) 반복횟수 참조정보 > 셋팅하는 실제 데이터 
	        		 */
	        		
	        		if ( occCount > data.length){
	        			throw new InvalidAccessException(item.getName() +"의 반복참조정보 값("+occCount+") 보다 실제데이터 길이("+data.length+")가 작습니다. "  , path);
	        		}
	        		
	        		//0번 배열 추가
	        		item.createItem(item, 
	        				item.getRoot().normalizeFullName(item.getParent().getItemPath() + Types.FIELD_SEPARATOR + item.getName() + "[" + 0 + "]"), 
	        				false);
	        		/**
	        		 * item 이 속해있는 childList 크기가 증가로 인해, iterator를 다시 생성한다.
	        		 */
	        		iter = item.getRoot().iterator();
	        		for (;iter.hasNext();) {	
	        			if (item.equals(iter.next())) {
	        				break;
	        			}
	        		}
	        	}else { //VField 외의 값 : 기본동작로직
	        		int occCount = 0;
	        		String path = "";
	        		try {
	        			path = getOccRefItem(item);
	        			occCount = item.getRoot().getInt(path);
	        		}
	        		catch(Exception oe) {
	        			//throw new InvalidAccessException(item.getName() +"의 반복참조정보 값이 숫자가 아닙니다 - " + " : " + oe.getMessage() , path);
	        			logger.warn("반복참조정보 값 에러 : "+occCount, oe);
	        		}
	        		
	        		if (occCount < item.getOccMin() 
	        				|| (!"*".equals(item.getOccType()) && !"+".equals(item.getOccType()) && occCount > item.getOccMax())) { 
	        			throw new InvalidAccessException("반복횟수가 OccMin~OccMax 의 범위를 벗어납니다. , " + item.getName(), item.getItemPath());
	        		}
	        		
	        		/**
	        		 * 반복횟수 참조정보가 비정상적으로 클때 방어로직
	        		 * 그리드 오류처리로직) 자식배열 길이합계 * 반복횟수 참조정보 > 셋팅하는 실제 데이터 
	        		 * 필드   오류처리조직) 필드 길이            * 반복횟수 참조정보 > 셋팅하는 실제 데이터
	        		 */
	        		int sumChildLength = 0;
	        		Item childItem= null;
	        		if (item.isGroup()){
	        			for(int i=0;i<item.getChildList().size();i++){
	        				childItem = item.getChild(i);
	        				if (childItem.getIndex() == -1){
	        					continue;
	        				}
	        				sumChildLength += item.getChild(i).getLength();
	        			}
	        			if (sumChildLength * occCount > data.length){
	        				throw new InvalidAccessException(item.getName() +"의 반복참조정보 값("+occCount+") * 자식배열 길이합계("+sumChildLength+") = 계산된길이("+(sumChildLength * occCount)+") 보다 실제데이터 길이("+data.length+")가 작습니다. "  , path);
	        			}
	        		}else{
	        			if (item.getLength() * occCount > data.length){
	        				throw new InvalidAccessException(item.getName() +"의 반복참조정보 값("+occCount+") * 길이("+item.getLength()+") = 계산된길이("+(item.getLength() * occCount)+") 보다 실제데이터 길이("+data.length+")가 작습니다. "  , path);
	        			}
	        		}
	        		
	        		item.createItem(item, 
	        				item.getRoot().normalizeFullName(item.getParent().getItemPath() + Types.FIELD_SEPARATOR + item.getName() + "[" + (occCount-1) + "]"), 
	        				false);
	        		
	        		/**
	        		 * item 이 속해있는 childList 크기가 증가로 인해, iterator를 다시 생성한다.
	        		 */
	        		iter = item.getRoot().iterator();
	        		for (;iter.hasNext();) {	
	        			if (item.equals(iter.next())) {
	        				break;
	        			}
	        		}
	        	}
	        }
	        
			/**
			 * index == -1 인 경우는 무시한다
			 */
			if (item.getIndex() == -1) {		
				continue;
			}

	        switch (item.getNodeTypeId()) {
			case NODE_FIELD:
				try {
	            	item.setValue(ByteUtil.cut(data, item.getLength()));
				} catch(Exception e) {
					// Padding space
					byte[] space = new byte[item.getLength()];
					for (int i=0; i<item.getLength(); i++) {
						space[i] = (byte)0x20;
					}
					item.setValue(space);
				}
				
				try {
					if(data != null && data.length >= item.getLength()) {
						data = ByteUtil.sub(data, item.getLength());
					}
				} catch(Exception e) {
					StringBuffer sb = new StringBuffer();
//		        	sb.append("error when set value to item - " + item.getName());
//		        	sb.append(" "+e.getMessage());
//					throw new MessageException("error when set value to item - " + item.getName(), e);
					sb.append("전문레이아웃 매핑 중 데이터 셋팅 오류 ("+ e.getMessage() + ")");
		        	sb.append("\n");
		        	sb.append("전문레이아웃="+item.getItemPath());
		            throw new MessageException(sb.toString());
				}
	            break;
			}
		}		

        if (data.length > 0) {
        	StringBuffer sb = new StringBuffer();
//        	sb.append("setData() : " + item.getItemPath() + " : data size가 맞지 않습니다. (overflow) ");
//        	sb.append("length=" + (originalLength-data.length) + "(+" + data.length + ")");
//        	sb.append("defined length=" + (originalLength-data.length) + ", received length=" + originalLength+". ");
        	sb.append("전문레이아웃 매핑 중 데이터 사이즈 오류 (전체 정의한 길이=" + (originalLength-data.length) +", 받은 길이="+ originalLength + ")");
        	sb.append("\n");
        	sb.append("전체 파싱 후에 데이터가 남음,  길이="+ data.length);
//        	sb.append("마지막 전문레이아웃="+(item==null?"":item.getItemPath()));
            throw new MessageSetDataOverflowException(sb.toString());
        }
        
        return data;
    }
    
  
    protected byte[] toByteArray() {
//    	byte[] bytes = new byte[0];
    	
    	
    	//보정 작업
    	for (Iterator iter = iterator(); iter.hasNext();) {
			Item item = (Item) iter.next();
			/**
			 * index == -1 인 경우는 무시한다
			 */
			
			if (item ==null || item.getIndex() == -1) {
				continue;
			}
			int occCount = 0;
	        switch (item.getNodeTypeId()) {
			case NODE_FIELD:
				//특정필드의 value를 얻어오는 logic
				if( item!=null && 
					("*".equals(item.getOccType()) && item.getLength() ==0) ){
					try{
//						occCount = item.getRoot().getInt(item.getParent().getItemPath() + Types.FIELD_SEPARATOR + item.getOccRef());
						String path = getOccRefItem(item);
	        			occCount = item.getRoot().getInt(path);
	        			item.setLength(occCount);
					}catch(Exception e) {
						logger.warn("반복참조정보 값 에러 : "+occCount, e);
					}
				}
				break;
			case NODE_GROUP:
				if("*".equals(item.getOccType()) ){
					occCount = item.getRoot().getInt(item.getParent().getItemPath() + Types.FIELD_SEPARATOR + item.getOccRef());
					if (occCount != item.arraySize()){
						item.getRoot().setInt(item.getParent().getItemPath() + Types.FIELD_SEPARATOR + item.getOccRef(), item.arraySize());
					}
							
				}
				break;
				
	        }
    	
    	}
    	return super.toByteArray();

    }       
    public String toString() {
        
        StringBuffer sb = new StringBuffer();
        
        sb.append(getName() + " (VFIELD message)");

        sb.append("\n" + super.toString());
        //sb.append("\n");

        return sb.toString();
    }
    

}
