package com.eactive.eai.transformer.message;

import java.io.UnsupportedEncodingException;
import java.util.Iterator;

import com.eactive.eai.transformer.function.util.CodeConversion;
import com.eactive.eai.transformer.layout.Item;
import com.eactive.eai.transformer.util.ByteUtil;
import com.eactive.eai.transformer.util.CommonLib;
import com.eactive.eai.transformer.util.IConv;

/**
 * <pre>
 * CICS CARD 전문을 위한 메시지타입.
 * Delimeter방식으로 처리됨
 * </pre>
 */
public class CardMessage extends BytesMessage {	  
//    //------------------------------------------------------------
//    // Card Delimeter 정의 : 20080228 DHLEE
//    //------------------------------------------------------------
//    private static final byte TH = (byte) 0xFF; // Text Header 구분자
//    private static final byte FD = (byte) 0x2A; // Field Delimeter
//    private static final byte LF = (byte) 0x0A; // Line Feed
//    private static final byte CD = (byte) 0x1A; // Cell Delimeter
//    private static final byte RD = (byte) 0x1B; // Record Delimeter
//    //------------------------------------------------------------	
	
	public static final String NAME = "CARD";
	protected byte[] data;
	private boolean bStaled = false;

	public CardMessage() {
		super();
	}

	@Override
	public void createObject() {
		data = new byte[0];
	}

	@Override
	protected void composeData(Item item, Object value) {
		if (item.getIndex() < 0)
			return;

		if (!bStaled) {
			bStaled = true;
		}
	}

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

		return getDataBytes();
	}

	@Override
	protected void doSetData(Object data) {
		this.data = (byte[]) data;
		int rcvLength = this.data.length;

		byte[] _data = ByteUtil.copy(this.data);

		// System.out.println(CommonLib.getDumpMessage(_data));
		// ------------------------------------------
		// TODO : Card의 Delimeter 방식으로 parsing 하도록 수정해야 함
		// 전문의 HEADER부를 처리하는 로직이 필요
		// OP Message LL 과 OP Message를 처리하는 방안
		// 1. OP Message LL, OP Message를 떼어내고 Parsing 하는 방법
		// ------------------------------------------
		// Delimeter방식
		// byte[] result = setCardDataBytes((byte[]) _data);

		byte[] result = setCardFixedDataBytes((byte[]) _data);

		if (result.length > 0) {
			StringBuilder sb = new StringBuilder(10000);
			sb.append("[CardMessage] setData() : " + getItemPath() + " : data 갯수가 맞지 않습니다. ");
			sb.append("length=" + rcvLength + ", (+" + result.length + ")");
			sb.append("\nData Dump (Original Message) = " + CommonLib.hexDump((byte[]) data));
			sb.append("\nMessage Dump = \n" + toString());
			logger.error(sb.toString());
			throw new InvalidAccessException("[CardMessage] setData() : " + getItemPath()
					+ " : data 갯수가 맞지 않습니다. length=" + rcvLength + ", (+" + result.length + ")",
					getItemPath());
		}
	}

	// ----------------------------------------------------------
	// only for CardMessage : 고정길이 방식
	// CardMessage의 setData() 함수의 recursive 처리를 위한 함수
	// ----------------------------------------------------------
	private byte[] setCardFixedDataBytes(byte[] data) throws InvalidAccessException {
		if(data == null) return new byte[0];
		for (Iterator iter = iterator(); iter.hasNext();) {
			Item item = (Item) iter.next();
			if(item == null) continue;
			switch (item.getNodeTypeId()) {
			case NODE_FIELD:
				if (item.getIndex() != -1) {
					try {
						item.setValue(ByteUtil.cut(data, item.getLength()));
					} catch (Exception e) {
						throw new InvalidAccessException("error when set value to item - " + (item==null?"":item.getName()), e);
					}
					data = ByteUtil.sub(data, item.getLength());
				}
				break;
			case NODE_GROUP:				
				break;
			case NODE_MESSAGE:
				// 1. OP LL, OP Message 처리
				// 2. COMMON HEADER 처리
				byte[] opLL = new byte[2];
				// Read OP LL
				System.arraycopy(data, 0, opLL, 0, opLL.length);

				// Calculate OP Message Length
				int opLength = 0;
				try {
					opLength = ByteUtil.checkLengthField(opLL);
				} catch (Exception e) {
					logger.error(e.getMessage(), e);
				}
				// System.out.println("OP LENGTH = " + opLength);
				// GET COMMON HEADER LENGTH - KM240
				byte[] headerLL = new byte[3];
				System.arraycopy(data, opLength + 2, headerLL, 0, headerLL.length);

				int leaderLength = 0;
				try {
					leaderLength = Integer
							.parseInt(new String(CodeConversion.seToAsc(headerLL)));
				} catch (Exception e) {
					logger.error(e.getMessage(), e);
				}
				// System.out.println("HEADER LENGTH = " + leaderLength);
				// Truncate Unusable Header Bytes
				data = ByteUtil.sub(data, opLength + leaderLength);
				break;
			default :
				break;
			}
		}
		return data;
	}
	
//    //----------------------------------------------------------
//    // only for CardMessage : Delimeter 방식
//    // CardMessage의 setData() 함수의 recursive 처리를 위한 함수
//    //----------------------------------------------------------
//    protected byte[] setCardDataBytes(byte[] data) {
//        //-----------------------------------------------------------------------
//        // Record Delimeter의 갯수을 얻어내고 그값에 따라 레이아웃아이템을 만든다.
//        // DHLEE
//        //-----------------------------------------------------------------------
//        int dataLength = 0;
//        if(data != null) dataLength = data.length;
//        
//        if (getIndex()==-1 && ("*".equals(getOccType()) || "+".equals(getOccType()) || getOccMax() > getOccMin())) {
//            
//            //int occCount = getRoot().getInt(getParent().getItemPath() + Types.FIELD_SEPARATOR + getRefOcc());
//            int occCount = 0;
//            for (int i = 0; i < dataLength; i++) {            
//                if( (data[i] == RD) ) {
//                    occCount ++;
//                    if( ((i+1) < dataLength) && (data[i] == FD) ) break;
//                }
//            }
//            
//            if (occCount < getOccMin() || (!"*".equals(getOccType()) && !"+".equals(getOccType()) && occCount > getOccMax())) 
//                throw new InvalidAccessException("반복횟수가 OccMin~OccMax 의 범위를 벗어납니다. , " + info(), getItemPath());
//
//            try {
//                createItem(this, getRoot().normalizeFullName(getParent().getItemPath() + Types.FIELD_SEPARATOR + getName() + "[" + (occCount-1) + "]"), false);
//            } catch (InvalidNodeException e) {
//                throw new InvalidAccessException(getItemPath(), e);
//            }
//        }
//
//        if (isField()) {
//            if (getIndex() != -1) {
//                
//                byte[] result = null;
//                byte[] remain = null;
//                                
//                try {
//                	//---------------------------------------------------
//                    // Card Delimeter Parsing : 20080228 DHLEE
//                    //---------------------------------------------------
//                    if(dataLength == 0) return new byte[0];
//                    int startPos = 0;
//                    
//                    for (int i = 0; i < dataLength; ) {
//                        if((data[i] == TH)) {
//                            i = i + 3;
//                            startPos = 4;
//                            continue;
//                        }
//                        else if((data[i] == FD) || (data[i] == CD) || (data[i] == RD)) {
//                            if(i == 0) {
//                                result = new byte[0];
//                            }
//                            else {
//                                result = new byte[i-startPos];
//                                
//                                System.arraycopy(data, startPos, result, 0, i-startPos);
//                                //--------------------------------------------------
//                                // ListView 에서 Table의 마지막에 R다음에 D가 오는 경우
//                                // 확인필요 :DHLEE
//                                //--------------------------------------------------
//                                if( (i+1 < dataLength) && (data[i] == RD) && (data[i+1] == FD)) {
//                                    remain = new byte[dataLength - (i+2)];
//                                    System.arraycopy(data, (i+2), remain, 0, remain.length);
//                                }
//                                else {
//                                    remain = new byte[dataLength - (i+1)];
//                                    System.arraycopy(data, (i+1), remain, 0, remain.length);
//                                }
//                            }
//                            break;
//                        }
//                        else {
//                            i++;
//                        }
//                    }                    
//                    setValue(result);
//                    //---------------------------------------------------
//                    
//				} catch(Exception e) {
//                    throw new InvalidAccessException(this.getItemPath(), e);	
//				}
//                return remain;
//            }
//            return data;
//        // group || message
//        } else {
//                        
//            if (isGroup() && (getIndex() == -1)) {
//                return data;
//            }
//
//            try {
//                // set data
//                childList = getChildList();
//
//                for (int i = 0; i < childList.size(); i++) {
//                    Item child = (Item) childList.get(i);
//                    data = child.setCardDataBytes(data);
//                }
//
//                return data;
//            } catch (Exception e) {
//                throw new InvalidAccessException(this.getItemPath(), e);
//            }
//        }
//    }	

	
    public byte[] toCardByteArray() {
    	return null;
    }
    
//    public byte[] toCardByteArray() {
//        //if(logger.isInfoEnabled()) logger.info("    [Item (" + getItemPak() + ")] toByteArray() : " + value + " : " + (value==null ? "" : value.getClass().getName())+", length : "+length);
//        // field
//        byte[] returnBytes = null;
//        byte[] FD = {(byte)0x2A};
//        byte[] RD = {(byte)0x1B};
//        
//        if (isField()) {
//        	try {
//	            if (getIndex() == -1) {
//	                return null;
//	            } else if (value == null) {
//                    returnBytes = FD;
//                } else if (value instanceof String) {
//	                returnBytes = ByteUtil.merge(((String)value).getBytes(), FD);
//	            } else if (value instanceof Number) {
//                    returnBytes = ByteUtil.merge(((Number) value).toString().getBytes(), FD);
//	            } else {
//	                returnBytes = ByteUtil.merge((byte[])value, FD);
//	            }
//                return returnBytes;
//            } catch(Exception e) {
//                logger.error(e.getMessage(), e);
//                logger.error("    [Item1 (" + getItemPath() + ")] toCardByteArray() : " + value + " : "+length+" : " + (value==null ? "" : value.getClass().getName()));
//                throw new RuntimeException(e.getMessage());
//            }
//            // group || message
//        } else {
//            if (isGroup() && (getIndex() == -1)) {
//                return null;
//            }
//
//            byte[] byteValue = null;
//
//            try {
//                List childList = getChildList();
//
//                for (int i = 0; i < childList.size(); i++) {
//                    Item child = (Item) childList.get(i);
//                    byteValue = ByteUtil.merge(byteValue, child.toCardByteArray());
//                }
//                // Group의 Record Delimeter 추가
//                if (isGroup()) {
//                    byteValue = ByteUtil.merge(RD, byteValue);
//                }
//                
//            } catch (Exception e) {
//                logger.error(e.getMessage(), e);
//                logger.error("    [Item2 (" + getItemPath() + ")] toCardByteArray() : " + value + " : "+length+" : " + (value==null ? "" : value.getClass().getName()));
//                throw new RuntimeException(e.getMessage());
//            }
//
//            return byteValue;
//        }
//    }

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

	/**
	 * 그룹 변환에서 사용되는 API.
	 */
//	public MessageData getMessageData(String itemPath) {
//		return null;
//	}
//
//	/**
//	 * 그룹 변환에서 사용되는 API.
//	 */
//	public void setMessageData(String itemPath, MessageData messageData) {
//	}
//
//	public void copy(Message message) throws InvalidMessageException {
//	}

	// ELSE---------------------------------------------------------------------

	@Override
	public byte[] getDataBytes() throws InvalidDataTypeException {
		/*
		 * if (isNotVariableDataUpdated()) { validateVariable(); }
		 */
		return data;
	}

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

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

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

	@Override
	public String toLogString() {
		return toLogString(false);
	}

	@Override
	public String toLogString(boolean ebcdic) {
		if (ebcdic) {
			byte[] buff = makeBytesLogString();

			return IConv.ebcdicToASCII(buff);
		} else {
			byte[] buff = makeBytesLogString();

			return new String(buff);
		}
	}

	@Override
	public String toString() {
		// if staled -> reorganize data

		try {
			getData();
		} catch (InvalidDataTypeException e) {
			logger.error("[CardMessage] " + e.getMessage(), e);
		}

		StringBuilder sb = new StringBuilder();
		sb.append("[CARD message]");

		if (data != null) {
			sb.append("\n    data = " + CommonLib.hexDump(data));
		}

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

		return sb.toString();
	}

	@Override
	public int getInt(String itemPath) {
		Item item = getItem(itemPath, true);

		if (item == null) {
			throw new InvalidAccessException("아이템의 참조 정보가 없습니다. - " + itemPath, itemPath);
		}

		Object value = item.getValue();
		if (value == null) {
			return 0;
		} else if (value instanceof byte[]) {
			try {
				return Integer.parseInt(new String((byte[]) value, "cp933").trim());
			} catch(UnsupportedEncodingException e) {
				throw new MessageException(e.getMessage(), e);
			}
		} else {
			throw new InvalidAccessException("Invalid한 데이타가 value에 저장되어 있읍니다.", itemPath);
		}
	}

	@Override
	public void setInt(String itemPath, int value) {
		int newValue = value;

		super.setInt(itemPath, newValue);
	}
}
