package kr.gmtc.gw.comp.socket.utils;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.*;

public class SocketUtil {
    public static final int NET_WOULD_BLOCK = 135;
	public static final int NET_SEND_OK = 0;
	
	public static int strToIntDef(String s, int def) {		
		try { 
			
			int intVal = Integer.parseInt(s);			
			return intVal;
			
		} catch (NumberFormatException e) {
			return def;
		}				
	}
	
	public static LocalDateTime defaultTime() {
		return LocalDateTime.of(1899, 12, 30, 00, 00);
	}
	
	public static String getStatckTrace(Throwable t) {
		StringWriter sw = new StringWriter();
		t.printStackTrace(new PrintWriter(sw));
		return sw.toString();
	}
	
	public static String GetAddress(String addressType) {
		String address = "";
        InetAddress lanIp = null;
        try {

            String ipAddress = null;
            Enumeration<NetworkInterface> net = null;
            net = NetworkInterface.getNetworkInterfaces();

            while (net.hasMoreElements()) {
                NetworkInterface element = net.nextElement();
                Enumeration<InetAddress> addresses = element.getInetAddresses();

                while (addresses.hasMoreElements()) {
                    InetAddress ip = addresses.nextElement();
                    if (ip instanceof Inet4Address) {
                        if (ip.isSiteLocalAddress()) {
                            ipAddress = ip.getHostAddress();
                            lanIp = InetAddress.getByName(ipAddress);
                        }
                    }
                }
            }

            if (lanIp == null)
                return null;

            if (addressType.equals("ip")) {
                address = lanIp.toString().replaceAll("^/+", "");
            } else if (addressType.equals("mac")) {
                address = getMacAddress(lanIp);
            } else {
                throw new Exception("Specify \"ip\" or \"mac\"");
            }

        } catch (UnknownHostException ex) {
            ex.printStackTrace();
        } catch (SocketException ex) {
            ex.printStackTrace();
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return address;
	}
	
	public static String getMacAddress(InetAddress ip) {
        String address = null;
        try {
            NetworkInterface network = NetworkInterface.getByInetAddress(ip);
            byte[] mac = network.getHardwareAddress();

            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < mac.length; i++) {
                sb.append(String.format("%02X%s", mac[i], (i < mac.length - 1) ? "-" : ""));
            }
            address = sb.toString();
        } catch (SocketException ex) {
            ex.printStackTrace();
        }
        return address;
    }
	
	public static String makeCRC(String value) throws Exception {
		return makeCRC(value, "EUC-KR");
	}
	
	public static String makeCRC(String value, String charset) throws Exception {		
		
		byte[] byteStr = value.getBytes(charset);
		byte   checkSum = 0;
		
		for (int i = 1; i < byteStr.length; i++) {			
			checkSum = (byte) (checkSum ^ byteStr[i]);
		}				
		
		String result = String.format("%02X", checkSum);
		
		return result;
	}

	public static boolean checkCRC(String value, String seperator) throws Exception {
		String data;
		String CRCChar;
		String CRCValue;
		String makeStr;
		
		if (value.length() < 3) {return false;}
		
		data     = value.substring(0, value.length() - 3);
		CRCChar  = value.substring(value.length() - 3, value.length() - 2);
		CRCValue = value.substring(value.length() - 2, value.length());
		
		if (!CRCChar.equals(seperator)) {return false;}
		
		makeStr = makeCRC(data);
		
		if (makeStr.equals(CRCValue)) {return true;}	
		
		return false;
	}
	
	
	public static String fillString(int digit, String changeStr, boolean isLeft) {
		String result = "";
		
		result = changeStr;
		
		if (isLeft) {
			result = String.format("%" + String.valueOf(digit) + "s", changeStr);
		}
		else {
			result = String.format("%-" + String.valueOf(digit) + "s", changeStr);
		}
		result = result.substring(0, digit);
		
		return result;
	}
	
	public static String byteArrayToHexString(byte[] bytes){ 
			
		StringBuilder sb = new StringBuilder(); 
		
		for(byte b : bytes){ 
			
			sb.append(String.format("%02X ", b&0xff));
		} 
		
		return sb.toString(); 
	}
	
	
	/**
     * int를 바이너리 스트링으로 변환하고 , length 에 부족한 길이만큼 0으로 채움
     * 
     * @param a
     * @return
     */
	public static String IntToBinaryString(int a, int length) {
		String bin = Integer.toBinaryString(a);
		int offset = length - bin.length();
		
		if (offset < 0) {
		  	
		} else {
		  for (int i = 0; i < offset; i++) {
			  bin = "0" + bin;
		  }
		}
		
		return  bin;
	}
	
	
	/**
     * 바이너리 스트링을 바이트로 변환
     * 
     * @param s
     * @return
     */
	public static byte binaryStringToByte(String s) {
        byte ret = 0, total = 0;
        for (int i = 0; i < 8; ++i) {
            ret = (s.charAt(7 - i) == '1') ? (byte) (1 << i) : 0;
            total = (byte) (ret | total);
        }
        return total;
    }
	
	/**
     * 바이너리 스트링을 바이트배열로 변환
     * 
     * @param s
     * @return
     */
    public static byte[] binaryStringToByteArray(String s) {
        int count = s.length() / 8;
        byte[] b = new byte[count];
        for (int i = 1; i <= count; ++i) {
            String t = s.substring((i - 1) * 8, i * 8);
            b[i - 1] = binaryStringToByte(t);
        }
        return b;
    }
    
    /**
     * 바이너리스트링을 Base64로 인코딩
     * 
     * @param data
     * @return
     */
	public static String encodeBase64(String data) {
	    byte[] b = binaryStringToByteArray(data);
	    String p = Base64.getEncoder().encodeToString(b);

		return p;
	}
	
	
	/**
     * 바이너리 바이트 배열을 스트링으로 변환
     * 
     * @param b
     * @return
     */
    public static String byteArrayToBinaryString(byte[] b) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < b.length; ++i) {
            sb.append(byteToBinaryString(b[i]));
        }
        return sb.toString();
    }
	
    /**
     * 바이너리 바이트를 스트링으로 변환
     * 
     * @param n
     * @return
     */
    public static String byteToBinaryString(byte n) {
        StringBuilder sb = new StringBuilder("00000000");
        for (int bit = 0; bit < 8; bit++) {
            if (((n >> bit) & 1) > 0) {
                sb.setCharAt(7 - bit, '1');
            }
        }
        return sb.toString();
    }
	
    
    /**
     * Base64를 바이너리스트링으로 디코딩
     * 
     * @param s
     * @return
     */
	public static String decodeBase64(String s) {
		byte[] decodedBytes = Base64.getDecoder().decode(s);
		String p = byteArrayToBinaryString(decodedBytes);
		return p;
	}
	
	/**
     * 바이너리스트링을 Integer로 변환
     * 
     * @param Binary
     * @return
     */
	public static Integer BinaryToInteger(String Binary) {
		int	ResutValue = 0;
		boolean chk = false;
		String ComvertBinary = ""; 
		String StrBinary = Binary.replaceAll(" ", "");
		String[] ArrayValue = StrBinary.split("");

		switch (ArrayValue[0]) {
			case "0":
				ResutValue = Integer.parseInt(StrBinary, 2);
				break;
			case "1":
				for (int i = ArrayValue.length-1; i >= 1; i--) {
					if (ArrayValue[i].equals("1") && !chk) {
						ComvertBinary = "1" + ComvertBinary;
						chk = true;
						continue;
					}
					
					if (chk) {
						switch (ArrayValue[i]) {
						case "0":
							ComvertBinary = "1" + ComvertBinary;
							break;
						case "1":
							ComvertBinary = "0" + ComvertBinary;
							break;
						} 
					}
					else {
						ComvertBinary = "0" + ComvertBinary;
					}
				}
				
				ResutValue = Integer.parseInt(ComvertBinary, 2) * -1;
				break;
		}		
		
		
		return ResutValue;
	}
	
	/**
     * 16진수 문자열(Hex String)을 Int배열로 변환
     * 
     * @param inputStr
     * @return
     */
	public static int[] hexStringToIntArray(String inputStr) {
	    int[] intArray;
	    int strLen = inputStr.length();
	    
	    if (strLen % 2 != 0) {
            inputStr = inputStr.substring(0, strLen - 1) + "0"
                    + inputStr.substring(strLen - 1, strLen);
            strLen++;
        } 
	    
	    intArray = new int[strLen / 2];
        int ctr = 0;
        for (int n = 0; n < strLen; n += 2) {
            intArray[ctr] = Integer.valueOf(inputStr.substring(n, n + 2), 16);
            ctr++;
        }
        
	    return intArray;
	}
	
	
	/**
     * int[] --> CRC16CCITT 
     * 
     * @param intArray
     * @return
     */
	public static String CRC16CCITT(int[] intArray) {
	    int crc = 0x0000;          // initial value
	    int polynomial = 0x1021;   // 0001 0000 0010 0001  (0, 5, 12) 
	    
	    for (int a : intArray) {
	        for (int i = 0; i < 8; i++) {
	            boolean bit = ((a   >> (7-i) & 1) == 1);
	            boolean c15 = ((crc >> 15    & 1) == 1);
	            crc <<= 1;
	            if (c15 ^ bit) crc ^= polynomial;
	         }
	    }

	    crc &= 0xFFFF;
	    String crcStr = Integer.toHexString(crc).toUpperCase();
	    return crcStr;
	   }
	
	/**
     * 바이너리스트링 --> CRC16CCITT
     *
     * @return
     */
	public static String CRC16CCITT(String binary) {
	    int crc = 0x0000;          // initial value
	    int polynomial = 0x1021;   // 0001 0000 0010 0001  (0, 5, 12) 
	    
	    byte[] bytes = binaryStringToByteArray(binary);
	    int[] intArray = new int[bytes.length];
	    int ctr=0;
	    for(byte b : bytes){
	      intArray[ctr] = b & 0xFF;
	      ctr++;
	    }
	    
	    for (int a : intArray) {
	        for (int i = 0; i < 8; i++) {
	            boolean bit = ((a   >> (7-i) & 1) == 1);
	            boolean c15 = ((crc >> 15    & 1) == 1);
	            crc <<= 1;
	            if (c15 ^ bit) crc ^= polynomial;
	         }
	    }

	    crc &= 0xFFFF;
	    String crcStr = Integer.toHexString(crc).toUpperCase();
	    return crcStr;
	   }  
	
	
	/**
     * CRC16_CCITT 생성함수
     * 
     * @param inputStr
     * @param isBinary (inputStr 이 binary String 이면 true)
     * @return
     */
	public static String makeCRC16_CCITT(String inputStr , boolean isBinary) {
	    int crc = 0x0000;          // initial value
	    int polynomial = 0x1021;   // 0001 0000 0010 0001  (0, 5, 12) 
	    int[] intArray;
	    int ctr;
	    String crcStr;
	    
	    // 입력 String 타입에 따른 int array 생성 
	    if (isBinary) {
	        byte[] bytes = binaryStringToByteArray(inputStr);
	        intArray = new int[bytes.length];
	        ctr=0;
	        for(byte b : bytes){
	          intArray[ctr] = b & 0xFF;
	          ctr++;
	        }    
	    } else {
	        intArray = new int[inputStr.getBytes().length];
	        ctr=0;
	        for(byte b : inputStr.getBytes()){
	            intArray[ctr] = b;
	            ctr++;
	        }
	    }
	    
	    // crc16_ccitt 생성 로직
	    for (int a : intArray) {
	        for (int i = 0; i < 8; i++) {
	            boolean bit = ((a   >> (7-i) & 1) == 1);
	            boolean c15 = ((crc >> 15    & 1) == 1);
	            crc <<= 1;
	            if (c15 ^ bit) crc ^= polynomial;
	         }
	    }
	    crc &= 0xFFFF;
	    
	    // 입력 String 타입과 동일한 결과값 생성
	    if (isBinary) {
	    	// int 를 16자리 바이트 스트링으로 변경
	    	crcStr = String.format("%16s", Integer.toBinaryString(crc)).replace(' ', '0');
	        
	        System.out.println("crcHex = "+Integer.toHexString(crc).toUpperCase());
	        System.out.println("crcBinary = "+crcStr);
	    } else {
	        String crcHex = Integer.toHexString(crc).toUpperCase();
	        System.out.println("crcHex = "+crcHex);
	        crcStr = hexToAscii(crcHex);
	        System.out.println("crcAscii = "+crcStr);
	    }
	    
	    
	    return crcStr;
	   }
	
	
	/**
     * CRC16 체크 함수
     * 
     * @param packet
     * @param isBinary
     * @return
     */
	public static boolean checkCRC16_CCITT(String packet, boolean isBinary) {
	       String msg;
	       String crc;
	       boolean isCrc;
	       
	       if (isBinary) {
	          int len = packet.length();
	           
	          msg = packet.substring(0, len-64);
	          crc = packet.substring(len-64, len-48);
	       } else {
	          String[] packetArray = packet.split("\\*");
		      msg = packetArray[0];
		      crc = packetArray[1];    
	       }
	       
		   String tempCrc = makeCRC16_CCITT(msg, isBinary);
		   
		   if (crc.equals(tempCrc)) isCrc = true;
		   else isCrc = false;
	       
	       return isCrc;
	   }
	
	
	/**
     * Hex String을 Ascii로 변환
     * 
     * @param hexStr
     * @return
     */   
	public static String hexToAscii(String hexStr) {
	    StringBuilder output = new StringBuilder("");
	    
	    for (int i = 0; i < hexStr.length(); i += 2) {
	        String str = hexStr.substring(i, i + 2);
	        output.append((char) Integer.parseInt(str, 16));
	    }
	    
	    return output.toString();
	}
	
	/**
     * 위경도를 도분초로 변환
     *
     * @return string[] 도, 분, 초
     */   
	public static String[] CoordinateToWGS84(Double LonLat, Boolean BLon) {
		String[] result = new String[3];
		
		double pDo = Math.floor(LonLat);
		result[0] = String.format("%.0f", Math.abs(pDo));
		
		double Temp = (Math.abs(LonLat) - Math.abs(pDo)) * 60.0;	
		
		double pBun = Math.floor(Temp);		
		if (pBun == 0) {
			result[1] = "00";
		} else {
			result[1] = String.format("%.0f", pBun);
		}
		
		Temp = (Temp - pBun) * 60.0;
		double pCho = Temp;
		
		if (pCho == 0) {
			result[2] = "00";
		} else {
			result[2] = String.format("%.0f", pCho);
		}		
		
		return result;
	}
	 
	/**
     * UTC 시간을 KST로 변환
     *
     * @return
     */
	public static String UtcToKst(String yyyyMMddHHmiss) {
		int yyyy = Integer.parseInt(yyyyMMddHHmiss.substring(0 ,4));
		int MM	 = Integer.parseInt(yyyyMMddHHmiss.substring(4 ,6));
		int dd	 = Integer.parseInt(yyyyMMddHHmiss.substring(6 ,8));
		int HH	 = Integer.parseInt(yyyyMMddHHmiss.substring(8 ,10));
		int mi	 = Integer.parseInt(yyyyMMddHHmiss.substring(10,12));
		int ss	 = Integer.parseInt(yyyyMMddHHmiss.substring(12,14));
		
        SimpleDateFormat dtformat = new SimpleDateFormat("yyyyMMddHHmmss");
        Calendar date = new GregorianCalendar(yyyy, MM-1, dd, HH, mi, ss);
        date.add(Calendar.HOUR_OF_DAY, 9);

        Date data = date.getTime();
        String strDate = dtformat.format(data);
		return strDate;
	}
	
	/**
     * UTC 시간을 KST로 변환(+특정 분)
     *
     * @return
     */
	public static String UtcToKst_Before(String yyyyMMddHHmiss, int minutes) {
		int yyyy = Integer.parseInt(yyyyMMddHHmiss.substring(0 ,4));
		int MM	 = Integer.parseInt(yyyyMMddHHmiss.substring(4 ,6));
		int dd	 = Integer.parseInt(yyyyMMddHHmiss.substring(6 ,8));
		int HH	 = Integer.parseInt(yyyyMMddHHmiss.substring(8 ,10));
		int mi	 = Integer.parseInt(yyyyMMddHHmiss.substring(10,12));
		int ss	 = Integer.parseInt(yyyyMMddHHmiss.substring(12,14));
		
        SimpleDateFormat dtformat = new SimpleDateFormat("yyyyMMddHHmmss");
        Calendar date = new GregorianCalendar(yyyy, MM-1, dd, HH, mi, ss);
        date.add(Calendar.MINUTE, minutes);
        date.add(Calendar.HOUR_OF_DAY, 9);

        Date data = date.getTime();
        String strDate = dtformat.format(data);
		return strDate;
	}
	
	/**
     * 문자형 시간 분 계산(+특정 분)
     *
     * @return
     */
	public static String StrDateTimeIncMin(String yyyyMMddHHmiss, int minutes) {
		int yyyy = Integer.parseInt(yyyyMMddHHmiss.substring(0 ,4));
		int MM	 = Integer.parseInt(yyyyMMddHHmiss.substring(4 ,6));
		int dd	 = Integer.parseInt(yyyyMMddHHmiss.substring(6 ,8));
		int HH	 = Integer.parseInt(yyyyMMddHHmiss.substring(8 ,10));
		int mi	 = Integer.parseInt(yyyyMMddHHmiss.substring(10,12));
		int ss	 = Integer.parseInt(yyyyMMddHHmiss.substring(12,14));
		
        SimpleDateFormat dtformat = new SimpleDateFormat("yyyyMMddHHmmss");
        Calendar date = new GregorianCalendar(yyyy, MM-1, dd, HH, mi, ss);
        date.add(Calendar.MINUTE, minutes);

        Date data = date.getTime();
        String strDate = dtformat.format(data);
		return strDate;
	}
	public static String makeCRC(byte[] value) {
		byte checkSum = 0;

		for(int i = 1; i < value.length; ++i) {
			checkSum ^= value[i];
		}

		return String.format("%02X", checkSum);
	}
	
	/**
     * 1바이트 00 체크섬 생성 함수
     * 
     * @param s
     * @return
     */
	public static String makeCheckSum(String s) {
        int mod = s.length() % 8;
        int sum = 0;
        String t="";
        
        if (mod != 0) {
          System.out.println("입력값이 바이트 단위가 아닙니다.");
        } else {
            for (int i=0; i<s.length(); i+=8){
                String b = s.substring(i, i+8);
                System.out.println("b = " + b);
                sum += Integer.parseInt(b, 2); 
            }
            
            String Hexsum = Integer.toHexString(sum);
            String hex = Hexsum.substring(Hexsum.length()-2, Hexsum.length());
            int a = Integer.parseInt(hex, 16);
            String bin = Integer.toBinaryString(a);
            
            int cnt = 8 - bin.length();
            if ( bin.length() < 8) {
                for(int i=0; i<cnt; i++){
                    bin = "0" + bin;
                }
            }
            
            int pos = 7;
            for(int i=0; i<8; i++){
                if (pos > i) {
                    if (bin.charAt(7-i) == '1') {
                        t = "1" + t;
                        pos = i;
                    } else {
                        t = "0" + t;
                    }
                } else {
                    if (bin.charAt(7-i) == '1') {
                        t = "0" + t;
                    } else {
                        t = "1" + t;
                    }
                }
            }
            
            // checksum = Integer.parseInt(t, 2);
            
            // String CheckHex = Integer.toHexString(checksum).toUpperCase();
        }
      
        return t;
    }
	
	/**
     * 체크섬 검증 함수
     * 
     * @param s
     * @return
     */
	public static boolean checkCheckSum(String s) {
        boolean isCheck;
        String data = s.substring(0, s.length()-3);
        String checksum = s.substring(s.length()-2, s.length());
        
        String temp = makeCRC(data.toString().getBytes(StandardCharsets.UTF_8));

         if (checksum.equals(temp)) {
            isCheck = true;
        } else {
            isCheck = false;
        }
        
        return isCheck;
    }

	/**
	 * HexString To ByteArray
	 * 
	 * @param s(Hex String)
	 * @return
	 */
	public static byte[] hexStringToByteArray(String s) {
		int len = s.length();
		byte[] data = new byte[len / 2];
		for (int i = 0; i < len; i += 2) {
			data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16));
		}
		return data;
	}
}
