package kr.gmtc.tss.db;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.Queue;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import kr.gmtc.tss.data.vo.TrackVO;
import kr.gmtc.tss.main.MainServer;
import kr.gmtc.tss.status.CountStatus;

public class BatchInsertManager {
	private final Logger logger = LoggerFactory.getLogger(this.getClass());

	private SqlSessionFactory sqlSessionFactory;
	private SqlSession session;
	
	private Queue<TrackVO> sqlLogTrackQueue;
	private Queue<TrackVO> sqlLogTrackLastQueue;

	private int savePos=0;
	private int saveLast=0;
	private int saveStatic=0;
	private int failPos=0;
	private int failLast=0;
	private int failStatic=0;
	
	private String topic;
	
	/**
	 * mybatis batch insert 속도가 느려서 jdbc 객체를 직접 제어함.
	 * @param sqlSessionFactory
	 * @param dbType
	 */
	public BatchInsertManager(SqlSessionFactory sqlSessionFactory, Queue<TrackVO> sqlLogTrackQueue,Queue<TrackVO> sqlLogTrackLastQueue) {
		this.sqlSessionFactory = sqlSessionFactory;
		this.sqlLogTrackQueue = sqlLogTrackQueue;
		this.sqlLogTrackLastQueue = sqlLogTrackLastQueue;
//		this.dbType=dbType;
		openSession(false);
	}
	
	public void openSession(boolean forceOpen) {
		if(session==null||forceOpen)
			session = sqlSessionFactory.openSession();
	}
	
	public void closeSession() {
		if(session!=null)
			session.close();
	}
	
	public void insertTrackData(TrackVO[] setTrackVOArr, String topic) {
		this.topic = topic;
		insertTrackData(setTrackVOArr,true);
	}
	
	
	private void insertTrackData(TrackVO[] setTrackVOArr, boolean isBatch) {
		int fusionFail = 0;
		int fusionSave = 0;
		int bboxFail = 0;
		int bboxSave = 0;
		
		if(setTrackVOArr==null || setTrackVOArr.length<1) {
//			logger.error("insertDataAll - No tableList data");
			logger.debug("insertTrackData(Batch:"+isBatch+") - no data in setTrackVOArr");
			return;
		}
//		Table destTable=tableList.get(0);
		
		
//		String sql = "INSERT INTO " + destTable.getName() + " (" + getInsertCols(destTable.getColumnList()) + ") " 
//					+ "VALUES (" + getInsertValueParams(destTable.getColumnList()) + ")";

//		String tableName = "SACP_CCTV_TRACK_" + setTrackVOArr[0].getRecptnDt().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
		
//		SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd");
//		String now_dt = format.format(setTrackVOArr[0].getRecptnDt());
		
//		String tableName = "SACP_TRACK_" + now_dt;
		String tableName = "SACP_TRACK_FUSION";
		String sql = "INSERT INTO "+tableName+" (" + 
				"	TRGT_ID, RECPTN_DT, LAT, LON, SPD, COS, TRGT_TY, REVISN_LAT, REVISN_LON, MODE_S_CD, SSR_CD, CLSGN, AC_REG_NO, REGISTER_ID) " + 
				"	VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
		
		String subTableName = "SACP_FUSION_TRACK_BBOX";
		String subSql = "INSERT INTO "+subTableName+" (" + 
				"	TRGT_ID, RECPTN_DT, CCTV_ID, BBOX_TOP_LEFT_X, BBOX_TOP_LEFT_Y, BBOX_BOTTOM_RIGHT_X, BBOX_BOTTOM_RIGHT_Y, CRDNT_X, CRDNT_Y, REGISTER_ID) " + 
				"	VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
		
		PreparedStatement ps=null;
		PreparedStatement stmt=null;
		Connection con=null;
		try  {
			
			con=session.getConnection();
			if(con.isClosed() || !con.isValid(1)) {
				closeSession();				
				openSession(true);
				con=session.getConnection();
			}
			con.setAutoCommit(false);
			
			ps = con.prepareStatement(sql);
			stmt = con.prepareStatement(subSql);
			for (int i = 0; i < setTrackVOArr.length; i++) {
				TrackVO vo = setTrackVOArr[i];
				//입력으로 처리
				//중복 입력 방지를 위함
				vo.setInserted(true);
				try {
					ps.setString(1, vo.getTargetId());
					ps.setString(2, vo.getReceptionDate());
					ps.setString(3, vo.getLatitude());
					ps.setString(4, vo.getLongitude());
					ps.setString(5, vo.getSpeed());
					ps.setString(6, vo.getCourse());
					ps.setString(7, vo.getTargetType());
					ps.setString(8, vo.getCorrectedLatitude());
					ps.setString(9, vo.getCorrectedLongitude());
					ps.setString(10, vo.getModeSCode());
					ps.setString(11, vo.getSsrCode());
					ps.setString(12, vo.getCallsign());
					ps.setString(13, vo.getTailNumber());
					ps.setString(14, "fusion");

					///건별 입력
					if(!isBatch) {
						ps.execute();
						savePos++;
						fusionSave++;
					}
					
					try {
						for (int j = 0; j < vo.getBbox().size(); j++) {
							stmt.setString(1, vo.getTargetId());
							stmt.setString(2, vo.getReceptionDate());
							stmt.setString(3, vo.getBbox().get(j).getCctvId());
							stmt.setString(4, vo.getBbox().get(j).getLeftTopX());
							stmt.setString(5, vo.getBbox().get(j).getLeftTopY());
							stmt.setString(6, vo.getBbox().get(j).getRightBottomX());
							stmt.setString(7, vo.getBbox().get(j).getRightBottomY());
							stmt.setString(8, vo.getBbox().get(j).getPx());
							stmt.setString(9, vo.getBbox().get(j).getPy());
							stmt.setString(10, "fusion");
							
							///건별 입력
							if(!isBatch) {
								stmt.execute();
								bboxSave++;
							} else {
								stmt.addBatch();	
							}
						}
					} catch(Exception ex) {
						bboxFail++;
						throw ex;
					}
					
					//배치 입력
					if(isBatch) {
						ps.addBatch();	
					}
				} catch (Exception e) {
					logger.debug("Data Insert error UseSqlLog");
					sqlLogTrackQueue.add(vo);
					failPos++;
					fusionFail++;
				}
				
			}
			
			if(isBatch) {
				ps.executeBatch();
				stmt.executeBatch();
				//배치 insert는 arr 건수만큼 
				//savePos+=setTrackVOArr.length;
				//logger.info("savePos 건수"+savePos);
				
				fusionSave = setTrackVOArr.length - fusionFail;
				logger.info("topic {} 배치 저장 건수 {} : 실패 건수 {}", topic, fusionSave, fusionFail);
			} else {
				logger.info("topic {} 단건 저장 건수 {} : 실패 건수 {}", topic, fusionSave, fusionFail);
			}

			con.commit();
			
		} catch (Exception e) {
			logger.error("insertData(Batch:"+isBatch+") error", e);
			if(isBatch) {
				logger.error("Try insert row by row");
				insertTrackData(setTrackVOArr, false);
			}
			
		} finally {
			
			if(ps!=null) {
				try {
					ps.close();
				} catch (SQLException e) {
					logger.error("insertData(Batch:"+isBatch+") PreparedStatement close error",e);
				}
			}
			
			if(stmt!=null) {
				try {
					stmt.close();
				} catch (SQLException e) {
					logger.error("insertData(Batch:"+isBatch+") PreparedStatement close error",e);
				}
			}
			if(con!=null) {
				try {
					con.commit();
					con.setAutoCommit(true);
				} catch (SQLException e) {
					logger.error("insertData(Batch:"+isBatch+") commit error",e);
				}
			}
		}
	}

	public void insertTrackLastData(TrackVO[] setTrackVOArr) {
		insertTrackLastData(setTrackVOArr,true);
	}
	
	private void insertTrackLastData(TrackVO[] setTrackLastVOArr, boolean isBatch) {

		if(setTrackLastVOArr == null || setTrackLastVOArr.length<1) {
//			logger.error("mergeDynamicLastAll - No tableList data");
			logger.debug("insertTrackLastData(Batch:"+isBatch+") - no data in setTrackVOArr");
			return;
		}
		
		String tableName = "SACP_TRACK_LAST";
		String sql = "";
		
		if(MainServer.LAST_DATA_CNT > 0) {
			sql = " UPDATE " + tableName
				  + " SET AC_REG_NO=?, CLSGN=?, SSR_CD=?, MODE_S_CD=?, RECPTN_DT=?, SENSOR_TY=?, CAT_TY=?, LAT=?, LON=?, "
				  + " SPD=?, COS=?, ALT=?, VIRTL_TRGT_AT=?, TRACK_CRRCT_AT=?, TRACK_CNFDNC=?, CCTV_ID=?, X_CNTS=?, Y_CNTS=?, "
				  + " UPDUSR_ID=?, UPDT_DT=TO_TIMESTAMP(SYSDATE,'yyyyMMdd')"
				  + " WHERE TRGT_ID = ?";
		}else {
			sql = " INSERT INTO " + tableName
				  + " ( TRGT_ID, AC_REG_NO, CLSGN, SSR_CD, MODE_S_CD, RECPTN_DT, SENSOR_TY, CAT_TY, "
				  + "  LAT, LON, SPD, COS, ALT, VIRTL_TRGT_AT, TRACK_CRRCT_AT, TRACK_CNFDNC, CCTV_ID, "
				  + "  X_CNTS, Y_CNTS, REGISTER_ID)"
				  + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
		}
		
		System.out.println("last sql = "+sql);
		PreparedStatement ps=null;
		Connection con=null;
		try  {
			con=session.getConnection();
			if(con.isClosed() || !con.isValid(1)) {
				closeSession();				
				openSession(true);
				con=session.getConnection();
			}
			con.setAutoCommit(false);
			
//			Statement statement = session.getConnection().createStatement();
			ps = con.prepareStatement(sql);
			
			for (int i = 0; i < setTrackLastVOArr.length; i++) {
				TrackVO vo = setTrackLastVOArr[i];
				//입력으로 처리
				//중복 입력 방지를 위함
				vo.setInserted(true);
				
				try {
					if(MainServer.LAST_DATA_CNT > 0) {
					}else {
					}
	
					///건별 입력
					if(!isBatch) {
						ps.execute();
						saveLast++;
						
						if(MainServer.LAST_DATA_CNT == 0 ) {
							MainServer.LAST_DATA_CNT = 1;
						}
					}
				} catch (Exception e) {
					logger.debug("Data Insert error UseSqlLog");
					sqlLogTrackLastQueue.add(vo);
					failLast++;
				}
				
				//배치 입력
				if(isBatch) {
					ps.addBatch();	
				}
			}
			
			if(isBatch) {
				ps.executeBatch();

				if(MainServer.LAST_DATA_CNT == 0 ) {
					MainServer.LAST_DATA_CNT = 1;
				}
				
				//배치 insert는 arr 건수만큼 
				saveLast += setTrackLastVOArr.length;
			}

			con.commit();
			
			
		} catch (Exception e) {
			logger.error("trackLast(Batch:"+isBatch+") error", e);
			if(isBatch) {
				logger.error("Try insert row by row");
				// insertTrackLastData(setTrackLastVOArr, false);
			}
			
		} finally {
			
			if(ps!=null) {
				try {
					ps.close();
				} catch (SQLException e) {
					logger.error("trackLast(Batch:"+isBatch+") PreparedStatement close error",e);
				}
			}
			if(con!=null) {
				try {
					con.commit();
					con.setAutoCommit(true);
				} catch (SQLException e) {
					logger.error("trackLast(Batch:"+isBatch+") commit error",e);
				}
			}
		}
	}
	public synchronized CountStatus getCountAndSetZero() {
		CountStatus countStatus = new CountStatus(savePos, saveLast, saveStatic, failPos, failLast, failStatic);
		
		savePos = 0;
		saveLast = 0;
		saveStatic = 0;
		failPos = 0;
		failLast = 0;
		failStatic = 0;

		return countStatus;
	}
	
	/**
	 * 
	 * 오류 발생시 건/건으로 저장할 수 있도록 수정
	 * @param setDynamicVoArr
	 */
	/*public void insertData(SetDynamicVO[] setDynamicVoArr) {

		if(setDynamicVoArr==null || setDynamicVoArr.length<1) {
//			logger.error("insertDataAll - No tableList data");
			logger.debug("insertData - no data in setDynamicVoArr");
			return;
		}
//		Table destTable=tableList.get(0);
		
		
//		String sql = "INSERT INTO " + destTable.getName() + " (" + getInsertCols(destTable.getColumnList()) + ") " 
//					+ "VALUES (" + getInsertValueParams(destTable.getColumnList()) + ")";

		String tableName = "th_ais_" + setDynamicVoArr[0].getDateTime().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
		
		String sql = "INSERT INTO "+tableName +" (" + 
				"	ship_id, recv_dt, lon_val, lat_val, sog_val, cog_val, hdg_val, rot_val, zone_id) " + 
				"	VALUES (?, TO_TIMESTAMP(?,'yyyyMMddhh24miss'), ?, ?, ?, ?, ?, ?, ?)";
		
		
		PreparedStatement ps=null;
		Connection con=null;
		try  {
			
			con=session.getConnection();
			if(con.isClosed() || !con.isValid(1)) {
				closeSession();				
				openSession(true);
				con=session.getConnection();
			}
			con.setAutoCommit(false);
					
			
//			Statement statement = session.getConnection().createStatement();
			ps = con.prepareStatement(sql);
			
			for (int i = 0; i < setDynamicVoArr.length; i++) {
				SetDynamicVO vo = setDynamicVoArr[i];
				//입력으로 처리
				//중복 입력 방지를 위함
				vo.setInserted(true);
				
				
				try {
					
					ps.setString(1, vo.getShipID());
	//				ps.setDate(2, vo.getDateTime());
					ps.setString(2, vo.getDateTime().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")));
					ps.setDouble(3, vo.getLongitude());
					ps.setDouble(4, vo.getLatitude());
					ps.setDouble(5,vo.getSog());
					ps.setDouble(6,vo.getCog());
					ps.setInt(7, vo.getHeading());
					ps.setInt(8, vo.getRot());
					ps.setString(9, vo.getZoneId());
	//				ps.addBatch();
					ps.execute();

				} catch (Exception e) {
//					logger.error("insertData error", e);
					
					
				}
				
			}
//			ps.executeBatch();

			con.commit();
			
			
		} catch (Exception e) {
			logger.error("insertData error", e);
		} finally {
			
			if(ps!=null) {
				try {
					ps.close();
				} catch (SQLException e) {
					logger.error("insertData PreparedStatement close error",e);
				}
			}
			if(con!=null) {
				try {
					con.commit();
					con.setAutoCommit(true);
				} catch (SQLException e) {
					logger.error("insertData commit error",e);
				}
			}
		}
		
	}*/
	
//	/**
//	 * Insert colums 성성
//	 * @param columnList
//	 * @return
//	 */
//	private String getInsertCols(List<Column> columnList) {
//		StringBuilder result=new StringBuilder();
//		for (int i 	= 0; i < columnList.size(); i++) {
//			result.append(columnList.get(i).getName());
//			//마지막에는 ,를 안붙이기 위해
//			if(i!=columnList.size()-1)
//				result.append(",");
//		}
//		return result.toString();
//	}
//	
//
//	private String getInsertValueParams(List<Column> columnList) {
//		StringBuilder result=new StringBuilder();
//		for (int i 	= 0; i < columnList.size(); i++) {
//			result.append(setInsertParam(columnList.get(i)));
//			//마지막에는 ,를 안붙이기 위해
//			if(i!=columnList.size()-1)
//				result.append(",");
//		}
//		return result.toString();
//	}
//
//	/**
//	 * 파라미터는 ? 로 표현하여 ps.set~~~으로...
//	 * @param column
//	 * @return
//	 */
//	private String setInsertParam(Column column) {
//		switch (dbType) {
//		case ORACLE:
//			return setInsertParamOracle(column);
//		default:
//			//mariadb default
//			return setInsertParamMariadb(column);
//		}
//		
//		
//	}
//	
//	
//	private String setInsertParamMariadb(Column column) {
//		
//		if(column.isCustom()) {
//			return column.getCustomStr();
//		}else {
//			switch (column.getColumnTypeName()) {
//			case "DATE":
//			case "DATETIME":
//				return "STR_TO_DATE(?,'%Y%m%d%H%i%S')";
//			case "TIMESTAMP":
//			case "TIMESTAMP_WITH_TIMEZONE":
//				return "STR_TO_DATE(?,'%Y%m%d%H%i%S%f')";	
//			case "TIME":
//			case "TIME_WITH_TIMEZONE":
//				return "STR_TO_DATE(?,'%H%i%S')";	
//			default:
//				return "?";
//			}
//		}
//	}
//	
//
//	
//	
//	private String setInsertParamOracle(Column column) {
//
//		if(column.isCustom()) {
//			return column.getCustomStr();
//		}else {
//			switch (column.getColumnTypeName()) {
//			case "DATE":
//			case "DATETIME":
//				return "TO_DATE(?,'yyyymmddhh24miss')";
//			case "TIMESTAMP":
//			case "TIMESTAMP_WITH_TIMEZONE":
//				return "TO_TIMESTAMP(?,'yyyymmddhh24missff')";	
//			case "TIME":
//			case "TIME_WITH_TIMEZONE":
//				return "TO_DATE(?,'hh24miss')";	
//			default:
//				return "?";
//			}
//		}
//		
//	}
	
	
	
	

}
