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;

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

	// 프레임 워크 구성요소 //
	/** 서비스 설정, {@code application.properties} */
	@Resource(name = "ServiceConfig")
	private ServiceConfig serviceConfig;
	
	// DB //
	/**
	 * DB1 Connection<br>
	 * application.yml에 db.db1.use가 true로 설정되어야 사용가능<br>
	 * db.db1.use가  false일 경우 주석처리 (Bean 런타임 에러 발생)
	 * @see DB1Connection
	 */
//	@Resource(name = "DB1Connection")
//	private DB1Connection db1Connection;
	
	private EaiIfCheckService eaiIfCheckService;
	private EaiProcService eaiProcService;
	
	// DB //

	
	
	// 업무처리 //
	/** 업무 처리 스레드 */
	private List<CustomThread> listCustomThreads; 
	// private CustomThread callThread_ArtsArr;
	// private CustomThread callThread_ArtsDep;
	// private CustomThread callThread_AcdmArr;
	// private CustomThread callThread_AcdmDep;
	// private CustomThread callThread_Gam;
	// private CustomThread callThread_Amos;
	// private CustomThread callThread_Notam;
	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 StateManager stateMgr;

	private boolean swBeforeStatus = true;

	/** 초기화 실패 시 프로그램 종료 */
// 	private void systemError() {
// //		logger.writeLevelLog("-- FAIL TO STARTUP --", LogLevelType.LOG_ERROR, "AllLog");
// 		System.exit(1);
// 	}

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

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

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

			// 업무 스래드(프로시저 호출)
			listCustomThreads = new ArrayList<CustomThread>();

			listCustomThreads.add(new CustomThread("callThread_Gam", this, procCallInterval, this::callThread_Gam, null, false));
			listCustomThreads.add(new CustomThread("callThread_AcdmArr", this, procCallInterval, this::callThread_AcdmArr, null, false));
			listCustomThreads.add(new CustomThread("callThread_AcdmDep", this, procCallInterval, this::callThread_AcdmDep, null, false));
			listCustomThreads.add(new CustomThread("callThread_ArtsArr", this, procCallInterval, this::callThread_ArtsArr, null, false));
			listCustomThreads.add(new CustomThread("callThread_ArtsDep", this, procCallInterval, this::callThread_ArtsDep, null, false));
			listCustomThreads.add(new CustomThread("callThread_Amos", this, procCallInterval, this::callThread_Amos, null, false));
			listCustomThreads.add(new CustomThread("callThread_Notam", this, procCallInterval, this::callThread_Notam, null, false));

			// 이중화 체크 및 업무스래드 체크
			chkThreadStatus = new CustomThread("chkThreadStatus", this, procCallInterval, 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(){

		stateMgr.updateState();

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

		// 이중화 Active / Standby 상태 변경 체크
		if(swBeforeStatus != swNewStatus){

			if(swNewStatus){
				for(CustomThread ct : listCustomThreads ) ct.start();
			}else{
				for(CustomThread ct : listCustomThreads ) ct.gracefulStop();
			}

		}

		// 해당 프로그램이 Active일때 Thread Interrupt 유무 체크
		if(swNewStatus){

			for(CustomThread ct : listCustomThreads ){
				if(ct.isInterrupted()){
					running = false;
					break;
				}
			}

			if(running){
				stateMgr.updateState(SystemState.Normal);
			}else{
				stateMgr.updateState(SystemState.Error);
			}
		}

		swBeforeStatus = stateMgr.isActive();

		// logger.info("swBeforeStatus : " + swBeforeStatus);
		
	}



	/** 종료 이벤트 (강제종료시 수행 안됨) */
	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 callThread_ArtsArr() throws InterruptedException {
		
		// if(!running) return ;

		List<HashMap<String, Object>> dataCnt = eaiIfCheckService.getArtsArr();
		
		String sCnt =  String.valueOf(dataCnt.get(0).get("DATA_CNT"));
		
		if( Integer.parseInt(sCnt) > 0 ) {
			
			LocalDateTime dt_before = LocalDateTime.now();
			
			eaiProcService.callProcArtsArr();
			
			Duration diff = Duration.between(dt_before, LocalDateTime.now());
			logger.info("callProcArtsArr Call " + sCnt + "건 " + diff.toMillis() + "msec");
		}
		
		// Thread.sleep(3000);

	}
	
	private void callThread_ArtsDep() throws InterruptedException {
		
		// if(!running) return ;

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

		String sCnt =  String.valueOf(dataCnt.get(0).get("DATA_CNT"));
		
		if( Integer.parseInt(sCnt) > 0 ) {
			
			LocalDateTime dt_before = LocalDateTime.now();
			
			eaiProcService.callProcArtsDep();
			
			Duration diff = Duration.between(dt_before, LocalDateTime.now());
			logger.info("callProcArtsDep Call "  + sCnt + "건 " + diff.toMillis() + "msec");
			
		}
		
		// Thread.sleep(3000);
		
	}
	
	private void callThread_AcdmArr() throws InterruptedException {
		
		// if(!running) return ;

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

		String sCnt =  String.valueOf(dataCnt.get(0).get("DATA_CNT"));
		
		if( Integer.parseInt(sCnt) > 0 ) {
			
			LocalDateTime dt_before = LocalDateTime.now();
			
			eaiProcService.callProcAcdmArr();
			
			Duration diff = Duration.between(dt_before, LocalDateTime.now());
			logger.info("callProcAcdmArr Call "  + sCnt + "건 " + diff.toMillis() + "msec");
		}
		
		// Thread.sleep(3000);
	}
	
	private void callThread_AcdmDep() throws InterruptedException {
		
		// if(!running) return ;

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

		String sCnt =  String.valueOf(dataCnt.get(0).get("DATA_CNT"));
		
		if( Integer.parseInt(sCnt) > 0 ) {
			
			LocalDateTime dt_before = LocalDateTime.now();
			
			eaiProcService.callProcAcdmDep();
			
			Duration diff = Duration.between(dt_before, LocalDateTime.now());
			logger.info("callProcAcdmDep Call "  + sCnt + "건 " + diff.toMillis() + "msec");
		}
		
		// Thread.sleep(3000);
	}
	
	private void callThread_Gam() throws InterruptedException {
		
		// if(!running) return ;

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

		String sCnt =  String.valueOf(dataCnt.get(0).get("DATA_CNT"));
		
		if( Integer.parseInt(sCnt) > 0 ) {
			
			LocalDateTime dt_before = LocalDateTime.now();
			
			eaiProcService.callProcGam();
			
			Duration diff = Duration.between(dt_before, LocalDateTime.now());
			logger.info("callProcGam Call "  + sCnt + "건 " + diff.toMillis() + "msec");
		}
		
		// Thread.sleep(3000);
	}
	
	
	private void callThread_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();
			
			eaiProcService.callProcAmos();
			
			Duration diff = Duration.between(dt_before, LocalDateTime.now());
			logger.info("callProcAmos Call "  + sCnt + "건 " + diff.toMillis() + "msec");
		}
		
		// Thread.sleep(3000);
	}
	
	private void callThread_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();
			
			eaiProcService.callProcNotam();
			
			Duration diff = Duration.between(dt_before, LocalDateTime.now());
			logger.info("callProcNotam Call "  + sCnt + "건 " + diff.toMillis() + "msec");
		}
		
		// Thread.sleep(3000);
	}



}
