package kr.gmtc.gw.elastic.controller;

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 java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;

import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Point;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
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 co.elastic.clients.elasticsearch.ml.get_calendars.Calendar;
import kr.gmtc.gw.comp.thread.CustomThread;
import kr.gmtc.gw.elastic.services.AeraService;
import kr.gmtc.gw.elastic.services.ElkSearchService;
import kr.gmtc.gw.elastic.vo.AnalyzeArea.Area;
import kr.gmtc.gw.elastic.vo.ReplayLog.FusionVO;
import kr.gmtc.gw.elastic.vo.ReplayLog.LogMsgParserVO;

@Component("aeraSchduler")
public class AeraSchduler implements ApplicationListener<ContextClosedEvent> {
    
	@Autowired
	private ElkSearchService elkSearchService;

    private AeraService aeraService;

    private List<CustomThread> getDBdataThreads;
    
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private int areaSelectInterval = 10 * (60 * 1000);
	private int analyzeOnAreaInterval = 1 * (60 * 1000);

	private final GeometryFactory gf = new GeometryFactory();

	private Map<String, Area> mapTaxiway = new ConcurrentHashMap<String, Area>();
    
	private Queue<LogMsgParserVO> parserFusionQ = new LinkedBlockingQueue<LogMsgParserVO>();

	private LocalDateTime procBeforeDT;

    public AeraSchduler(AeraService aeraService){
        this.aeraService = aeraService;
    }

    @Order(0)
	@EventListener(ApplicationReadyEvent.class)
	public void initialize() {
		try {

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

			getDBdataThreads.add(new CustomThread("selectAeraList", this, areaSelectInterval, this::selectAeraList, null, false));
			getDBdataThreads.add(new CustomThread("analyzeOnArea", this, analyzeOnAreaInterval, this::analyzeOnArea, 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 : getDBdataThreads ){
				ct.start();
			}

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

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

	private void analyzeOnArea(){

		if(mapTaxiway.size() == 0) selectAeraList();

		if(searchElastic().equals("OK")) {
			
			checkArea();

		}

	}


    private void selectAeraList(){

        // List<HashMap<String, Object>> getAeraList = aeraService.getAeraListMap();

		// Map<String, List<HashMap<String, Object>>> groupedAreas = aeraService.groupAreasByID(getAeraList);

		List<Area> areaList =  aeraService.getAeraList();

		mapTaxiway.clear();
		areaList.forEach(area -> mapTaxiway.put(area.getId(), area));
        mapTaxiway.forEach((id, area) -> area.generateLineToBufferedPolygon(10));

		String asdfa = "asdfadf";
    }

	private String searchElastic(){

		String sIndex   = "3";
		String sStartDt, sEndDt; 

		// TODO : DB table 시각 가져오기, file저장
		if(procBeforeDT == null ){
			procBeforeDT = LocalDateTime.now().minusMinutes(10);
		}

		LocalDateTime currentDT = LocalDateTime.now();
		
		sStartDt = procBeforeDT.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss.SSS"));
		sEndDt = currentDT.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss.SSS"));
		
		procBeforeDT = currentDT;

		String searchRes = elkSearchService.searchFusion(parserFusionQ, sIndex, sStartDt, sEndDt);

		if(searchRes.equals("OK")){
			logger.info("Elastic 데이터 조회 완료");
		}

		return searchRes;

	}

	public void checkArea(){

		
        for(LogMsgParserVO logVO : parserFusionQ  ){

            for(int idx = 0; idx < logVO.getData().size(); idx ++ ){

                List<FusionVO> fList = (List<FusionVO>) logVO.getData();

                for(FusionVO fVO : fList){

                    double dLon = Double.parseDouble(fVO.getLon());
                    double dLat = Double.parseDouble(fVO.getLat());
                    // AeraSchduler as = new AeraSchduler( new AeraService() );
                    List<String> areaList = checkPositionInTaxiway(dLon, dLat);

                    if (!areaList.isEmpty()){


                        
                    }

                }

            }

        }

    }


	public List<String> checkPositionInTaxiway(double lon, double lat) {
		
		List<String> useTwyList = new ArrayList<>();

		Point point = gf.createPoint(new Coordinate(lon, lat));

		try {

			if (point == null) {
    			throw new NullPointerException("Point param is null");
    		}
			
			for (Area area : mapTaxiway.values()) {
				// result = result || area.isContains(point);

				if(area.isContains(point)){
					useTwyList.add(area.getId());
				}

			}
			
		} catch (RuntimeException e) {
			logger.error("[checkPositionInTaxiway] is generate runtime error ", e);
			return useTwyList;
		} catch(Exception e) {
    		logger.error("[checkPositionInTaxiway] is fatal error ", e);
    		return useTwyList;
    	}
		
		return useTwyList;
	}

    @Override
    public void onApplicationEvent(ContextClosedEvent event) {
        // TODO Auto-generated method stub
        throw new UnsupportedOperationException("Unimplemented method 'onApplicationEvent'");
    }







}
