package kr.gmtc.gw.dev.packetsend.controller;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;

import javax.annotation.Resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import kr.gmtc.gw.comp.socket.udp.UDPEventListener;
import kr.gmtc.gw.comp.socket.udp.UDPManager;
import kr.gmtc.gw.comp.socket.utils.SocketUtil;
import kr.gmtc.gw.comp.socket.vo.MsgObjVO;
import kr.gmtc.gw.dev.packetsend.file.TextReaderMergeAndSort;
import kr.gmtc.gw.dev.packetsend.thread.CustomThread;

//@Component("sendTestFileController")
public class SendTestFileController implements ApplicationListener<ContextClosedEvent> {
    
	// @Resource(name = "ServiceConfig")
	// private ServiceConfig serviceConfig;
	
	/* 실생상태 확인 */
	private boolean running = false;
	
	/* UDP test 소켓 */
	//UDPEchoClient UDPSocket ;
	
	@Resource(name = "packetQ")
	Queue<String> packetQ;
	
	@Value("${asde.filepath1}")
	private String sfilepath1;
	
	@Value("${asde.filepath2}")
	private String sfilepath2;
	
	public Queue<Map<String, String>> fileQ;

	// 업무처리 //
	/** 업무 처리 스레드 */
	private CustomThread bizThread;

	protected Logger logger;

	private LinkedBlockingQueue<MsgObjVO> sendQ;
	private Thread sendQThread;

	private boolean isRunning;

	protected UDPManager udpManager;
	private UDPEventListener udpListener;

	protected SendTestFileController(@Value("${root}") String path){

		isRunning  = false;

		logger = LoggerFactory.getLogger(this.getClass());

		logger.info("[SendTestFileController] path : " + path);
		logger.info("IN");

		sendQ = new LinkedBlockingQueue<MsgObjVO>(100000);

		udpListener = createUDPEventListener();
		udpManager  = new UDPManager(path, udpListener);

		sendQThread = new Thread(new Runnable() {
			@Override
			public void run() {
				while (isRunning) {
					try {
						int cnt = sendQ.size();
						if (cnt > 10000) {
							cnt = 10000;
						}

						for (int i = 0; i <= cnt - 1; i++) {
							if (sendQ != null) {
								MsgObjVO vo = sendQ.poll();
								if (vo != null) {
									udpManager.broadcastMsg(vo);
									vo = null;
								}
							}
						}
						Thread.sleep(1);
					} catch (InterruptedException e) {
						logger.error(SocketUtil.getStatckTrace(e));
					}
				}
			}
		});
		sendQThread.setName("GmtController-sendQThread");

	}
	/**
	 * 실행 (1순위)<br>
	 * 프로그램  초기화
	 */
	@Order(0)
	@EventListener(ApplicationReadyEvent.class)
	public void initialize() {
		try {
			// 업무 스레드 생성, 대기 없이 무한반복
			bizThread = new CustomThread("bizThread", this, CustomThread.NO_SLEEP, this::bizWork, null, false);
			
			// UDP소켓 객체 생성
			//UDPSocket = new UDPEchoClient();
					
			fileQ =  new LinkedBlockingQueue<Map<String, String>>();
			
		} catch(Exception e){
			logger.error("[MainController] Prepare Fail " + e.getMessage() );
			System.exit(1);
		}
	}
	
	
	/**
	 * 실행 (2순위)<br>
	 * 프로그램 실행
	 */
	@Order(1)
	@EventListener(ApplicationReadyEvent.class)
	public void start() {
		try {
			running = true;
			udpManager.start();
			
			// 업무로직 시작
			bizThread.start();
			getFileBufer(fileQ, sfilepath1, sfilepath2);
			
		} catch (Exception e) {
			logger.error("[MainController] Start Fail " + e.getMessage());
			System.exit(1);
		}
	}
	
	/**
	 * 업무 로직<br>
	 * 자동으로 무한반복 하므로 내부에 while문 필요 없음.<br>
	 * 예외처리를 하지 않을 경우 {@link CustomThread} 내부에서 로그 처리
	 */
	private void bizWork() throws InterruptedException {
		
		// 읽어드린 데이터를 모두 전송한경우 초기화
		if(fileQ.peek() == null) {
			getFileBufer(fileQ, sfilepath1, sfilepath2);
		}
		
		try {		
			
			// 클라이언트 포트 브로드캐스트
			MsgObjVO msgVO = new MsgObjVO();
			if(fileQ.peek() != null) {
				
				Map<String, String> qMap = new HashMap<String, String>();
				Map<String, String> qMap_after = new HashMap<String, String>();
				
				qMap = fileQ.poll();
				
				//msgVO.setMsg(qMap.get("data").getBytes(serviceConfig.getClientSendCharset()));
				
				String sHexData = qMap.get("data").replaceAll("\\p{Z}", "");
				
				msgVO.setMsg(new java.math.BigInteger(sHexData, 16).toByteArray());
				broadcastUDP(msgVO);
				
				// 다음 전송까지 대기 시간 계산
				qMap_after = fileQ.peek();
				
				String sTime_befre = qMap.get("time");
				String sTime_after = qMap_after.get("time");
				
				Date tb = new SimpleDateFormat("yyyyMMddHHmmss").parse(sTime_befre);
		        Date ta = new SimpleDateFormat("yyyyMMddHHmmss").parse(sTime_after);
				
		        long diffSec = (ta.getTime() - tb.getTime());
		        		
		        logger.info("[MainController-bizWork] send log : " + "***전송:" + sTime_befre + "***다음:" + sTime_after + "***차이:" + diffSec);
		        
//		        if(diffSec <0 ) {
//		        	String sTim = "" ;
//		        }
				
//				System.out.print("\n***" + lSendCnt + "건 전송*** " + qMap.get("data"));
				
				Thread.sleep(diffSec);
			}
			
		} catch (InterruptedException e) {
			if (running) {
				logger.error("[MainController-bizWork] InterruptedException : " + e.getMessage());
			} else { // 종료 중 인터럽트 발생 시 throw
				System.out.println(e);
			}
		} catch (Exception e) {
			if (running) {
				logger.error("[MainController-bizWork] running process Exception : " + e.getMessage());
			} else { // 종료 중 인터럽트 발생 시 throw
				System.out.println(e);
			}
		}
		
		
	}
	
	
	public void getFileBufer(Queue<Map<String, String>> fileQ, String sFilePath1, String sFilePath2) {
		
		//TextReader rf = new TextReader(file1, file2, sFilePath1, sFilePath2);
		TextReaderMergeAndSort rf = new TextReaderMergeAndSort(fileQ, sFilePath1, sFilePath2);
		rf.readFile();
	   // System.out.println(result);

	}

	public void stop() {
		
		// UDP소켓 종료
//		try {
//			UDPSocket.start();
//		} catch (IOException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
		
	}


	protected void broadcastUDP(MsgObjVO vo) {
		try {
			if (vo != null && vo.getMsg() != null && sendQ != null) {
//				vo.setSendModeType();
				if (!vo.getMsg().equals("")) {
					sendQ.offer(vo);
				}
				
			}
		} catch (Exception e) {
			logger.error(SocketUtil.getStatckTrace(e));
		}
	}

	protected UDPEventListener createUDPEventListener() {
		UDPEventListener listener = new UDPEventListener() {

			@Override
			public void udpDataIn(MsgObjVO vo) {
				// TODO Auto-generated method stub
				throw new UnsupportedOperationException("Unimplemented method 'udpDataIn'");
			}

			@Override
			public void udpSendData(MsgObjVO vo) {
				// TODO Auto-generated method stub
				throw new UnsupportedOperationException("Unimplemented method 'udpSendData'");
			}

			@Override
			public void udpSendData(String ip, int port, MsgObjVO vo) {
				// TODO Auto-generated method stub
				throw new UnsupportedOperationException("Unimplemented method 'udpSendData'");
			}

			@Override
			public void connected(String ip, int port, int statusCode, String description) {
				// TODO Auto-generated method stub
				throw new UnsupportedOperationException("Unimplemented method 'connected'");
			}

			@Override
			public void disconnected(String ip, int port, int statusCode, String description) {
				// TODO Auto-generated method stub
				throw new UnsupportedOperationException("Unimplemented method 'disconnected'");
			}

			@Override
			public void connectionStatus(String ip, int port, String connEvent, int statusCode, String description) {
				// TODO Auto-generated method stub
				throw new UnsupportedOperationException("Unimplemented method 'connectionStatus'");
			}

		};
		
		return listener;
	}

	/** 종료 이벤트 (강제종료시 수행 안됨) */
	@Override
	public void onApplicationEvent(@SuppressWarnings("unused") ContextClosedEvent event) {
		//this.stop();
		logger.info("====================== SYSTEM STOPED ======================");
	}
    
}
