package com.takensoft.pohangTp.common.util;



import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;

import org.json.XML;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

public class CommonUtil {

	/**
     * @author 최정우
     * @since 2019.12.11
     * 
     * 데이터의 표준화 사용 유무 
     */
	private static boolean IS_USE_STANDARD = true;
	
	public static void setIsUseStandard (boolean isUseStandard) {
		IS_USE_STANDARD = isUseStandard;
	}
	
	public static boolean getIsUseStandard () {
		return IS_USE_STANDARD;
	}
	
	/**
     * @author 최정우
     * @since 2019.11.13
     * 
     * 빈 문자열 검사
     */
    public static boolean isNull(Object obj) {
        return obj == null;
    }
    

    /**
     * @author 최정우
     * @since 2019.11.13
     * 
     * string to int check
     */
    public static boolean isInt (String text) {
		try {
			Integer.parseInt(text);
			return true;
		} catch(NumberFormatException e) {
			return false;
		}
	}
    
    /**
     * @author 최정우
     * @since 2019.11.13
     * 
     * string to int check
     */
    public static boolean isLong (String text) {
		try {
			Long.parseLong(text);
			return true;
		} catch(NumberFormatException e) {
			return false;
		}
	}
	
    /**
     * @author 최정우
     * @since 2019.11.13
     * 
     * string to double check
     */
	public static boolean isDouble (String text) {
		try {
			Double.parseDouble(text);
			return true;
		} catch (NumberFormatException e) {
			return false;
		} catch (Exception e) {
			return false;
		}
	}
	
	/**
     * @author 최정우
     * @since 2020.01.08
     * 
     * object to int
     */
	public static int parseInt (Object obj) {
		try {
			return Integer.parseInt(obj.toString());
		} catch(Exception e) {
			return 0;
		}
	}
	
	/**
     * @author 최정우
     * @since 2020.01.08
     * 
     * string to int
     */
	public static int parseInt (String text) {
		try {
			return Integer.parseInt(text);
		} catch(NumberFormatException e) {
			return 0;
		}
	}
	
	/**
     * @author 최정우
     * @since 2020.01.08
     * 
     * int to double 
     */
	public static long parseLong (int number) {
		try {
			return (long) number;
		} catch(Exception e) {
			return 0;
		}
	}
	
	/**
     * @author 최정우
     * @since 2020.01.08
     * 
     * object to double 
     */
	public static long parseLong (String text) {
		try {
			return Long.parseLong(text);
		} catch(Exception e) {
			return 0;
		}
	}

	/**
     * @author 최정우
     * @since 2020.01.08
     * 
     * object to double 
     */
	public static long parseLong (Object obj) {
		try {
			if (obj instanceof Integer) {
				return (long) obj;
			} else {
				return Long.parseLong(obj.toString());
			}
		} catch(Exception e) {
			return 0;
		}
	}
	
	/**
     * @author 최정우
     * @since 2020.01.08
     * 
     * int to double 
     */
	public static double parseDouble (int number) {
		try {
			return (double) number;
		} catch(Exception e) {
			return 0.0;
		}
	}
	
	/**
     * @author 최정우
     * @since 2020.01.08
     * 
     * object to double 
     */
	public static double parseDouble (String text) {
		try {
			return Double.parseDouble(text);
		} catch(Exception e) {
			return 0.0;
		}
	}

	/**
     * @author 최정우
     * @since 2020.01.08
     * 
     * object to double 
     */
	public static double parseDouble (Object obj) {
		try {
			if (obj instanceof Integer) {
				return (double) obj;
			} else {
				return Double.parseDouble(obj.toString());
			}
		} catch(Exception e) {
			return 0.0;
		}
	}
	
	/**
     * @author 최정우
     * @since 2019.11.13
     * 
     * 문자열의 모든 공백 제거
     */
	public static String allTrim(String text) {
		return text.replaceAll("\\p{Z}", "");
	}
	
	/**
     * @author 최정우
     * @since 2019.11.13
     * 
     * 문자열 앞뒤 공백 제거후, 문자열 사이에 존재하는 공백을 한개의 공백으로 치환
     * ex) " abcd     efg    hijk   " => "abcd efg hijk" 
     */
	public static String normalizeSpace(String text) {
		return text.trim().replaceAll("\\s+", " ");
	}
	
	/**
     * @author 최정우
     * @since 2019.11.13
     * 
     * 숫자 빼고 모든 문자 제거
     */
	public static String getOnlyNumber (String text) {
		return text.replaceAll("[^0-9]", "");
	}
	
	/**
     * @author 최정우
     * @since 2019.11.13
     * 
     * 문자 빼고 모든 숫자 제거
     */
	public static String getOnlyText (String text) {
		return text.replaceAll("[0-9]", "");
	}
	
	/**
     * @author 최정우
     * @since 2019.11.13
     * 
     * 특정 문자열 개수 check
     */
	public static int getWordCount (String text, String word) {
		int size = 0;
		int fromIndex = -1;
		while ((fromIndex = text.indexOf(word, fromIndex + 1)) >= 0) {
			size++;
		}
		return size;
	}
	
	/**
     * @author 최정우
     * @since 2019.11.13
     * 
     * 문자열 to Date문자열
     */
	public static boolean isDate (String text) {
		if (StringUtil.isEmpty(text) == true || StringUtil.isEmpty(getOnlyNumber(text)) == true || getOnlyNumber(text).length() < 6) {
			return false;
		}
		
		//공백을 제외한 문자얻기, 대문자로 치환
		String newText = allTrim(text).toUpperCase();
		
		try {
			//문자열의 날짜 패턴 생성
			String pattern = createDatePattern(newText);
			if (pattern == null) {
				return false;
			}
		
			SimpleDateFormat newPattern = new SimpleDateFormat(pattern);
			//문자열 날짜 형태로 변환
			newPattern.parse(newText);
			return true;
		} catch (Exception e) {
			//e.printStackTrace();
			return false;
		}
	}
	
	/**
     * @author 최정우
     * @since 2019.11.13
     * 
     * 문자열 to Date문자열
     */
	public static String parseDateText (String text) {
		if (StringUtil.isEmpty(text) == true || StringUtil.isEmpty(getOnlyNumber(text)) == true || getOnlyNumber(text).length() < 6) {
			return null;
		}
		
		//공백을 제외한 문자얻기, 대문자로 치환
		String newText = allTrim(text).toUpperCase();
		
		//문자열의 날짜 패턴 생성 
		String pattern = createDatePattern(newText);
		if (pattern == null) {
			return null;
		}

		Date date = null;
		String dateText = null;
		try {
			SimpleDateFormat newPattern = new SimpleDateFormat(pattern);
			
			//문자열 날짜 형태로 변환
			date = newPattern.parse(newText);
			
			//DB에 저장할 날짜 패턴
			SimpleDateFormat defalutPattern = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			dateText = defalutPattern.format(date);
		} catch (Exception e) {
			//e.printStackTrace();
		}
		return dateText;
	}
	
	public static <T> List<T> mapToList (Map<?, T> map) {
		List<T> items = new ArrayList<T>();
		
		if (map != null) {
			for(Map.Entry<?, T> item : map.entrySet()) {
				items.add(item.getValue());
	        }
		}
		
		return items;
	}
	
	public static Map objectToMap(Object obj) {
		if (obj != null) {
			try {
				return (Map) obj;
			} catch (Exception e) {
				return new HashMap();
			}
		} else {
			return new HashMap();
		}
	}
	
	
	/*
	 * 시간 타입
	 * PM, AM
	 */
	public final static List<String> TIME_TYPES = Arrays.asList(new String[] {"AM", "PM", "오전", "오후"});
	
	/*
	 * 날짜 포맷 패턴's
	 */
	public final static List<Character> DATE_PATTERNS = Arrays.asList(new Character[] {'y', 'M', 'd', 'H', 'm', 's'});
	
	/*
	 * 날짜 포맷 패턴's의 최대 문자열 수
	 */
	public final static List<Integer> DATE_PATTERNS_MAX_LENGTH = Arrays.asList(new Integer[] {4, 2, 2, 2, 2, 2});
	
	/**
     * @author 최정우
     * @since 2019.11.13
     * 
     * 문자열의 날짜 패턴 생성
     */
	public static String createDatePattern (String date) {
		
		List<Character> DATE_PATTERNS = Arrays.asList(new Character[] {'y', 'M', 'd', 'H', 'm', 's'});
		
		//시간 표기가 (0~12 -> AM, PM사용)인지 (0~23)인지 확인 후, 날짜 포맷 패턴's에 있는 시간 패턴 변경
		int timeTypeFindIndex = -1;
		for (int i = 0; i < TIME_TYPES.size(); i++) {
			//("AM", "PM", "오전", "오후" 중 1개)가 포함된 단어가 있는지 확인, Index 위치를 담기(없으면 -1)
			if ((timeTypeFindIndex = date.indexOf(TIME_TYPES.get(i))) > -1) {
				//문자열에 포함된 ("AM", "PM", "오전", "오후" 중 1개) 삭제
				date = date.replaceAll(TIME_TYPES.get(i), "");
				//시간 패턴 변경 [H -> h]
				DATE_PATTERNS.set(3, 'h');
				break;
			}
		}
		
		//숫자를 뺀 나머지 문자열 가지고오기 ex) "2020.08.03" -> ".."
		//숫자를 뺀 나머지 문자열 가지고오기 ex) "2020.08.03 19:20:21" -> "..::"
		final char[] separators = getOnlyText(date).toCharArray();
		
	
		
		//사용할 최대 패턴 수
		int maxPatterSize = 0;
		if (DATE_PATTERNS.size() <= separators.length) {
			maxPatterSize = DATE_PATTERNS.size();
		} else {
			maxPatterSize = separators.length;
		}
		
		//구분자별 Index 위치's (사용할 최대 패턴 수 + 시작점:-1, 종료점:date문자열의 최종 길이)
		List<Integer> sizeByPatterns = new ArrayList<Integer>();
		
		
		//구분자 별 Index 위치 파악 후, 앞에 있는 문자열의 수 찾은 후, 추가 (마지막 패턴 뒤에 있는 문자열을 따로 처리해줘야함)
		int fromIndex = -1;
		for (int i = 0; i < maxPatterSize; i++) {
			//구분자
			char separator = separators[i];
			
			//'현재 찾은 위치' : 이전에 찾은 위치(찾기 시작할 위치 => fromIndex) + 1 부터 찾기 시작함
			int currentFromIndex = date.indexOf(separator, fromIndex + 1);
			
			//현재 패턴의 문자열 수 = '현재 찾은 위치' - '이전에 찾은 위치' - 1 [추가]
			sizeByPatterns.add(currentFromIndex - fromIndex - 1);			
			
			//'현재 찾은 위치'는 '이전에 찾은 위치'가 됨
			fromIndex = currentFromIndex;
		}
		//마지막 패턴 뒤에 있는 문자열 = '문자열의 길이' - '마지막에 찾은 위치(이전에 찾은 위치)' - 1 [추가]
		sizeByPatterns.add(date.length() - fromIndex - 1);
		
		
		//패턴을 담을 변수
		StringBuilder pattern = new StringBuilder();
		
		//DATE_PATTERS 순서 대로, 각 구분자 별 Index 위치 크기만큼 문자열에 패턴 삽입 + 구분자 삽입
		//마지막 전까지만 for문 돌림
		for (int i = 0, patternIndex = 0; i < sizeByPatterns.size() && patternIndex < DATE_PATTERNS.size(); i++, patternIndex++) {
			
			//패턴 추가
			int usingSize = 0;
			for (int j = 0; j < sizeByPatterns.get(i); j++) {
				if (j >= usingSize + DATE_PATTERNS_MAX_LENGTH.get(patternIndex)) {
					usingSize += DATE_PATTERNS_MAX_LENGTH.get(patternIndex++);
					
					/*단 한개의 패턴이라도 '최대 문자열 수'를 넘어서면 -> '날짜 아님'*/
					if (i >= sizeByPatterns.size() || patternIndex >= DATE_PATTERNS.size()) {
						return null;
					}
				}
				
				pattern.append(DATE_PATTERNS.get(patternIndex));
			}
			
			//날짜 구분자 추가 (마지막 구분자까지만) 
			if (i < separators.length) {
				pattern.append(separators[i]);
			}
			
			
		}
		
		if (timeTypeFindIndex > -1) {
			pattern.insert(timeTypeFindIndex, 'a');
		}
		
		if(!(pattern.toString().equals("-") || pattern.toString().equals("/") || pattern.toString().equals("."))){
			pattern = null;
		}
		
		return pattern.toString();
	}
	
	
	/**
     * @author 최정우
     * @since 2020.01.26
     * 
     * ping 체크
     */
	public static boolean pingCheck (String ip) {
		InetAddress inetAddress;
		try {
			inetAddress = InetAddress.getByName(ip);
			return inetAddress.isReachable(1000);
		} catch (UnknownHostException e) {
			return false;
		} catch (IOException e) {
			return false;
		} catch (Exception e) {
			return false;
		}
	}
	
	/**
     * @author 김성원
     * @since 2024.01.04
     * 
     * 접속 체크 (ip + port)
     */
	public static boolean linkCheck(String ip, int port) {
		Socket socket = new Socket();
		try {
			socket.connect(new InetSocketAddress(ip, port), 1000);
			boolean isConnect = socket.isConnected();
			socket.close();
			return isConnect;
		} catch (UnknownHostException e) {
			return false;
		} catch (IOException e) {
			return false;
		} catch (Exception e) {
			return false;
		}
	}	
	
	
	/**
	 * @author 최정우
	 * @since 2019.12.09
	 * 
	 * 데이터 셋 목록 Convert LinkedHashMap<String, Object> to List<String>
	 */
	public static List<List<Object>> rowDataMapToList (List<LinkedHashMap<String, Object>> rowMapData) throws Exception {
		List<List<Object>> rowData = new ArrayList<List<Object>>();
		for (int i = 0; i < rowMapData.size(); i++) {
			List<Object> row = new ArrayList<Object>();
			LinkedHashMap<String, Object> mapdata = rowMapData.get(i);
	        for( String key : mapdata.keySet() ){
	        	if (mapdata.get(key) == null) {
	        		row.add(null);//null값 대체  
	        	} else {
	        		row.add(mapdata.get(key).toString());
	        	}
	        }
	        rowData.add(row);
		}
		return rowData;
	}
	
	/**
	 * @author 최정우
	 * @since 2020.01.26
	 *
	 * 현재 client의 HttpServletRequest 조회
	 */
	public static HttpServletRequest getHttpServletRequest () {
		try {
			ServletRequestAttributes servletRequestAttribute = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
			return servletRequestAttribute.getRequest();
		} catch (NullPointerException e) {
			return null;
		}
	}
	
	
	/**
	 * @author 최정우
	 * @since 2020.01.26
	 *
	 * 현재 client의 HttpSession 조회
	 */
	public static HttpSession getHttpSession (boolean create) {
		try {
			HttpServletRequest request = getHttpServletRequest();
			if (request != null) {
				return request.getSession(create);
			} else {
				return null;
			}
		} catch (NullPointerException e) {
			return null;
		}
	}
	
	/**
	 * @author 김성원
	 * @since 2024.01.10
	 *
	 * 사용자 세션 정보 조회
	 */
	public static HashMap<String, Object> getHttpSessionMember () {
		try {
			HttpServletRequest request = getHttpServletRequest();
			if(request.getSession().getAttribute(AuthUtil.LOGIN_USER_SESSION) != null) {				
				return (HashMap<String, Object>)request.getSession().getAttribute(AuthUtil.LOGIN_USER_SESSION);	
			}else {
				return null;
			}	
		} catch (NullPointerException e) {
			return null;
		}
	}
	
	/**
	 * @author 최정우
	 * @since 2020.01.26
	 *
	 * HttpServletRequest를 활용한 Client IP 가지고오기
	 * Header의 X-FORWARDED-FOR 값을 통해 IP 조회, 만약 Header에 X-FORWARDED-FOR가 없으면 getRemoteAddr() 이걸로 조회
	 */
	public static String getClientIp () {
		try {
			HttpServletRequest request = getHttpServletRequest();
			if (null != request.getHeader("X-FORWARDED-FOR")) {
				return request.getHeader("X-FORWARDED-FOR");
			} else {
				return request.getRemoteAddr();
			}
		} catch (NullPointerException e) {
			return null;
		}
	}
	

	
	/**
	 * @author 최정우
	 * @since 2019.12.09
	 * 
	 * JSONObject to Map<String, Object>
	 */
	public static Map<String, Object> jsonObjectToMap( JSONObject jsonObj ) {
        Map<String, Object> map = null;
        try {
            map = new ObjectMapper().readValue(jsonObj.toJSONString(), Map.class) ;
            	
        } catch (JsonParseException e) {
            e.printStackTrace();
        } catch (JsonMappingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return map;
    }
	
	/**
	 * @author 최정우
	 * @since 2019.12.09
	 * 
	 * JSONObject to List<Map<String, Object>>
	 */
	public static List<Map<String, Object>> jsonArrayToMap( JSONArray jsonObj ) {
		List<Map<String, Object>> map = null;
        try {
            map = new ObjectMapper().readValue(jsonObj.toJSONString(), new TypeReference<List<Map<String, Object>>>(){}) ;
            	
        } catch (JsonParseException e) {
            e.printStackTrace();
        } catch (JsonMappingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return map;
    }
	
	/**
	 * @author 최정우
	 * @since 2019.12.09
	 * 
	 * xmlStr to JsonStr
	 */
	public static String xmlStrToJsonStr(String xmlStr) throws Exception {
 	    org.json.JSONObject jObject = XML.toJSONObject(xmlStr);
 	    ObjectMapper mapper = new ObjectMapper();
 	    mapper.enable(SerializationFeature.INDENT_OUTPUT);
 	    Object json = mapper.readValue(jObject.toString(), Object.class);
 	    String output = mapper.writeValueAsString(json);
 	    return output;
	}
	
	
	
	/**
     * @author 김성원 
	 * @since 2024.01.10
     * 
     * 데이터베이스 난수생성
     */
	public static String getRandKey(String prifix) {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
		long timeInMillis =System.currentTimeMillis();
		Date timeInDate = new Date(timeInMillis);		
		return prifix+"_"+sdf.format(timeInDate)+UUID.randomUUID().toString().substring(0,5);
	}
	
}
