package kr.gmtc.tss.data;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Queue;

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

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

import kr.gmtc.tss.data.vo.TrackVO;
import kr.gmtc.tss.data.vo.TrackVO.BoundingBox;
import kr.gmtc.tss.status.CountStatus;

public class ParseTargetRunnable implements Runnable {
	
	private boolean flagFile;
	private boolean flagDataBase;
	private boolean flagDataFile;
	
	private Logger logger = LoggerFactory.getLogger(this.getClass());
	
	Queue<String> rcvQueue;
	Queue<TrackVO> trackQueue;
	Queue<TrackVO> trackLastQueue;
	Queue<String> dataFileQueue;
	Queue<TrackVO> messageBodyQueue;
	
	private int rcvPos = 0;
	private int rcvStatic = 0;
	
	private boolean isRunning = false;	
	private int listChunkSize = 100; 
	private long sleepMillis;
	
	private String classification;
	private String swclassification;
	private String logclassification;

	public ParseTargetRunnable(Queue<String> rcvQueue, Queue<TrackVO> trackQueue, Queue<TrackVO> trackLastQueue
			, Queue<String> dataFileQueue,Queue<TrackVO> messageBodyQueue, int listChunkSize
			, long sleepMillis, boolean flagFile, boolean flagDataBase, boolean flagDataFile
			, String classification, String swclassification, String logclassification) {
		this.rcvQueue = rcvQueue;
		this.trackQueue = trackQueue;
		this.trackLastQueue = trackLastQueue;
		this.dataFileQueue = dataFileQueue;
		this.messageBodyQueue = messageBodyQueue;
		this.listChunkSize = listChunkSize;
		this.sleepMillis = sleepMillis;
		this.flagFile = flagFile;
		this.flagDataBase = flagDataBase;
		this.flagDataFile = flagDataFile;
		this.classification = classification;
		this.swclassification = swclassification;
		this.logclassification = logclassification;
	}
	
	@Override
	public void run() {
		isRunning = true;
		
		while (isRunning) {
			try {
				
				int loopCnt = 0;
				if(rcvQueue.size()>listChunkSize) {
					loopCnt = listChunkSize;
				}else {
					loopCnt = rcvQueue.size();
				}
				String [] chunkArr = new String[loopCnt];
				
				for (int i = 0; i < loopCnt; i++) {
					chunkArr[i] = rcvQueue.poll();
				}
				
				for (int i = 0; i < chunkArr.length; i++) {
					parseAndEnqueue(chunkArr[i]);
				}
				
			} catch (Exception e) {
				if (e.getMessage() != null) {
					logger.debug("[ParseTarget] Parsing Error Message : " ,e);
				} else {
					logger.info("[ParserThread] Parsing Error Call Stack" ,e);
				}
			} 
			
			sleep(sleepMillis);
		}
	}
	
	private void parseAndEnqueue(String result) {
		try {
			ArrayList<TrackVO> histroyQueue = new ArrayList<TrackVO>();
			ObjectMapper mapper = new ObjectMapper();
			
			Map<String, Object> convert = mapper.readValue(result, new TypeReference<Map<String, Object>>() {});
			ArrayList<LinkedHashMap<String, Object>> list = (ArrayList<LinkedHashMap<String, Object>>) convert.get("data");
			
			logger.info("수신 데이터 변환 건수 {}", list.size());
			
			DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss.SSS");
	        String dateTime = dateFormat.format(new Date());
			
	        StringBuilder sb = new StringBuilder();
			
	        sb.append(dateTime); sb.append(",");
	        sb.append(logclassification); sb.append(",");	//log구분
	        sb.append(swclassification); sb.append(",");	//sw구분
	        sb.append(classification); sb.append("|");		//서버구분
			int i = 0;
			int z = 0;
			for (LinkedHashMap<String, Object> map : list) {
				TrackVO t = mapper.convertValue(map, TrackVO.class);
				
				if(flagDataFile) {
					if (i > 0) sb.append("^");
					
					sb.append(t.getTargetId()); 			sb.append(",");	
					sb.append(t.getReceptionDate()); 		sb.append(",");	
					sb.append(t.getLatitude()); 			sb.append(",");	
					sb.append(t.getLongitude()); 			sb.append(",");	
					sb.append(t.getSpeed()); 				sb.append(",");	
					sb.append(t.getCourse()); 				sb.append(",");	
					sb.append(t.getTargetType()); 			sb.append(",");	
					sb.append(t.getCorrectedLatitude()); 	sb.append(",");	
					sb.append(t.getCorrectedLongitude()); 	sb.append(",");	
					sb.append(t.getModeSCode()); 			sb.append(",");	
					sb.append(t.getSsrCode()); 				sb.append(",");	
					sb.append(t.getCallsign()); 			sb.append(",");	
					sb.append(t.getTailNumber()); 			sb.append(",");
					int j = 0;
					for (BoundingBox b : t.getBbox()) {
						if (j > 0) sb.append("$"); 
						sb.append(b.toDataFormat());
						
						j++;
					}
					sb.append(",");
					sb.append(t.getAlt());
					
					logger.info("logstash 포맷 변환 건수 {}", z++);
				}
				
				histroyQueue.add(t);
				i++;
			}
			
			
			if(flagFile) {
				messageBodyQueue.addAll(histroyQueue);
			}
			
			if(flagDataFile) {
				dataFileQueue.add(sb.toString());
			}
			
			if(flagDataBase) {
				if (histroyQueue.size() > 0) {
					trackQueue.addAll(histroyQueue);
				} 
				
//				if (flagDataBase) {
//					histroyQueue
//					.stream()
//					.collect(Collectors.groupingBy(TrackVO::getTargetId,           
//							Collectors.maxBy(Comparator.comparing(TrackVO::getReceptionDate))
//							)).forEach((key, value) -> {
//								trackLastQueue.add(value.get());
//							});;
//				}
			}
			
		} catch (Exception e) {
			logger.error("[ParserThread] Parsing Error Message : " + e.getMessage());
			
//			e.printStackTrace();
//			if (e.getMessage() != null) {
//				logger.debug("[ParserThread] Parsing Error Message : " + e.getMessage());
//				logger.debug("",e);
//			} else {
//				logger.debug("[ParserThread] Parsing Error Packet Message :" + result);
//			}
		}
	}

	
	public synchronized CountStatus getCountAndSetZero() {
		CountStatus countStatus = new CountStatus(rcvPos,rcvStatic);
		rcvPos = 0;
		rcvStatic = 0;
		return countStatus;
	}
	
	private void sleep(long millis) {
		try {
			Thread.sleep(millis);
		} catch (InterruptedException e) {
			logger.debug("[ParseTargetRunnable] sleep error : "+e.getLocalizedMessage());
		}
	}

	public void setRunning(boolean isRunning) {
		this.isRunning = isRunning;
	}
}
