package com.takensoft.pohangTp.common.vo;

import java.math.BigDecimal;
import java.sql.Timestamp;
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.regex.Pattern;

import com.takensoft.pohangTp.common.connection.db.vo.ConnectionDB;
import com.takensoft.pohangTp.common.util.CommonUtil;
import com.takensoft.pohangTp.common.util.StringUtil;




public class SystemCode {

	//191자 => mariaDB에서 indexing가능한 최대 크기 765Byte/한글 4Byte(utf8mb4) 
	public final static int DEFAULT_VARCHAR_SIZE = 191;
		
	//mariaDB에서 indexing가능한 varchar 최대 크기 765Byte
	public final static int MAX_VARCHAR_SIZE = 765;
	
	
	/**
	 * @author 최정우
	 * @since 2019.11.13
	 * 
	 * 데이터 타입을 정의한 상수 Class 입니다.
	 * 상수명(java데이터타입, 초기값, mysql데이터타입, display데이터타입
	 */
	public enum DataType {
		NULL(null, null)
		, BYTE(Byte.class, 0)
		, BOOL(Boolean.class, false)
		, CHAR(Character.class, '\u0000')
		, STRING(String.class, "")
		, SHORT(Short.class, 0)
		, INT(Integer.class, 0)
		, DECIMAL(BigDecimal.class,0)
		, LONG(Long.class, 0)
		, FLOAT(Float.class, 0.0)
		, DOUBLE(Double.class, 0.0)
		, DATE(Date.class, "")
		, DATETIME(Date.class, "")
		, TIMESTAMP(Timestamp.class, "")
		, ENTER(Character.class,'\n');

		/**
		 * enum(열거형데이터)의 생성자 
		 * 1. 상수에 나열된 parameter와 형태가 똑같이 만들어줘야함.
		 * 2. 접근제한자는 private로만 다른 접근제한자는 허용하지 않음
		 */
		private DataType (Class<?> javaType, Object iniValue) {
	        this.javaType = javaType;
	        this.initValue = iniValue;
	    }
		
		/**
		 * java데이터타입
		 */
		final private Class<?> javaType;
		
		/**
		 * 초기값
		 */
		final private Object initValue;
		
	    public Class<?> getJavaType() {
	        return javaType;
	    }
	    public Object getInitValue() {
	        return initValue;
	    }
	    	 
	    
	    /**
		 * 데이터 타입 검사, DataType 조회
		 */
	    public static Map<DataType, String> getDataTypeList () {
	    	Map<DataType, String> info = new LinkedHashMap<DataType, String>();
	    	info.put(STRING, "문자열");
	    	info.put(LONG, "정수");
	    	info.put(DOUBLE, "실수");
	    	info.put(DATE, "날짜");
	    	info.put(DATETIME,"날짜,시간");
	    	return info;
		}
	    
	    
	    /**
		 * 데이터 타입 검사, DataType 조회
		 */
	    public static List<DataType> getDataTypeArray () {
	    	List<DataType> info = new ArrayList<DataType>();
	    	info.add(STRING);
	    	info.add(LONG);
	    	info.add(DOUBLE);
	    	info.add(DATE);
	    	info.add(DATETIME);
	    	info.add(BOOL);
	    	return info;
		}
	    
	    /**
		 * 데이터 타입 검사, DataType 조회
		 */
	    public static DataType getDataType (String text) {
	    	/*
	    	 * 2021-11-02 김혜민 dataType 전화번호, 날짜 정규식 수정
	    	 * 
	    	 * 
	    	 * 전화번호 정규식*/
	    	String regExp = "^\\d{2,3}-\\d{3,4}-\\d{4}$";
	    	String regDateTime = "\\d{4}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01]) (0[1-9]|1[0-9]|2[0-4]):(0[1-9]|[1-5][0-9]):(0[1-9]|[1-5][0-9])";
	    	String regDate = "\\d{4}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])";
	    			
			if(StringUtil.isEmpty(text)) {
				return NULL;
			} else if(CommonUtil.isLong(text)) {
				/*
				 * 숫자로 변환 가능 하지만 앞에 '0'이 붙어 있으면 문자열로 봄
				 * ex) 0102 -> String, 1102 -> Long
				 */
				if (text.length() > 1 && text.indexOf("0") == 0) {
					return STRING;
				} else {
					return LONG;
				}
			}else if(CommonUtil.isDouble(text)) {
				
				return DOUBLE;
				
			} else if (CommonUtil.isDate(text)) {
				/*
		    	 * 2021-11-02 김혜민 dataType 전화번호, 날짜 정규식 수정
		    	 * 
		    	 * 
		    	 * 전화번호 정규식*/
		    	boolean expResult = Pattern.matches(regExp, text);
		    	boolean dateTimeResult = Pattern.matches(regDateTime, text);
		    	boolean dateResult = Pattern.matches(regDate, text);
		    	
				if(expResult) {
					return STRING;
				}else if(dateResult) {				
					return DATE;
				}else {
					return DATETIME;
				}
				
			} else {
				return STRING;
			}
		}
	    
	    /**
		 * 데이터 타입 검사, dbDataType 조회
		 */
	    public static String convertDbDataType (DataType datatype, Integer size) {
	    	if (datatype == STRING) {
	    		if (size == null || size <= MAX_VARCHAR_SIZE) {
	    			return "varchar";
	    		} else {
	    			return "text";
	    		}
	    	} else if (datatype == DATE) {
	    		return "date";
	    	} else if (datatype == DATETIME){
	    		return "datetime";
	    	} else if (datatype == LONG) {
	    		return "bigint";
	    	} else if (datatype == DOUBLE) {
	    		return "double";
	    	} else {
	    		return "varchar";
	    	}
	    }
	    
	    /**
		 * 데이터 타입 검사, dbDataType -> javaType 조회
		 */
	    public static DataType convertDataTypeDbtoJava (DataType datatype, Integer size) {
	    	
	    	
	    	return null;	    	
	    }
	    
	    
	    /**
		 * 데이터 타입 형태로 값 변경
		 */
		public static Object parse(DataType dataType, Object value) {
			if (CommonUtil.isNull(value) == true) {
				return null;
			}
			
			Class<?> javaType = dataType.getJavaType();
			try {
				if (javaType.getSimpleName().equals("Byte")) {
					return Byte.parseByte(value.toString());
				} else if (javaType.getSimpleName().equals("Boolean")) {
					return Boolean.parseBoolean(value.toString());
				} else if (javaType.getSimpleName().equals("Character")) {
					return value.toString().toCharArray();
				} else if (javaType.getSimpleName().equals("String")) {
					return value.toString();
				} else if (javaType.getSimpleName().equals("Short")) {
					//Short.parseShort(value.toString().replaceAll("[^0-9]",""));
					return Short.parseShort(value.toString());
				} else if (javaType.getSimpleName().equals("Integer")) {
					//Integer.parseInt(value.toString().replaceAll("[^0-9]",""));
					return Integer.parseInt(value.toString());
				} else if (javaType.getSimpleName().equals("Long")) {
					//Long.parseLong(value.toString().replaceAll("[^0-9]",""));
					return Long.parseLong(value.toString());
				} else if (javaType.getSimpleName().equals("Float")) {
					//Float.parseFloat(value.toString().replaceAll("[^0-9]",""));
					return Float.parseFloat(value.toString());
				} else if (javaType.getSimpleName().equals("Double")) {
					//Double.parseDouble(value.toString().replaceAll("[^0-9]",""));
					return Double.parseDouble(value.toString());
				} else {
					return value;
				}
			} catch (Exception e) {
				e.printStackTrace();
				return dataType.getInitValue();
			}
		}
		
		/**
		 * 데이터 타입 형태로 값 변경
		 */
		public static boolean parseCheck (DataType dataType, Object value) {
			if (CommonUtil.isNull(value) == true) {
				return false;
			}
			
			Class<?> javaType = dataType.getJavaType();
			try {
				if (javaType.getSimpleName().equals("Byte")) {
					Byte.parseByte(value.toString());
				} else if (javaType.getSimpleName().equals("Boolean")) {
					Boolean.parseBoolean(value.toString());
				} else if (javaType.getSimpleName().equals("Character")) {
					value.toString().toCharArray();
				} else if (javaType.getSimpleName().equals("String")) {
					value.toString();
				} else if (javaType.getSimpleName().equals("Short")) {
					//Short.parseShort(value.toString().replaceAll("[^0-9]",""));
					Short.parseShort(value.toString());
				} else if (javaType.getSimpleName().equals("Integer")) {
					//Integer.parseInt(value.toString().replaceAll("[^0-9]",""));
					Integer.parseInt(value.toString());
				} else if (javaType.getSimpleName().equals("Long")) {
					//Long.parseLong(value.toString().replaceAll("[^0-9]",""));
					Long.parseLong(value.toString());
				} else if (javaType.getSimpleName().equals("Float")) {
					//Float.parseFloat(value.toString().replaceAll("[^0-9]",""));
					Float.parseFloat(value.toString());
				} else if (javaType.getSimpleName().equals("Double")) {
					//Double.parseDouble(value.toString().replaceAll("[^0-9]",""));
					Double.parseDouble(value.toString());
				}
				
				return true;
			} catch (Exception e) {
				//e.printStackTrace();
				return false;
			}
		}
	    
	}
	
	/**
	 * @author 최정우
	 * @since 2019.11.20
	 * 
	 * 데이터 베이스 타입별 상수를 정의
	 */
	public enum DatabaseType {
		MYSQL("MySql", "com.mysql.jdbc.Driver", "jdbc:mysql://", "3306", "allowMultiQueries=true&useSSL=false&characterEncoding=UTF-8&serverTimezone=UTC")
		, MARIADB("MariaDB", "org.mariadb.jdbc.Driver", "jdbc:mariadb://", "3306", "")
		, ORACLE("Oracle", "oracle.jdbc.OracleDriver", "jdbc:oracle:thin:@", "1521", "")
		, MSSQL("MS-SQL", "com.microsoft.sqlserver.jdbc.SQLServerDriver", "jdbc:sqlserver://", "1433", "")
		, TIBERO("Tibero", "com.tmax.tibero.jdbc.TbDriver", "jdbc:tibero:thin:@", "8629", "")
		, POSTGRESQL("PostgreSql", "org.postgresql.Driver", "jdbc:postgresql://", "5432", "");
		
		private DatabaseType(String value, String driverClassName, String baseUrl, String defalutPort, String option) {
			this.value = value;
			this.driverClassName = driverClassName;
			this.baseUrl = baseUrl;
			this.defalutPort = defalutPort;
			this.option = option;
		}
		
		/**
		 * 한글값
		 */
		final private String value;
		
		/**
		 * 드라이버 클래스
		 */
		final private String driverClassName;
		
		/**
		 * 접속 URL
		 */
		final private String baseUrl;
		
		/**
		 * 기본 포트
		 */
		final private String defalutPort;
		
		/**
		 * 접속 옵션
		 */
		final private String option;
		
		
		/**
		 * 한글값 조회
		 */
		public String getValue () {
			return value;
		}
		
		/**
		 * 드라이버 클래스 조회
		 */
		public String getDriverClassName () {
			return driverClassName;
		}
		
		/**
		 * 접속 URL 조회
		 */
		public String getBaseUrl () {
			return baseUrl;
		}
		
		/**
		 * 기본 포트 조회
		 */
		public String getDefaultPort () {
			return defalutPort;
		}
		
		/**
		 * 접속 옵션 조회
		 */
		public String getOption () {
			return option;
		}
		
		/**
		 * 접속 URL 조회
		 */
		public String getUrl (ConnectionDB connectionDB) {
			StringBuilder builder = new StringBuilder();		
			if ((this == ORACLE || this == TIBERO ) && connectionDB.isGroupDatabase() == false) {
				builder.append(this.baseUrl);
				builder.append(connectionDB.getConectIp());
				builder.append(":");
				builder.append(connectionDB.getConectPort());
				builder.append(":");
				builder.append(connectionDB.getDatabaseNm());				
			} else {
				builder.append(this.baseUrl);
				builder.append(connectionDB.getConectIp());
				builder.append(":");
				builder.append(connectionDB.getConectPort());
				if(this == MSSQL) {
					builder.append(";databaseName=");
					
				}else {
					builder.append("/");
				}				
				builder.append(connectionDB.getDatabaseNm());				
				if (this == MYSQL) {
					builder.append("?");
					builder.append(this.option.replaceAll("&amp;", "&"));
				}else if(this == POSTGRESQL) {
					builder.append("?");
					builder.append("currentSchema="+connectionDB.getSchemaNm());					
				}
			}
			
			return builder.toString();
		}
		
		/**
		 * 접속 URL 조회
		 */
		public String getUrl (String ip, String port, String databaseName, boolean isGroupDatabase, String option) {
			StringBuilder builder = new StringBuilder();		
			if ((this == ORACLE || this == TIBERO ) && isGroupDatabase == false) {
				builder.append(this.baseUrl);
				builder.append(ip);
				builder.append(":");
				builder.append(port);
				builder.append(":");
				builder.append(databaseName);
			} else {
				builder.append(this.baseUrl);
				builder.append(ip);
				builder.append(":");
				builder.append(port);
				builder.append("/");
				builder.append(databaseName);
			}
			
			if (StringUtil.isEmpty(option) == false) {
				builder.append("?");
				option.replaceAll("&amp;", "&");
				builder.append(option);
			}	
		
			
			return builder.toString();
		}
		
		/**
		 * 데이터 베이스 타입별 상수 목록 조회
		 */
		public static Map<DatabaseType, String> getDatabaseTypeList () {
	    	Map<DatabaseType, String> info = new LinkedHashMap<DatabaseType, String>();
	    	DatabaseType[] array = DatabaseType.values();
	    	for (int i = 0; i < array.length; i++) {
	    		info.put(array[i], array[i].getValue());
	    	}
	    	return info;
		}
		
		/**
		 * 데이터 베이스 타입별 상수 목록 조회
		 */
		public static Map<DatabaseType, Map<String, String>> getDatabaseTypeInfoList () {
			Map<DatabaseType, Map<String, String>> info = new LinkedHashMap<DatabaseType, Map<String, String>>();
	    	DatabaseType[] array = DatabaseType.values();
	    	for (int i = 0; i < array.length; i++) {
	    		Map<String, String> detailInfo = new HashMap<String, String>();
	    		detailInfo.put("key", array[i].toString());
	    		detailInfo.put("value", array[i].getValue());
	    		detailInfo.put("driverClassName", array[i].getDriverClassName());
	    		detailInfo.put("baseUrl", array[i].getBaseUrl());
	    		detailInfo.put("defalutPort", array[i].getDefaultPort());
	    		detailInfo.put("option", array[i].getOption());
	    		info.put(array[i], detailInfo);
	    	}
	    	return info;
		}
		
	}
	
	
	/**
	 * @author 최정우
	 * @since 2019.11.13
	 * 
	 * SPMiner에서 등록되는 파일 타입과, 해당 파일 타입의 등록가능한 확장자를 정의한 상수 Class 입니다.
	 * 첨부파일, 이미지파일, 데이터 셋 파일 순서입니다.
	 */
	public enum FileType {
		ALL_FILE(Arrays.asList("bmp","jpg","gif","png","jpeg","bmp","wma","avi","wav","mp3","mp4","mpeg","wmv","asf","pdf","ppt","pptx","docx","xls","xlsx","csv","hwp","txt","doc","zip", "json", "xml"))
		, IMG_FILE(Arrays.asList("bmp","jpg","gif","png","jpeg"))
		, DATASET_FILE(Arrays.asList("xls","xlsx","csv"));
		
		private FileType(List<String> extensions) {
			this.extensions = extensions;
		}
		
		/**
		 * 확장자 목록
		 */
		final private List<String> extensions;
		
		/**
		 * 확장자 목록 조회
		 */
		public List<String> getExtensions () {
			return extensions;
		}
	}
		
}
