package kr.gmtc.gw.asderecv.asde.parser;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import kr.gmtc.gw.asderecv.asde.asterixSpec.AsterixSpecLoader;
import kr.gmtc.gw.asderecv.asde.asterixSpec.vo.AsterixSpecVO;
import kr.gmtc.gw.asderecv.asde.asterixSpec.vo.AsterixSpecVO_Codes;
import kr.gmtc.gw.asderecv.asde.asterixSpec.vo.AsterixSpecVO_DataItem;
import kr.gmtc.gw.asderecv.asde.asterixSpec.vo.AsterixSpecVO_Structure;
import kr.gmtc.gw.asderecv.asde.utils.AsterixParserUtils;
import kr.gmtc.gw.asderecv.asde.utils.FieldNameReflectionUtils;
import kr.gmtc.gw.asderecv.rest.vo.ServiceAsdeData;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;


public class AsterixParser {
	
	private Logger logger;

	private AsterixSpecLoader asterixSpec;
	
	private List<ServiceAsdeData> serviceMap;
	private AsterixSpecVO currentSpec;

	private LinkedHashMap<String, String> resultMap ;
	private ServiceAsdeData scMap ;
	
	private AsterixParserUtils apUtils;
	private FieldNameReflectionUtils fieldUtils;
	
	private DecimalFormat decFormat = new DecimalFormat("#.#########");

	private String debugLogMode = "none";
	
	/***************************************** Constructor & Main Method *****************************************/
	
	public AsterixParser()
    {
		initParser();
    }
	
	public AsterixParser( List<ServiceAsdeData> serviceMap) {
		
		initParser();
		this.serviceMap = serviceMap;
        
	}
	
	private void initParser() {
		
        logger = LoggerFactory.getLogger(AsterixParser.class);

        fieldUtils = new FieldNameReflectionUtils();
		apUtils = new AsterixParserUtils();
        asterixSpec = new AsterixSpecLoader();
		
	}
	
	public List<LinkedHashMap<String, String>> parse(byte[] data, String sRecvTime) {
        String sCatNo = null;
        try
        {
            if(data.length < 5) return null;
            
            // 바이트 값을 16진수(0xff, 255)와 비트연산하여 정수로 변환
            sCatNo = String.valueOf(data[0] & 0xff);

            // 데이터부의 category와 일치하는 스펙 가져오기
            currentSpec = asterixSpec.getSpec(sCatNo);

            if(currentSpec == null ) {
                logger.error("Asterix Spec이 정의되어 있지 않음. [수신메시지의 category:" + sCatNo + "]");
            	return null;
            }else {
            	if (currentSpec.getDataitem().isEmpty()) {
                    logger.error("Asterix Spec yml파일에 구문 오류가 있음. [수신메시지의 category:" + sCatNo + "]");
            	}
            }
            
            // 데이터 블럭의 분석 결과
            List<LinkedHashMap<String, String>> result_list = new ArrayList<LinkedHashMap<String, String>>();
    	    
            // 수신받은 데이터 총길이
    	    int total_len = data.length;
    	    
    	    // 데이터블럭 사이즈 (데이터 블럭(1) : 데이터 패킷(N))
    	    byte[] blSize = new byte[2];
    	    
    	    // 분석 처리 대상 블럭
    	    byte[] blockData = null;

    	    // 한번에 여러개의 블럭을 수신 받은경우 반복하여 처리 하기위한 블럭단위 초기 인덱스
    	    // 1개 블럭의 마지막 인덱스를 저장
    	    int index = 0;
    	    
    	    // 데이터 블록에 정의되어 있는 총 길이 만큼 잘라서 분석하고 (real_data)
    	    // 남아 있는 데이터가 있다면 새로운 데이터블록이므로 반복하여 처리
    	    while(total_len > index)
    	    {
//    	        int iCatNo = data[index] & 0xff;
    	        
    	    	// 데이터 블록의 총 길이(2옥텟)
    	    	blSize[0] = data[index+1];
    	    	blSize[1] = data[index+2];
    	        
    	    	int iDataBlockSize = apUtils.unsignedToInt(blSize);
    	        int iBlockStartPos = 3;
    	        
//    	        logger.error("[AsterixParser:parse] 수신 데이터 총 길이:" + total_len + " / 분석대상 블록 길이" +  iDataBlockSize, LogLevelType.LOG_DEBUG, "AllLog");
    	        
    	        if(iDataBlockSize <= 0) return result_list;
    	        
    	        // 데이터 블록의 총 길이만큼 초기화
    	        blockData = new byte[iDataBlockSize];
    	        
    	        try
    	        {
    	        	if((index+iDataBlockSize) > total_len) iDataBlockSize = total_len-index;
    	        	
    	        	 /* System.arraycopy(src, srcPos, dest, destPos, length);
    					src - 원본 배열
    					srcPos - 원본 배열의 복사 시작 위치
    					dest - 복사할 배열
    					destPost - 복사할 배열의 복사 시작 위치
    					length - 복사할 요소의 개수
    		         * */
    	        	System.arraycopy(data, index + iBlockStartPos, blockData, 0, iDataBlockSize - iBlockStartPos);
    	        	
    	        	List<LinkedHashMap<String,String>> list = parseOneBlock(blockData, sRecvTime);
    	        	
    	        	if(list != null) result_list.addAll(list);
    	        }
    	        catch(Exception e)
    	        {
    	        	e.printStackTrace();
                    logger.error("Exception:" + e);
    	        	return result_list;
    	        }
    	        
    	        index += iDataBlockSize;
    	    }
            
            return result_list;
            
        }
        catch(Exception e)
        {
            logger.error("수신메시지 분석 실패 [CAT " + sCatNo + "]");
            return null;
        }
    }
    
	
	private List<LinkedHashMap<String, String>> parseOneBlock(byte[] blockData, String sRecvTime) throws Exception {
	    
	    int body_len = blockData.length;
	    
	    List<LinkedHashMap<String, String>> result = new ArrayList<LinkedHashMap<String, String>>();

	    Map<Integer, AsterixSpecVO_DataItem> mapUAP;
	    
	    int index = 0;
	    int index_before = 0;
	    int idx;
	    
	    while(index < body_len-1)
	    {
	    	
	        int to_read = body_len - index;
	        mapUAP = new LinkedHashMap<Integer, AsterixSpecVO_DataItem>();

	        // UAP 체크
	        for(idx=0; idx<to_read;idx++)
	        {
	            int fspec = blockData[index+idx];
	            if(!makeUAP(mapUAP, idx, fspec)) break;
	        }
	        
	        // UAP가 안만들어진경우 
	        if (mapUAP.isEmpty() || blockData[index+idx] == 0) {
	        
	        	byte[] zeroChkByte = new byte[to_read];
	        	
	        	System.arraycopy(blockData, index, zeroChkByte, 0, to_read);
	        	
	        	int isZeroChk = apUtils.signedToInt(zeroChkByte);

	        	if(isZeroChk == 0) {
	        		break;
	        	}else {
                    logger.error("UAP Null Break [Etc data:" + apUtils.cnvBytesToHex(zeroChkByte) + "]");
                    logger.error("UAP Null Break [Block data:" + apUtils.cnvBytesToHex(blockData) + "]");
	        		break;
	        	}
	        }
	        
	        int iUAPoffset = idx+1;
	        int iRecode_len = body_len - index - iUAPoffset;
	        int iRecodeStartIndex = index + iUAPoffset;
	        
	        if(iRecode_len < 0) break;
	        
	        byte[] byteRecodeData = new byte[iRecode_len];

	        /* System.arraycopy(src, srcPos, dest, destPos, length);
				src - 원본 배열
				srcPos - 원본 배열의 복사 시작 위치
				dest - 복사할 배열
				destPost - 복사할 배열의 복사 시작 위치
				length - 복사할 요소의 개수
	         * */
	        System.arraycopy(blockData, iRecodeStartIndex, byteRecodeData, 0, iRecode_len);
	        
	        Iterator<Integer> it = mapUAP.keySet().iterator();
	        scMap = new ServiceAsdeData();

	        // 카테고리유형 셋팅
	        fieldUtils.setItem(scMap, "cat_ty", currentSpec.getCat());
	        
	        //수신시각 셋팅
	        fieldUtils.setItem(scMap, "recptn_dt", sRecvTime);
        	
	        int result_index = 0;
	        resultMap = new LinkedHashMap<String, String>();
	        while(it.hasNext())
	        {

	            Integer frn = it.next();
	            AsterixSpecVO_DataItem di = mapUAP.get(frn);
	            if(di == null) continue;
	            
	            //resultMap = new LinkedHashMap<String, String>();
	            result_index = makeResult(di, byteRecodeData, result_index);
	            if(result_index < 0) break;

	        }
	        
	        if(resultMap != null && resultMap.size() > 0)
	        {

                if(debugLogMode.equals("line"))
                    logger.debug("parging 결과 :" + resultMap.toString());

	            result.add(resultMap);

	            // 서비스용 map에 적재하되 식별자가 없는 데이터는 스킵
	            // key 값으로 사용할 수 있는 필드에 값이 있는 경우만
	            if( filterSvcData(scMap) ) {
                    CalcScvData(resultMap);
	            	serviceMap.add(scMap);
	            }
	           
	        }
	        
	        index_before = index;
	        
	        index = index + iUAPoffset + result_index;
	        
	        // log
	        byte[] bLogByte = new byte[index - index_before];
	        System.arraycopy(blockData, index_before, bLogByte, 0, index - index_before);
//	        logger.error("[AsterixParser:parseOneBlock] packet [CNT :" + parserCnt  + ", Hex Data :" + apUtils.cnvBytesToHex(bLogByte) + "]", LogLevelType.LOG_DEBUG, "AllLog");
	        
	        if(result_index < 0) break;
	        
	    }
	    
	    return result;
	}
	
	/***************************************** Make Result *****************************************/

    private void CalcScvData(LinkedHashMap<String, String> resMap) {

        double dX = apUtils.parseDoubleDef(resMap.get("X-COMPONENT"));
        double dY = apUtils.parseDoubleDef(resMap.get("Y-COMPONENT"));
        int iXm = apUtils.parseIntDef(resMap.get("XM"));
        int iYm = apUtils.parseIntDef(resMap.get("YM"));
        double iVx = apUtils.parseDoubleDef(resMap.get("VX"));
        double iVy = apUtils.parseDoubleDef(resMap.get("VY"));
        double dDistNM = 1;
        double dAzimuth = 0;
        double dSpeed;
        int iMax = 8191;

        Map<String, Double> posMap;

        // if(resMap.get("CALLSIGN") != null && resMap.get("CALLSIGN").equals("CAL160")){
        //     String dafdf = "";
        // }

        // 위경도 변환 (x,y -> wgs84)
        if(dX != 0 || dY != 0) {
            if( !(iXm == 0 && iYm == 0) ){

                if (iXm == 1) {
                    if(dX > 0) dX = dX + iMax;
                    else if(dX < 0) dX = dX - iMax;
                    else dX = 0;
                }

                if (iYm == 1) {
                    if(dY > 0) dY = dY + iMax;
                    else if(dY < 0) dY = dY - iMax;
                    else dY = 0;
                }

            }

            dDistNM = Math.sqrt(dX * dX + dY * dY) / 1852.0; // m >> NM
            dAzimuth = apUtils.calcAngleDegree(0, 0, dX, dY);

            posMap = apUtils.calcDistanceAzimuth(apUtils.CONST_BASE_LAT, apUtils.CONST_BASE_LON, dDistNM, dAzimuth);

            scMap.setLat(String.format("%.14f", posMap.get("lat")));
            scMap.setLon(String.format("%.14f", posMap.get("lon")));
            //scMap.setCos(String.format("%.2f", dAzimuth));
        }

        // 속도 계산
//        if(resMap.get("cat_ty").equals("11")){
//            dSpeed = Math.sqrt(Math.pow(iVx, 2) + Math.pow(iVy, 2));
//        }
//        else if(resMap.get("cat_ty").equals("10")){
//
//        )
        dSpeed = Math.sqrt(Math.pow(iVx, 2) + Math.pow(iVy, 2));


        dSpeed = dSpeed * 3.6; // m/s >> km/h

        //SX(Vx, 16) = 0 or SY(Vy, 8) = 0 if VX >= 0 or VY >= 0


        fieldUtils.setItem(scMap, "spd", String.format("%.2f", dSpeed));
//        fieldUtils.setItem(scMap, "spd_kts", String.format("%.2f", dSpeed * 0.5399570136727677));

        // 방향 계산
        double dir = apUtils.calcDirection(iVx, iVy);
        fieldUtils.setItem(scMap, "cos", String.format("%.2f", dir));


        // Cat-11은 자체 필드(resMap->LAT값 체크) 사용하되 값이 없으면 계산한 위치값 셋팅
        if( !(resMap.get("LAT") == null || resMap.get("LAT").equals("")) ){
            fieldUtils.setItem(scMap, "lat", resMap.get("LAT"));
            fieldUtils.setItem(scMap, "lon", resMap.get("LON"));

        }else{
            fieldUtils.setItem(scMap, "lat", scMap.getLat());
            fieldUtils.setItem(scMap, "lon", scMap.getLon());
        }

        // fieldUtils.setItem(scMap, "posx", dX + "");
        // fieldUtils.setItem(scMap, "posy", dY + "");

         //cat10 alt
         if( !(resMap.get("FL") == null || resMap.get("FL").equals("")) ){

            // scMap.setAlt(String.valueOf(apUtils.parseDoubleDef(resMap.get("FL"))));

            String sTmpAlt = "";
            double dalt = apUtils.parseDoubleDef(resMap.get("FL"));
            
            //if(dalt > 160 || dalt < 0.7)  dalt = 0;
            if(dalt > 160 )  dalt = 0;
            if(dSpeed <= 100) dalt = 0;


            sTmpAlt = String.valueOf(dalt * 100); // FL -> ft
            
            scMap.setAlt(sTmpAlt);
            
        }

        //cat11 alt
        if( !(resMap.get("MFL") == null || resMap.get("MFL").equals("")) ){

            String sTmpAlt = "";
            double dalt = apUtils.parseDoubleDef(resMap.get("MFL"));
            
            if(dalt > 160 || dalt < 0.7)  dalt = 0;

            sTmpAlt = String.valueOf(dalt * 100); // FL -> ft
            
            scMap.setAlt(sTmpAlt);
        }

        // scMap.setCarty((String) resMap.get("VFI"));
        

    }

	private int makeResult(AsterixSpecVO_DataItem di, byte[] data, int index) {
		
		if(di == null || data == null || index < 0) return -1;
		
		int iRetIndex = -1;
		
		String sFrn = di.getFrn();
		int iSize = Integer.parseInt(di.getOctet());
		List<AsterixSpecVO_Structure> st = di.getStructure();
		
//		if(resultMap == null) resultMap = new LinkedHashMap<String, String>();
		if(scMap == null) scMap = new ServiceAsdeData();
		
		String sFormat = di.getFormat().trim();
		
		if(iSize < 0) return -1;
		if(data.length - index < iSize) {
			iSize = data.length - index;
		}
		
		if(di.getItemno().equals("SPF"))  iRetIndex = makeResult_End(data, index);
		else if(di.getRule().equals("X")) iRetIndex = makeResult_NeverSent(di, data, index, iSize);
		else if(sFormat.equalsIgnoreCase("fixed"))      iRetIndex = makeResult_Fixed(data, index, iSize, st);
		else if(sFormat.equalsIgnoreCase("compound"))   iRetIndex = makeResult_Compound(data, index, sFrn);
		else if(sFormat.equalsIgnoreCase("variable"))   iRetIndex = makeResult_Variable(data, index, sFrn);
		else if(sFormat.equalsIgnoreCase("repetitive")) iRetIndex = makeResult_Repetitive(data, index, di);
		else iRetIndex = -1;
		
		printDebugLog(data, index, iRetIndex, di);
		
		return iRetIndex;
		
    }
	
	private int makeResult_Fixed(byte[] data, int index, int iSize, List<AsterixSpecVO_Structure> itemStrucs) {
        
        int size = iSize;

        byte[] bTargetItem = new byte[size];
        
        System.arraycopy(data, index, bTargetItem, 0, size);
        
        index += size;
        
        for(int i=0; i<itemStrucs.size(); i++)
        {
        	
            AsterixSpecVO_Structure struc = itemStrucs.get(i);
            
            String name = struc.getName().toUpperCase();
            String sResultValue = null ;
            
            if(name == null || name.trim().length() == 0) name = "unknown";
            
            int frombit = Integer.parseInt(struc.getFrombit());
            int tobit = Integer.parseInt(struc.getTobit());
//            int bit_length = frombit - tobit;
            
            int from_octet = apUtils.getOctetSeq(bTargetItem, frombit);
            int to_octet = apUtils.getOctetSeq(bTargetItem, tobit);
            
            if(from_octet == -1 || to_octet == -1) continue;
            
            AsterixSpecVO_Codes itemCodes = struc.getCodes();

            String scale = struc.getResolution();
            String num_min = struc.getMin();
            String num_max = struc.getMax();
            
            if(scale == null || scale.trim().length() == 0) scale = "1";
            
            String dataType = struc.getDatatype();
            
            double no = 0;
            byte[] bnum = null;
            boolean is_string = false;
            
            if(itemCodes == null)
            {
                
                if(from_octet == to_octet)
                {
                	
                	if(dataType.trim().equalsIgnoreCase("signbit")) {
                        no = apUtils.signedToInt_withSignBit_1byte(bTargetItem[to_octet - 1]);
                    }
                    else if(dataType.trim().equalsIgnoreCase("int")) {
                        no = apUtils.signedToInt(new byte[]{bTargetItem[to_octet - 1]});
                	}else {
                		int number = bTargetItem[to_octet - 1] & 0xff;
                        
                        int from8bit = frombit%8;
                        if(from8bit==0) from8bit = 8;
                        
                        int shift = 8 - from8bit;
                        number = number << shift;
                        number = number & 0xff;
                        number = number >>> 8 - (frombit - tobit + 1) ;

                        no = (double)number;
                	}
                    
                }
                else
                {
                    
                	int byte_size = to_octet - from_octet;
                    
                    if(to_octet != from_octet) byte_size += 1;
                    bnum = new byte[byte_size];
                    System.arraycopy(bTargetItem, from_octet-1, bnum, 0, byte_size);
                    
                    is_string = false;
                    
                    if(dataType.trim().equalsIgnoreCase("uint")) {
                    	
                    	if(bnum.length > 4) {
                            logger.error("bnum.length:" + bnum.length );
                        }else {
                        	no = apUtils.unsignedToInt(bnum);
                        }
                    	
                    	
                    }
                    else if(dataType.trim().equalsIgnoreCase("int")) 
                    {
//                        if(bit_length < 16 && bit_length % 16 != 0)
//                        {
//                            int shift = 16 - bit_length - 1;
//                            bnum[0] = (byte)(bnum[0] << shift);
//                            bnum[0] = (byte)(bnum[0] & 0xff);
//                            bnum[0] = (byte)(bnum[0] >>> shift);
//                            bnum[0] = (byte)(bnum[0] & 0xff);
//
//                        }
                        no = apUtils.signedToInt(bnum);
                    }
                    else if(dataType.trim().equalsIgnoreCase("signbit")) 
                    {
                    	no = apUtils.signedToInt_withSignBit(bnum);
                    }
                    else if(dataType.trim().equalsIgnoreCase("octal")) 
                    {
                    	sResultValue = apUtils.makeOctal(bnum);
                        is_string = true;
                    }
                    else if(dataType.trim().equalsIgnoreCase("6bitschar")) 
                    {
                    	sResultValue = apUtils.makeICAOCode(bnum);
                        is_string = true;
                    }
                    else if(dataType.trim().equalsIgnoreCase("string"))
                    {
                    	sResultValue = apUtils.byteArrayToString(bnum);
                        is_string = true;
                        
                    }
                    else if(dataType.trim().equalsIgnoreCase("ascii")) 
                    {
                    	sResultValue = apUtils.makeAscii(bnum);
                        is_string = true;
                    }
                    else sResultValue = "";
                }
                
                if(!is_string)
                {
                	no *= Double.parseDouble(scale);

                    // if(num_min != null){
                    //     if(no < Integer.parseInt(num_min)){
                    //         no = Integer.parseInt(num_min);
                    //     }
                    // }

                    // if(num_max != null){
                    //     if(no > Integer.parseInt(num_max)){
                    //         no = Integer.parseInt(num_max);
                    //     }
                    // }

                	sResultValue = String.valueOf(decFormat.format(no));
                }
            }
            else
            {
                if(from_octet == to_octet)
                {
                    int number = bTargetItem[to_octet - 1] & 0xff;
//                    int shift = (8*size) - frombit;
//                    number = number << shift;
//                    number = number & ( 0xff - shift );
//                    number = number >>> (8 - ((size*8)-(tobit+shift))); 
                    int from8bit = frombit%8;
                    if(from8bit==0) from8bit = 8;
                    
                    int shift = 8 - from8bit;
                    number = number << shift;
                    number = number & 0xff;
                    number = number >>> 8 - (frombit - tobit + 1) ;
                    
                    sResultValue = Integer.toString(number);
                    
                }
                else
                {
                	
                	int byte_size = to_octet - from_octet;
                    
                    if(to_octet != from_octet) byte_size += 1;
                    bnum = new byte[byte_size];
                    System.arraycopy(bTargetItem, from_octet-1, bnum, 0, byte_size);
                    
                	if(bnum.length > 4) {
                    	logger.error("bnum.length:" + bnum.length );
                    	sResultValue = "";
                    }else {
                    	no = apUtils.unsignedToInt(bnum);
                    	sResultValue = String.valueOf(no);
                    }
                	
                }
            }
            
            resultMap.put(name, sResultValue);
            if(struc.getServiceFieldName() != null) {
            	inputServiceField(struc, sResultValue);
            	
            }
            
        }
        
        return index;
    }
	
	private int makeResult_Compound(byte[] data, int index, String sFrn) {
		
		try
        {
            String di_frn = sFrn;
            int idx = 0;
            Map<Integer, AsterixSpecVO_DataItem> compound_UAP = new LinkedHashMap<Integer, AsterixSpecVO_DataItem>();
            
            while(true)
            {
                int fspec = data[index+idx] & 0xff;
                if(!makeUAP_Compound(compound_UAP, idx, fspec, di_frn)) break;
                ++idx;
            }
            
            index += (idx+1);
            
            Iterator<Integer> it = compound_UAP.keySet().iterator();
            
            while(it.hasNext())
            {
            	int idx_after = index;
            	 
                Integer frn = it.next();
                AsterixSpecVO_DataItem _di = compound_UAP.get(frn);
                index = makeResult( _di, data, index);
                
                printDebugLog(data, idx_after, index, _di);
                
                if(index == -1) break;
            }
            
        	 
        }
        catch(Exception e)
        {
            e.printStackTrace();
            logger.error("Exception:" + e + "sFrn:" + sFrn + " data:[" + Arrays.toString(data) + "]");
            index = -1;
        }
        
        return index;
    }
	
	private int makeResult_Variable(byte[] data, int index, String sFrn) {

        try
        {
            String di_frn = sFrn;

            Map<Integer, AsterixSpecVO_DataItem> variable_UAP = new LinkedHashMap<Integer, AsterixSpecVO_DataItem>();
            
            makeUAP_Variable(variable_UAP, di_frn, data, index);
            
            Iterator<Integer> it = variable_UAP.keySet().iterator();
            
            while(it.hasNext())
            {
            	int idx_after = index;
            	
                Integer frn = it.next();
                AsterixSpecVO_DataItem _di = variable_UAP.get(frn);
                index = makeResult(_di, data, index);
                
                printDebugLog(data, idx_after, index, _di);
                
                if(index == -1) break;
            }
        	
            return index;
        }
        catch(Exception e)
        {
            logger.error("Exception:" + e + "sFrn:" + sFrn + " data:[" + Arrays.toString(data) + "]");
            return -1;
        }
    }

	private int makeResult_Repetitive(byte[] data, int index, AsterixSpecVO_DataItem di) {
        int di_len = Integer.parseInt(di.getOctet());
        
        try
        {
            List<AsterixSpecVO_Structure> st = di.getStructure();
            AsterixSpecVO_Structure s = st.get(0);
            if(s == null) return  index + di_len;
            
            int rep = data[index] & 0xff;
            
            ++index;
//            Map<String, String> result_map = new HashMap<String, String>();
            di.setOctet(String.valueOf(di_len-1));
            di.setFormat("fixed");

            for(int i=0; i<rep; i++)
            {
            	int idx_after = index;
            	
                index = makeResult(di, data, index);
                
                printDebugLog(data, idx_after, index, di);
            }
            
            // 초기화
            di.setOctet(String.valueOf(di_len));
            di.setFormat("repetitive");
            

            return index;
        }
        catch(Exception e)
        {
            e.printStackTrace();
            logger.error("[AsterixParserUtils:makeResultRepetitive] Exception:" + e + "ITEM:" + di.getItemno() + " data:[" + Arrays.toString(data) + "]");
            return 0;
        }
    }
	
    private int makeResult_End(byte[] data, int index) {
    	int size = 0;
    	byte[] bTmp = new byte[1];
    	
    	int iSPF = data[index];
    	bTmp[0] = data[index];
    	
    	String spfHex = "";
    	
    	size = iSPF & 0xff;
    	
		byte[] target = new byte[size];
		
		if(data.length - index < size) {
			
			System.out.print("****size:" + size + " data.size:"+data.length + " Index:" + index);
			
			size = data.length - index;
		}
		
		System.arraycopy(data, index, target, 0, size);
         
    	for(int idx=0; idx < size; idx++)
        {
    		if(!spfHex.equals("")) {
    			spfHex = spfHex.concat(" ");
    		}
    		
    		spfHex = spfHex.concat(String.format("%02X", target[idx] &0xff));
    		
        }
    	
    	resultMap.put("SPF", spfHex);
    	index += size;

    	return index;
    }
    
    private int makeResult_NeverSent(AsterixSpecVO_DataItem di, byte[] data, int index, int iSize) {

    	int size = iSize;
        
        byte[] target = new byte[size];
        
        try
        {
            System.arraycopy(data, index, target, 0, size);
        }
        catch(Exception e)
        {
        	logger.error("[AsterixParserUtils:makeNeverSent] Exception:" + e );
        	return -1;    
        }
        
        index += size;
        
        int no = -999;
        if(target.length > 4) {
        	logger.error("[AsterixParserUtils:makeNeverSent] target.length:" + target.length );
        }else {
        	no = apUtils.signedToInt(target);
        }
       
        
        resultMap.put(di.getItemno(), String.valueOf(no));

        // debug log 출력
    	//printDataLog("NeverSent", di, map, target);
        
        return index;
    }
	
    
    /***************************************** Make UAP *****************************************/
    
    private boolean makeUAP(Map<Integer, AsterixSpecVO_DataItem> mUAP, int idx, int fspec ) {
    	
    	// byte로 변환한 수신페킷중 3, 4, 5, 6 번째 byte를 이용하여 어떤 fspec이 포함되어 있는지 확인하고 순서에 맞게 분석용 UAP를 생성
    	
        if(mUAP == null) mUAP = new HashMap<Integer, AsterixSpecVO_DataItem>();
        if(idx < 0) return false;
        
//        logger.error("[AsterixParserUtils] UAP_" + idx + "_data:" + prtIntToCnv(fspec), LogLevelType.LOG_DEBUG, "AllLog");
        
        // "& 0xff" : 8비트만 사용하기 위함
        fspec = fspec & 0xff;
        
        boolean extendable = false;
        
        // 1이면 FX(Field Extension Indicator)
        if((fspec & 0x01) == 0x01) extendable = true;
        
        // 128(8bit, 0b10000000), 맨 앞자리부터 체크하기 위함
        int comp = 0x80;
        
        //UAP의 FX를 제외한 FRN 순번 체크 1~7, 8~14, 15~21, 22~28 (idx = FRN 그룹순번 1 ~ 4)
        int offset = (idx * 7) + 1; 
        
        // 8bit를 하나씩 체크하여 1인 것만 생성
        // offset => FRN 번호
        for(int i=0; i<7; i++)
        {
            if((fspec & comp) == comp) mUAP.put(new Integer(offset), apUtils.findAsterixDataItem(offset, currentSpec));
            
            // 8번째자리 -> 7번째자리 -> 6번째자리 -> ... -> 1번째 자리 
            comp = comp >>> 1;
            ++offset;
        }
        
        return extendable;
            
    }
    
    private boolean makeUAP_Compound(Map<Integer, AsterixSpecVO_DataItem> mUAP, int idx, int fspec, String frn) {
        if(mUAP == null) mUAP = new HashMap<Integer, AsterixSpecVO_DataItem>();
        if(idx < 0) return false;
        
        fspec = fspec & 0xff;
        
        boolean extendable = false;
        if((fspec & 0x01) == 0x01) extendable = true;
        
        int comp = 0x80;
        
        int offset = (idx * 7) + 1;
        
        for(int i=0; i<7; i++)
        {
            if(((fspec & comp) == comp) )
            {
            	String key = frn + apUtils.zeroPadding2digitAtFront(offset);
            	mUAP.put(new Integer(key), apUtils.findAsterixDataItem(Integer.parseInt(key), currentSpec));
            }
            comp = comp >>> 1;
            ++offset;
        }
        
        return extendable;
            
    }
    
    private void makeUAP_Variable(Map<Integer, AsterixSpecVO_DataItem> mUAP, String di_frn, byte[] data, int index) {
        if(mUAP == null) mUAP = new HashMap<Integer, AsterixSpecVO_DataItem>();
        
        int comp = 0x01;
        int offset = 1;
        int idx = index;

        while(true)
        {
            try
            {
                String key = di_frn + apUtils.zeroPadding2digitAtFront(offset);
                
                AsterixSpecVO_DataItem di = apUtils.findAsterixDataItem(Integer.parseInt(key), currentSpec);
                if(di == null) {
                	key = di_frn + apUtils.zeroPadding2digitAtFront(1);
                	di = apUtils.findAsterixDataItem(Integer.parseInt(key), currentSpec);
                }
                if(di == null) break;
                
                mUAP.put(new Integer(key), di);

                int data_length = Integer.parseInt(di.getOctet());
                idx = idx + data_length;
                int next = data[idx-1] & 0xff;

                if((next & comp) != comp) break; 
                ++offset;
            }
            catch(Exception e)
            {
                break;
            }
        }
    }
    
    
    /***************************************** Service Field  *****************************************/
    
	public boolean filterSvcData(ServiceAsdeData _scMap) {

        if(_scMap == null) return false;

		boolean bFilter = true;

		if (!_scMap.getTrgt_id().equals("")
            || !_scMap.getMode_s_cd().equals("")
            || !_scMap.getSsr_cd().equals("")
            || !_scMap.getClsgn().equals("")
            || !_scMap.getTail_no().equals("")
            || !_scMap.getTrack_no().equals("")
		) {

			if (!(_scMap.getLat().equals("0") && _scMap.getLon().equals("0"))) {
                bFilter = true;
                // if( _scMap.getCarty() != null){
                //     bFilter = false;
                // }
                
			} else {
				bFilter = false;
			}

		} else {
			bFilter = false;
		}

		return bFilter;
	}


    public void inputServiceField(AsterixSpecVO_Structure struc, String sValue) {
    	
    	String sServiceName = struc.getServiceFieldName();

        fieldUtils.setItem(scMap, sServiceName, sValue.trim());

        return;
    	
    }
    
    private void printDebugLog(byte[] logdata, int sidx, int tidx, AsterixSpecVO_DataItem di) {
    	
    	if( !debugLogMode.equals("detail")) return;
    	
    	int iSize = tidx - sidx;
    	
    	if(iSize <= 0) return ;
    	
    	// log
		byte[] bLogByte = new byte[tidx - sidx];
	    System.arraycopy(logdata, sidx, bLogByte, 0, tidx - sidx);
	     
	    String sLotTxt = "";
	    if(di.getStructure() == null) return;
    	
    	sLotTxt = di.getItemno().concat(" : ")
				.concat(apUtils.prtStrucItems(di.getStructure(), resultMap))
                .concat(" ").concat(apUtils.cnvBytesToHex(bLogByte))
				.concat(" ").concat(Arrays.toString(bLogByte)) ;
		
						
		logger.debug("[AsterixParser:makeResult] parser_Log [" + sLotTxt + "]");
    }

    public void setDebugLogMode(String debugLogMode) {
        this.debugLogMode = debugLogMode;
    }
    
    
    
}
