package kr.gmtc.gw.dev.asderecv.asde.asterix;

import kr.gmtc.gw.dev.asderecv.asde.asterix.spec.AsterixSpecVO;
import kr.gmtc.gw.dev.asderecv.asde.asterix.spec.AsterixSpecVO_DataItem;
import kr.gmtc.gw.dev.asderecv.asde.asterix.spec.AsterixSpecVO_Structure;
import kr.gmtc.gw.dev.asderecv.asde.utils.AsdeUtil;

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

import java.util.*;

public class AsterixParserUtils
{
	private Logger logger = LoggerFactory.getLogger(AsterixParserUtils.class);
 
	private double AVG_ERAD_NM = 6371.0 / 1.852;
	private double DE2RA = 0.017453292519943295;
	private double RA2DE  = 57.295779513082323;
	private double GPI = 3.14159265359;
	
	public double CONST_BASE_LAT = 37.454228;
	public double CONST_BASE_LON = 126.460915;
	
    ///////////////////**************************//////////////////////////
    
    public AsterixSpecVO_DataItem findAsterixDataItem(int idx, AsterixSpecVO spec)
    {
        try
        {
            List<AsterixSpecVO_DataItem> di = spec.getDataitem();
            if(di == null) return null;
            String idxString = String.valueOf(idx);
            
            for(int i=0; i < di.size(); i++)
            {
            	AsterixSpecVO_DataItem d = di.get(i);
                if(d.getFrn().equals(idxString))  
                {
                    return d;
                }
            }
        }
        catch(Exception e)
        {
            logger.error("[AsterixParserUtils:findAsterixDataItem] Exception:" + AsdeUtil.getStatckTrace(e));
        }
        
        return null;
    }
    
    public String byteToString(int b)
    {
        return String.format("0x%02x", b&0xff);
    }
    
    public String byteArrayToString(byte[] b)
    {
        StringBuilder sb = new StringBuilder();
        sb.append("0x");
        for(int i=0; i<b.length; i++)
        {
            sb.append(String.format("%02x", b[i]&0xff));
        }
        
        return sb.toString();    
    }
    
    public int unsignedToInt(byte[] data)
    {
        int val = 0;
        int len = data.length;
        
        if(len > 4) {
        	throw new RuntimeException("Too big to convert to integer");
        }
        
        for(int i=0; i<len; i++)
        {
            val = val << 8; 
            val = val | (data[i] & 0xff);
        }
        
        return val;
    }
    
    public long unsignedToInt64(byte[] data)
    {
        int val = 0;
        int len = data.length;
        
        if(len > 8) {
        	throw new RuntimeException("Too big to convert to integer");
        }
        
        for(int i=0; i<len; i++)
        {
            val = val << 8; 
            val = val | (data[i] & 0xff);
        }
        
        return val;
    }
    
    public final int signedToInt(byte[] data)
    {
    	return (new java.math.BigInteger(data)).intValue();
    }

    
    public final int signedToInt_withSignBit(byte[] data)
    {
    	
    	 String sVal = "";
         
         int sign = 0;
         int iRetVal = 0;
         
         int len = data.length;
         
         if(len > 4) {
        	 return 0;
         }
         
         for(int i=0; i<len; i++)
         {
        	 sVal = sVal.concat(String.format("%08d", Integer.parseInt(Integer.toBinaryString(data[i] & 0xff))));
        	 
         }
         
         sign = Integer.parseInt(sVal.substring(0, 1));
         iRetVal = Integer.parseInt(sVal.substring(1), 2);
         
    	 if(sign == 1) iRetVal = -(iRetVal);
    	 
    	return iRetVal;
    }
    
    public final int signedToInt_withSignBit_1byte(byte data)
    {
    	
    	 String sVal = "";
         
         int sign = 0;
         int iRetVal = 0;
         
         sVal = String.format("%08d", Integer.parseInt(Integer.toBinaryString(data & 0xff)));
         
         sign = Integer.parseInt(sVal.substring(0, 1));
         iRetVal = Integer.parseInt(sVal.substring(1), 2);

    	 if(sign == 1) iRetVal = -(iRetVal);
    	 
    	return iRetVal;
    }
    
    private StringBuffer makeString(StringBuffer sb, int val)
    {
        sb.append(val);
        return sb;
    }
    
    public int getOctetSeq(byte[] target, int bit)
    {
        if(target == null || target.length == 0) return -1;
        
        int size = target.length;
        int msb = size * 8;
        int octetSeq = 1;
        
        for(octetSeq = 1; octetSeq<=size; octetSeq++)
        {
            msb -= 8;
            if(bit > msb) return octetSeq;
        }
        
        return -1;
    }
    
    public String makeOctal(byte[] buf)
    {
        if(buf == null || buf.length != 2) return "";
        
        int num = buf[0] & 0xff;
        int num2 = buf[1];
        int n = buf[0] & 0x0f;
        n = n >>> 1;
        
        StringBuffer sbuffer = new StringBuffer();
        sbuffer = makeString(sbuffer, n);
        
        n = ((num2 >>> 6) & 0x03) | ((num & 0x01) << 2);
        
        sbuffer = makeString(sbuffer, n);
        
        n = ((num2 >>> 3) & 0x07);
        sbuffer = makeString(sbuffer, n);
        
        n = num2 & 0x07;
        sbuffer = makeString(sbuffer, n);
        
        return sbuffer.toString();
    }
    
    public String makeICAOCode(byte[] buf)
    {
        if(buf == null || buf.length != 6) return "";
        
        StringBuffer sb = new StringBuffer();
        
        int octet1 = buf[0];
        octet1 = octet1 & 0xff;
        
        octet1 = octet1 >>> 2;
        sb = makeTargetIdentification(sb, octet1);
        
        octet1 = buf[0];
        octet1 = octet1 & 0xff;
        int octet2 = buf[1];
        octet2 = octet2 & 0xff;
        octet2 = octet2 >>> 4;
        
        octet1 = octet1 << 4;
        octet1 = octet1 & 0x30;
        int number = octet1 | octet2;
        sb = makeTargetIdentification(sb, number);
        
        octet1 = buf[1] & 0xff;
        octet2 = buf[2] & 0xff;
        
        octet1 = octet1 << 2;
        octet1 = octet1 & 0x3f;
        octet2 = octet2 >>> 6;
        
        number = octet1 | octet2;
        sb = makeTargetIdentification(sb, number);
        
        octet1 = buf[2];
        number = octet1 & 0x3f;
        sb = makeTargetIdentification(sb, number);
        
        octet1 = buf[3];
        octet1 = octet1 & 0xff;
        number = octet1 >>> 2;
        sb = makeTargetIdentification(sb, number);
        
        octet1 = buf[3] & 0xff;
        octet2 = buf[4] & 0xff;
        
        octet1 = octet1 << 4;
        octet1 = octet1 & 0x3f;
        octet2 = octet2 >>> 4;
        
        number = octet1 | octet2;
        sb = makeTargetIdentification(sb, number);
        
        octet1 = buf[4] & 0xff;
        octet2 = buf[5] & 0xff;
        
        octet1 = octet1 << 2;
        octet1 = octet1 & 0x3f;
        octet2 = octet2 >>> 6;
        
        number = octet1 | octet2;
        sb = makeTargetIdentification(sb, number);
        
        octet1 = buf[5];
        number = octet1 & 0x3f;
        sb = makeTargetIdentification(sb, number);
        
        return sb.toString();
    }
    
    public String makeAscii(byte[] buf)
    {
    	String retVal = "";
    	
        if(buf == null || buf.length == 0) return "";
        
        retVal = new String(buf);
        
        return retVal.trim();
    }
    
    public String zeroPadding2digitAtFront(int number)
    {
        if(number <= 0) return "01";
        try
        {
            String str = String.valueOf(number);
            int length = str.length();

            if(length >= 2) return str;
            else return "0" + str;
        }
        catch(Exception e)
        {
            logger.error("[AsterixParserUtils:zeroPadding2digitAtFront] Exception:" + AsdeUtil.getStatckTrace(e));
            return "01";
        }
    }
    
    private StringBuffer makeTargetIdentification(StringBuffer sb, int val)
    {
        if(val == 0) return sb;
        else if(val == 32)
        {
            sb.append(" ");
            return sb;
        }
        else if(val >= 1 && val <= 26)
        {
            sb.append(Character.toChars(64 + val));
            return sb;
        }
        else if(val >= 48 && val <= 57)
        {
            sb.append(Character.toChars(val));
            return sb;
        }
        else return sb;

    }
    
///////////////////**************************//////////////////////////
    
    
    
    public String prtbyteToCnv(byte[] byteData, int flag) {
    	String retStr = "";
    	
    	String toHex = "";
    	
    	String toByte = Arrays.toString(byteData);
    	
    	String toBynary = "";
    	
    	String to8bitDec = "";
    	
    	for(int idx=0; idx < byteData.length; idx++)
        {
    		if(!toHex.equals("")) {
    			toHex = toHex.concat(" ");
    			toBynary = toBynary.concat(" ");
    			to8bitDec = to8bitDec.concat(" ");
    		}
    		
    		toHex = toHex.concat(String.format("%02X", byteData[idx] &0xff));
    		
    		toBynary = toBynary.concat(String.format("%08d", Integer.parseInt(Integer.toBinaryString(byteData[idx] &0xff ))));
    		
    		to8bitDec = to8bitDec.concat(Integer.toString(byteData[idx] & 0xff));
    		
        }
    	
    	if(flag == 0)
    		retStr = "DATA: " + toHex + " " + toBynary + " " + toByte + " " + to8bitDec;
    	else
    		retStr = "DATA: " + toHex;
    		
    	return retStr;
    }
    
    public double calcAngleDegree(double y1, double x1, double y2, double x2) {
    	
    	double angle;
    	double dx;
    	double dy;
    	
    	dx = x2 - x1;
    	dy = y2 - y1;
    	
    	// 두점사이의 절대각
		angle = Math.atan2(dy, dx);
		
		// ArcTan2 함수의 결과값은 라디안이므로, 도 단위로 변환
		angle = angle * 180 / Math.PI;
		
		if (angle < 0)
			angle = 360 + angle;
		
		return angle;
    	
    }

    public double calcDirection(double Vy, double Vx) {

        double angle;

        // 두점사이의 절대각
        angle = Math.atan2(Vy, Vx);

        // ArcTan2 함수의 결과값은 라디안이므로, 도 단위로 변환
//        angle = angle * 180 / Math.PI;
//
//        //if (angle > 160)
        angle = Math.toDegrees(angle) ;

        if (angle < 0)
            angle = 360 + angle;

        return angle;

    }
    
    public Map<String, Double> calcDistanceAzimuth(double Lat, double Lon, double DistNM, double Azimuth) {
    	
    	double dA;
    	double dB;
    	double dSinb;
    	double dCosb;
    	double dSinc;
    	double dCosc;
    	double dAzrad;
    	double dArcCosValue;
    	double dArcSinValue;
    	
    	Map<String, Double> retMap = new HashMap<String, Double>();
    	
    	dB      = DistNM / AVG_ERAD_NM;
    	dSinb   = Math.sin(dB);
    	dCosb   = Math.cos(dB);
    	dSinc   = Math.sin(DE2RA * (90.0 - Lat));
    	dCosc   = Math.cos(DE2RA * (90.0 - Lat));
    	dAzrad  = DE2RA * Azimuth;

    	dArcCosValue = dCosb * dCosc + dSinc * dSinb * Math.cos(dAzrad);
    	if(dArcCosValue < -1) dArcCosValue = -1;
    	if(dArcCosValue > 1) dArcCosValue = 1;
    	dA = Math.acos(dArcCosValue);

    	dArcSinValue = dSinb * Math.sin(dAzrad) / Math.sin(dA);
    	if(dArcSinValue < -1) dArcSinValue = -1;
    	if(dArcSinValue > 1) dArcSinValue = 1;
    	dB = Math.asin(dArcSinValue);

    	double outLat = RA2DE * ((GPI/2.0) - dA);
    	double outLon = RA2DE * dB + Lon;
    	
    	retMap.put("lat", outLat);
    	retMap.put("lon", outLon);
    	
    	return retMap;
    }
    
    public int parseIntDef(String sValue) {
    	
    	int defaultValue = 0;
    	
    	if (sValue == null) {
            return defaultValue;
        } else {
            try {
            	double dVal = Double.parseDouble(sValue);
                return (int) dVal;
            } catch (NumberFormatException nfe) {
                return defaultValue;
            }
        }
    	
    }
    
    public double parseDoubleDef(String sValue) {
    	
    	double defaultValue = 0;
    	
    	if (sValue == null) {
            return defaultValue;
        } else {
            try {
                return Double.parseDouble(sValue);
            } catch (NumberFormatException nfe) {
                return defaultValue;
            }
        }
    	
    }

    public String prtIntToCnv(int byteData) {
    	
    	String retStr = "";
    	
    	String toHex = String.format("%02X", byteData &0xff);
    	
    	String toByte = Integer.toString(byteData);
    	
    	String toBynary = String.format("%08d", Integer.parseInt(Integer.toBinaryString(byteData & 0xff )));
    	
    	
    	retStr = "DATA: " + toHex + " " + toBynary + " " + toByte + " " + (byteData & 0xff);
    	
    	return retStr;
    }
    

    public void printDataLog(String pos, AsterixSpecVO_DataItem di, Map<String, String> map, byte[] data) {
    	
    	//logger.writeLevelLog("[parse] [" + di.getItemno() +"] = "+map.get("SPF") + " | " + prtIntToCnv(iSPF) , LogLevelType.LOG_DEBUG, "AllLog");
    	
    	String sRetVal = "";
    	String mapName = "", mapData = "";
    	String sHexData = "";
    	
    	if(di == null ) return ;
    			
    	List<AsterixSpecVO_Structure> struc = di.getStructure();
    	
    	
    	for(int idx = 0; idx<struc.size(); idx++) {
    		
    		mapName = struc.get(idx).getName().toUpperCase();
    		
    		mapData = map.get(mapName);
    		
    		if(mapData == null) mapData = "N/A";
    		
    
    		
    		if (!sRetVal.equals("")) sRetVal = sRetVal.concat(", ");
    		
    		sRetVal = sRetVal.concat(mapName).concat("=").concat(mapData);
    	}
    	
    	sHexData = prtbyteToCnv(data,0);
    	
    	sRetVal = sRetVal.concat(" ").concat(sHexData);
    	
    	
    	
//    	logger.writeLevelLog("[parse-"+pos+"] [" + di.getItemno() +": Bytes:" + di.getOctet() + "] => Parse: "+sRetVal, LogLevelType.LOG_DEBUG, "AllLog");
    	
    }
    
    public String cnvBytesToHex(byte[] bArr) {
    	
    	String sRetVal = "";
    	int iLen = bArr.length;
    	
    	for(int idx = 0; idx<iLen; idx++) {
    		sRetVal = sRetVal.concat(String.format("%02X", bArr[idx] &0xff)).concat(" ");
    	}
    	
    	if(!sRetVal.equals("")) {
    		sRetVal = "[".concat(sRetVal.substring(0, sRetVal.length() -1)).concat("]");
    	}
    	
    	return sRetVal;
    }
    
    public String prtStrucItems(List<AsterixSpecVO_Structure> st, LinkedHashMap<String, String> resMap) {
    	
//    	Iterator<Integer> it = st.keySet().iterator();
    	String sRet = "";
    	String sVal = "";
    	
    	for(int i=0; i<st.size(); i++)
        {
    		AsterixSpecVO_Structure struc = st.get(i);
    		String name = struc.getName().toUpperCase();
    		sRet = sRet.concat(name).concat("=");
    		if(resMap.get(name) != null) {
    			sVal = resMap.get(name);
    		}else {
    			sVal = "N/A";
    		}
    		sRet = sRet.concat(sVal).concat(", ");
    		
        }
    	
    	return sRet ;
    }
}
