package kr.gmtc.gw.eyegw.controller;

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Queue;

import javax.annotation.Resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
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.gmt.so.state.StateManager;
import kr.gmt.so.state.model.SystemState;
import kr.gmtc.gw.comp.thread.CustomThread;
import kr.gmtc.gw.eyegw.config.ServiceConfig;
import kr.gmtc.gw.eyegw.db1.EaiIfCheckService;
import kr.gmtc.gw.eyegw.db1.EaiProcService;
import kr.gmtc.gw.eyegw.satats.IntrfcRecvStatsManager;

@Component("controller")
public class MainController implements ApplicationListener<ContextClosedEvent> {

	// 프레임 워크 구성요소 //
	/** 서비스 설정, {@code application.properties} */
	@Resource(name = "ServiceConfig")
	private ServiceConfig serviceConfig;
	
	private EaiIfCheckService eaiIfCheckService;
	private EaiProcService eaiProcService;

	// 업무처리 //
	/** 업무 처리 스레드 */
	private List<CustomThread> listCustomThreads; 
	
	private CustomThread chkThreadStatus;
	
	@Value("${settings.interval.proc.call}")
	private int procCallInterval;

	@Value("${settings.interval.chk.thread}")
	private int chkThreadInterval;
	
	DateTimeFormatter dfPattern = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
	
	private static final Logger logger = LoggerFactory.getLogger(MainController.class);

	@Autowired
	private SchedulController sch;

	@Autowired
    private StateManager stateMgr;

	private boolean swBeforeStatus = true;

	private IntrfcRecvStatsManager recvStatsMgr;

	/**
	 * 수정 금지<br>
	 * Spring Bean 객체 생성, logger 초기화
	 */
	public MainController(EaiProcService eaiProcService, EaiIfCheckService eaiIfCheckService) {

		this.eaiProcService = eaiProcService;
        this.eaiIfCheckService = eaiIfCheckService;
       
	}

	/**
	 * 실행 (1순위)<br>
	 * 프로그램  초기화
	 */
	@Order(0)
	@EventListener(ApplicationReadyEvent.class)
	public void initialize() {
		try {

			recvStatsMgr = new IntrfcRecvStatsManager();

			// 업무 스래드(프로시저 호출)
			listCustomThreads = new ArrayList<CustomThread>();
			
			listCustomThreads.add(new CustomThread("CallProc_AllFltSchdul", this, procCallInterval, this::CallProc_AllFltSchdul, null, false));
			listCustomThreads.add(new CustomThread("CallProc_Amos", this, procCallInterval, this::CallProc_Amos, null, false));
			listCustomThreads.add(new CustomThread("CallProc_Notam", this, procCallInterval, this::CallProc_Notam, null, false));

			// 이중화 체크 및 업무스래드 체크
			chkThreadStatus = new CustomThread("chkThreadStatus", this, 3000, this::chkThreadStatus, null, false);
						
		} catch (Exception e) {
			logger.error("MainContoller-Thread create fail");
			System.exit(1);
		}
	}

	/**
	 * 실행 (2순위)<br>
	 * 프로그램 실행
	 */
	@Order(1)
	@EventListener(ApplicationReadyEvent.class)
	public void start() {
		try {

			logger.info("MainContoller-Thread start");
			
			for(CustomThread ct : listCustomThreads ){
				ct.start();
			}

			chkThreadStatus.start();

			stateMgr.updateState();

		} catch (Exception e) {
			logger.error("MainContoller-Thread start fail");
			System.exit(1);
		}
	}


	/** 종료 처리 */
	public void stop() {
		
		for(CustomThread ct : listCustomThreads ){
			ct.gracefulStop();
		}

		chkThreadStatus.gracefulStop();
		
	}


	private void chkThreadStatus() throws InterruptedException{

		stateMgr.updateState();

		boolean running = true;
		boolean swNewStatus = stateMgr.isActive();

		// Active 상태로 변경된경우
		if(swNewStatus && !swBeforeStatus){
			logger.info("SW상태 변경됨[Active] 스레드 시작.");

			for(CustomThread ct : listCustomThreads ) {
				
				ct.start();

			}

		// Standby 상태로 변경된경우
		}else if(!swNewStatus && swBeforeStatus){
			logger.info("SW상태 변경됨[Standby] 스레드 중지.");
			
			for(CustomThread ct : listCustomThreads ) {
				ct.gracefulStop();
				ct.interrupt();
			}

		}else {

			// 해당 프로그램이 Active일때 Thread Interrupt 유무 체크
			if(swNewStatus){
				for(CustomThread ct : listCustomThreads ){
					if(ct.isInterrupted()){
						running = false;
						break;
					}
				}

				if(running){

					stateMgr.updateState(SystemState.Normal);

					for(CustomThread ct : listCustomThreads ){
						if(ct.getState() == Thread.State.NEW) ct.start();
					}

				}else{
					stateMgr.updateState(SystemState.Error);
				}

			}
		}

		swBeforeStatus = swNewStatus;
		
	}



	/** 종료 이벤트 (강제종료시 수행 안됨) */
	public void onApplicationEvent(ContextClosedEvent event) {

		stateMgr.updateState(SystemState.Stop);
		for(CustomThread ct : listCustomThreads ) ct.gracefulStop();
		chkThreadStatus.gracefulStop();

		logger.info("====================== SYSTEM STOPED ======================");
	}


	/**
	 * 업무 로직<br>
	 * 자동으로 무한반복 하므로 내부에 while문 필요 없음.<br>
	 * 예외처리를 하지 않을 경우 {@link CustomThread} 내부에서 로그 처리
	 */
	
	 private void CallProc_AllFltSchdul() throws InterruptedException {
		
		// if(!running) return ;

		List<HashMap<String, Object>> dataCnt = eaiIfCheckService.selectAllFltSchdul();

		String sCnt =  String.valueOf(dataCnt.get(0).get("DATA_CNT"));
		
		if( Integer.parseInt(sCnt) > 0 ) {
			
			LocalDateTime dt_before = LocalDateTime.now();
			
			Map<String, Object> paramMap = new HashMap<>();

			paramMap.put("ret_msg", "%");

			eaiProcService.callProcAll(paramMap);
			
			Duration diff = Duration.between(dt_before, LocalDateTime.now());
			logger.info("항공기 운항 스케줄 "  + sCnt + "건 " + diff.toMillis() + "msec");

			// 수신(처리) 건수 집계 -> ELK전송용
			String strRetMsg = (String) paramMap.get("ret_msg");

			if(strRetMsg == null ) return;

			String[] strMsgList = strRetMsg.split("\\^");

			//areaList.forEach(area -> mapTaxiway.put(area.getId(), area));

			if(sch.getMapLogCode() == null ){
				sch.selectCmmnCd();
			}
			
			for(String retMsgRow : strMsgList){

				String[] strMsgFields = retMsgRow.split("\\,");
				String strIntrvcId = strMsgFields[0];
				String strIntrvcMn = sch.getMapLogCode().get(strMsgFields[0]);
				String strIntrvcIfDt = strMsgFields[1];
				String strIntrvcCnt = strMsgFields[2];

				recvStatsMgr.writeStatsLog_KST(strIntrvcIfDt, strIntrvcId, strIntrvcMn, Integer.parseInt(strIntrvcCnt), 0);

			}

		}
		
		// Thread.sleep(3000);
	}
	
	private void CallProc_Amos() throws InterruptedException {
		
		// if(!running) return ;

		List<HashMap<String, Object>> dataCnt = eaiIfCheckService.getAmos();

		String sCnt =  String.valueOf(dataCnt.get(0).get("DATA_CNT"));
		
		if( Integer.parseInt(sCnt) > 0 ) {
			
			LocalDateTime dt_before = LocalDateTime.now();
			
			Map<String, Object> paramMap = new HashMap<>();

			paramMap.put("ar_dt", "%");
			paramMap.put("ret_minDt", "");
			paramMap.put("ret_procCnt", 0);

			eaiProcService.callProcAmos(paramMap);
			
			Duration diff = Duration.between(dt_before, LocalDateTime.now());
			logger.info("활주로 기상정보 "  + sCnt + "건 " + diff.toMillis() + "msec");

			// 수신(처리) 건수 집계 -> ELK전송용
			recvStatsMgr.writeStatsLog_KST((String) paramMap.get("ret_minDt"), "106", "AMOS", (int) paramMap.get("ret_procCnt"), 0);
		}
		
		// Thread.sleep(3000);
	}
	
	private void CallProc_Notam() throws InterruptedException {
		
		// if(!running) return ;

		List<HashMap<String, Object>> dataCnt = eaiIfCheckService.getNotam();

		String sCnt =  String.valueOf(dataCnt.get(0).get("DATA_CNT"));
		
		if( Integer.parseInt(sCnt) > 0 ) {
			
			LocalDateTime dt_before = LocalDateTime.now();
			
			Map<String, Object> paramMap = new HashMap<>();

			paramMap.put("ar_dt", "%");
			paramMap.put("ret_minDt", "");
			paramMap.put("ret_procCnt", 0);

			eaiProcService.callProcNotam(paramMap);
			
			Duration diff = Duration.between(dt_before, LocalDateTime.now());
			logger.info("NOTAM "  + sCnt + "건 " + diff.toMillis() + "msec");

			// 수신(처리) 건수 집계 -> ELK전송용
			recvStatsMgr.writeStatsLog_KST((String) paramMap.get("ret_minDt"), "107", "NOTAM", (int) paramMap.get("ret_procCnt"), 0);
		}
		
		// Thread.sleep(3000);
	}

}
