package com.eactive.eai.batch.scheduler;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Properties;

import com.eactive.eai.batch.common.BatchDirUtil;
import com.eactive.eai.batch.common.CalendarUtil;
import com.eactive.eai.batch.common.CommonKeys;
import com.eactive.eai.batch.common.LogUtil;
import com.eactive.eai.batch.doc.BatchDoc;
import com.eactive.eai.batch.eventscheduler.holiday.HolidayManager;
import com.eactive.eai.batch.flowController.DemandTargetVO;
import com.eactive.eai.batch.flowController.FlowControllerManager;
import com.eactive.eai.batch.message.EAIBatchMsgManager;
import com.eactive.eai.batch.rule.dirInfo.BatchJobInfoVO;
import com.eactive.eai.batch.rule.dirInfo.BatchJobPriorityManager;
import com.eactive.eai.batch.rule.dirInfo.BatchJobPriorityVO;
import com.eactive.eai.batch.rule.dirInfo.DirInfoManager;
import com.eactive.eai.common.dao.DAOFactory;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.util.DatetimeUtil;
import com.eactive.eai.common.util.Logger;


/**
* 1. 기능 :  Manager 클래스
* 2. 처리 개요 : 스케줄 내용은 메모리에 로딩하지 않는다.
* 3. 주의사항
*
* @author  :
* @version : v 1.0.0
* @see : 관련 기능을 참조
* @since   :
*/
public class SchedulerMessageManager
{

	//디버그용 로그유틸
	private static Logger logger = Logger.getLogger(Logger.LOGGER_SCHEDULER);
	
	//singleton 객체 생성
	private static SchedulerMessageManager instance = new SchedulerMessageManager();
	//singleton 객체 리턴
    public static SchedulerMessageManager getInstance() {
        return instance;
    }

	//생성자 (다른 클래스에서 new 불가)
	private SchedulerMessageManager() {}




    public SchedulerMessageVO getScheduleInfo(String bizCode, String processCode, String osidinstidstCode) throws Exception
    {
		SchedulerMessageDAO dao = (SchedulerMessageDAO)DAOFactory.newInstance().create(SchedulerMessageDAO.class);
        return dao.getScheduleInfo(bizCode, processCode, osidinstidstCode);
    }

    public SchedulerMessageVO getRecvScheduleInfo(String bizCode, String processCode, String osidinstidstCode) throws Exception
    {
		SchedulerMessageDAO dao = (SchedulerMessageDAO)DAOFactory.newInstance().create(SchedulerMessageDAO.class);
        return dao.getRecvScheduleInfo(bizCode, processCode, osidinstidstCode);
    }

    public int insertJobToQueue(SchedulerMessageVO info) throws Exception
    {
		SchedulerMessageDAO dao = (SchedulerMessageDAO)DAOFactory.newInstance().create(SchedulerMessageDAO.class);
        return dao.insertJobToQueue(info);
    }

    public int deleteJobFromProcessing(String uuid, String subUUID) throws Exception
    {
		SchedulerMessageDAO dao = (SchedulerMessageDAO)DAOFactory.newInstance().create(SchedulerMessageDAO.class);
        return dao.deleteJobFromProcessing(uuid, subUUID);
    }

    public int deleteJobFromProcessing() throws Exception
    {
		SchedulerMessageDAO dao = (SchedulerMessageDAO)DAOFactory.newInstance().create(SchedulerMessageDAO.class);
        return dao.deleteJobFromProcessing();
    }

    public synchronized ArrayList<SchedulerMessageVO> getExecutableJobFromQueue() throws Exception
	{
		SchedulerMessageDAO dao = (SchedulerMessageDAO)DAOFactory.newInstance().create(SchedulerMessageDAO.class);
		ArrayList<SchedulerMessageVO> executeList = new ArrayList<SchedulerMessageVO>();


		/*
		if ( isInProcess() ){
			long interval = System.currentTimeMillis() - lastProcess;
			if ( interval > ( 60 * 1000) ){
				setInProcess(false);
			}else{
				return executeList;
			}
		}
		*/


		//1. Job_Processing 테이블에서 모든 연결정보ID 조회
		HashSet<String> hSet = dao.getExecutingSysConnList();
		//logger.info("[스케쥴 핸들러]  - 작업 진행목록의 진행중 작업 건수: "+  hSet.size());

		//2. Job_Queue 테이블에서 수행 할 작업 (현재시간에 걸려있는 작업) 목록 조회 (순서: 우선순위 > 파일생성일 > 스케쥴시작시간)
		ArrayList<SchedulerMessageVO> aList = dao.getExecutableJobList();
		//logger.info("[스케쥴 핸들러]  - 작업 대기목록의 수행대상 작업 건수: "+  aList.size());
		if (aList.size() == 0) return aList;


		//------------------------------------------------------------------------------------------------
		// 2번 에서 조회한 모든 작업을 수행하는 것이 아니라 1번에서 사용중인 망상태를 체크하여 수행 가능한 작업만을 걸러준다.
		//------------------------------------------------------------------------------------------------
		int prePriority = -1;
		String preProcessCode = "";
		//3. Job_Queue 테이블에서 조회한 수행 할 각 작업마다 수행가능 조건 체크
		for (int i=0; i<aList.size(); i++) {
			SchedulerMessageVO info = (SchedulerMessageVO)aList.get(i);
			//logger.info("[스케쥴 핸들러] * 대기작업정보 ["+ (i+1) +"]: "+ info.getUUID() +" | "+ info.getProcessCode() +" | "+ info.getInstitutionCode() +" | "+ info.getFilePath() +" | "+ info.getFileName());
			
			
			//3-0. 휴일 처리 타입에 따라 
			String holyProcessGb = info.getHolyProcessGb();
			if ( holyProcessGb.equals("1")){ // 영업일이 아니면 처리하지 않음
				if ( HolidayManager.getInstance().isAllHoliday(DatetimeUtil.getCurrentDate())){
                    logger.info("[스케쥴 핸들러]    ★★★ 영업일이 아니므로 처리 하지 않습니다.(토요일도 처리 안함) ("+ info.getProcessCode() +" | "+ info.getInstitutionCode()+" | "+ info.getScheduleCode() +") ★★★");
                    continue;
				}
                //logger.debug("[스케쥴 핸들러]    ★★★ 오늘은 영업일.(토요일도 처리 안함) ("+ info.getProcessCode() +" | "+ info.getInstitutionCode() +" | "+ info.getScheduleCode()+") ★★★");
			}else if ( holyProcessGb.equals("2")){ // 영업일이 아니어도 토요일이 공휴일이 아닌경우에 처리함
				if ( HolidayManager.getInstance().isHoliday(DatetimeUtil.getCurrentDate())){
                    logger.info("[스케쥴 핸들러]    ★★★ 영업일이 아니므로 처리 하지 않습니다.(토요일은 처리함) ("+ info.getProcessCode() +" | "+ info.getInstitutionCode() +" | "+ info.getScheduleCode()+") ★★★");
                    continue;
				}
                //logger.debug("[스케쥴 핸들러]    ★★★ 오늘은 영업일.(토요일은 처리함) ("+ info.getProcessCode() +" | "+ info.getInstitutionCode() +" | "+ info.getScheduleCode()+") ★★★");
			}
			
//2009.8.13 우선순위 작업이 설정된  업무에 우선순위가 다른 작업이 동시에 들어오면 한번의 타이머 이벤트에 같은 우선순위의 작업만 진행함.
//start - by kscheon			
			//BATCH작업구분코드 (processCode)에 따른 프로퍼티 정보 가져오기
			Properties telegramInfo= null;
			String isPriority = "";
			String[] priorityList = null;
			try{
		        String propName = "TelegramInfo{"+info.getProcessCode()
		        					+"_"+info.getBizCode()+"}";
		        
		        PropManager pmanager = PropManager.getInstance();
		        telegramInfo= pmanager.getProperties(propName);
		        
		        
		        if (telegramInfo != null ){
		        	isPriority = telegramInfo.getProperty("priority");
		        }
		        BatchJobPriorityManager jobpmanager = BatchJobPriorityManager.getInstance();
	        	
		        try{
	        
			        //우선순위가 설정된 업무인 경우에는 같은 우선순위의 작업만 한번에 socket을 할당 할 수 있도록 한다.
			        if (isPriority != null && "Y".equals(isPriority)){
	
			        	
			        	String plist = telegramInfo.getProperty("priority.list");
			        	if (plist != null && plist.length() >0){//등록된 우선순위 목록이 있으면..
				        	
			        		priorityList = plist.split(",");
			        	}
			        	
			        	if (priorityList != null && priorityList.length >0){
	
				        	ArrayList<BatchJobPriorityVO> palist = jobpmanager.getPriorityData(priorityList[0]);
				        	
				        	if (palist == null || palist.size() <= 0){
				        		palist = new ArrayList<BatchJobPriorityVO>();
				        		
				        		for (int plistIndex = 0; plistIndex < priorityList.length; plistIndex++){
				        			BatchJobInfoVO bJobInfo = DirInfoManager.getInstance().getFileInfoByDstcd(info.getProcessCode(), priorityList[plistIndex]);
				        			
				        			if (bJobInfo == null){//우선순위 목록에 있는 작업구분이 등록된 Job이 아니면.. 에러를 띄우고 계속 작업을 진행한다.
				        				logger.error("[스케쥴 핸들러]    * 대기작업정보 ["+ (i+1) +"]: "+ info.getUUID() +" | "+ info.getProcessCode() +" | "+ info.getInstitutionCode() +" | "+ info.getFilePath() +" | "+ info.getFileName() +"  ==> 우선순위 목록에 등록된 배치작업 "+ priorityList[plistIndex]+" 이 BJ01에 등록되어 있지 않습니다. 해당 룰을 확인하세요");
				        			}else {
					        			BatchJobPriorityVO jpvo = new BatchJobPriorityVO();
					        			
					        			//기본정보를 설정한다.
					        			jpvo.setBizCode(priorityList[plistIndex]);
					        			jpvo.setOsdCd(info.getInstitutionCode());
					        			jpvo.setOsdName(info.getInstitutionName());
					        			jpvo.setProcessCd(info.getProcessCode());
					        			jpvo.setProcessName(info.getProcessName());
	
					        			palist.add(plistIndex, jpvo);
				        			}			        			
				        		}
	
					        		
				        		jobpmanager.addJobPriorityData(priorityList[0], palist);
				        	}
			        	
				        	boolean isresult = jobpmanager.isExecutePrePriority(priorityList[0], info.getBizCode());
				        	
				        	if (!isresult){//등록된 우선순위 작업 목록중 선행 작업이 실행되지 않았으면..
				        		logger.info("[스케쥴 핸들러]    * 대기작업정보 ["+ (i+1) +"]: "+ info.getUUID() +" | "+ info.getProcessCode() +" | "+ info.getInstitutionCode() +" | "+ info.getFilePath() +" | "+ info.getFileName() +"  ==> 해당 업무의 선행  우선순위 작업이 수행되지 않아,  다음 Timer Event 시 다시 확인합니다.");
				        		continue;
				        	}
			        	}
			        	
						int    priority = info.getPriority();
						String processCode = info.getProcessCode();
		
						if (prePriority  < 0){
							prePriority    = priority;
							preProcessCode = processCode;
						}
//						int prePriority = Integer.parseInt(preStrPriority);
//						int priority = Integer.parseInt(strpriority);
						if (preProcessCode.equals(processCode)){
							if (prePriority != priority){
								logger.info("[스케쥴 핸들러]    * 대기작업정보 ["+ (i+1) +"]: "+ info.getUUID() +" | "+ info.getProcessCode() +" | "+ info.getInstitutionCode() +" | "+ info.getFilePath() +" | "+ info.getFileName() +"  ==> 해당 업무의 선행  우선순위 작업이 먼저 수행합니다.");
								continue;
							}
							
							ArrayList<SchedulerMessageVO> exelist = dao.getExecutJobList(info.getProcessCode());
							
							if (exelist != null && exelist.size() > 0){//현재 실행중인 작업이 있으면.. 
								SchedulerMessageVO emessage = (SchedulerMessageVO)exelist.get(0);//첫번째 우선순위 작업을 가져온다..
								int epo = 0;
								try{
									epo = emessage.getPriority();
								}catch (Exception ex){
									logger.error("스케쥴 핸들러]    "+info.getProcessName() +" 업무의 현재  작업진행큐의 우선순위를 조회하는데 에러가 발생했습니다. - ["+epo+"]"+ex.getMessage());
								}
								if (epo < priority){
									logger.info("[스케쥴 핸들러]    * 대기작업정보 ["+ (i+1) +"]: "+ info.getUUID() +" | "+ info.getProcessCode() +" | "+ info.getInstitutionCode() +" | "+ info.getFilePath() +" | "+ info.getFileName() +"  ==> 해당 업무의 선행  우선순위 작업이 실행큐에서 처리중입니다. 다음 Timer Event 시 다시 확인합니다. ");
									continue;
								}
							}
	
	
							if (priorityList != null && priorityList.length >0){//우선순위 목록이 등록되어 있으면 실행목록 변경.
								
								String dataCountFlag = telegramInfo.getProperty("file.count.flag");
								if(dataCountFlag != null && dataCountFlag.length() > 0 && "Y".equals(dataCountFlag)){//flag가 설정되어 있으면..
	
									String fileDir = info.getFilePath();
						            String fileNm  = info.getRenamedFileName();
	
						            if (fileDir.charAt(fileDir.length()-1) != File.separatorChar) {
						                fileDir = fileDir + File.separator;
						            }
	
						            String fileName = fileDir + fileNm;
						            File file = new File(fileName);
						            if (!file.exists()) // 파일이 없으면 리턴 False
						            {
						                logger.error("스케쥴 핸들러]   데이터 파일 갯수를 확인하기 위해 파일 생성시 오류가 발생했습니다. ["+fileName+"] - 이후 작업은 에러를 무시하고 진행됩니다.");
						            }else {
						            	String dataCountPos = telegramInfo.getProperty("count.pos");
						            	String dataCountLen = telegramInfo.getProperty("count.length");
						            	String dataBizCode  = telegramInfo.getProperty("file.count.bizcode");
						            	
						            	BatchJobInfoVO bJobInfo = DirInfoManager.getInstance().getFileInfoByDstcd(info.getProcessCode(), dataBizCode);
					        			
					        			if (bJobInfo == null){//데이터 파일로 등록된 작업구분이  작업구분이 등록된 Job이 아니면.. 에러를 띄우고 계속 작업을 진행한다.
					        				logger.error("[스케쥴 핸들러]    * 대기작업정보 ["+ (i+1) +"]: "+ info.getUUID() +" | "+ info.getProcessCode() +" | "+ info.getInstitutionCode() +" | "+ info.getFilePath() +" | "+ info.getFileName() +"  ==> 우선순위 목록에 등록된 배치작업 "+dataBizCode+" 이 BJ01에 등록되어 있지 않습니다. 해당 룰을 확인하세요");
							        	}else {
							        		BatchJobPriorityVO ldatavo = jobpmanager.getPriorityDatabyBizCode(priorityList[0], dataBizCode);
											int executeDataCount = 0;
											
											if (ldatavo != null){
												executeDataCount = ldatavo.getDataCount();
											}else {// 20250910 npe 경고 아래 추가
												logger.error("[스케쥴 핸들러] getPriorityDatabyBizCode ERROR ");
												throw new Exception("[스케쥴 핸들러] getPriorityDatabyBizCode ERROR ");
											}
							            	
							            	int countPos = Integer.parseInt(dataCountPos);
							            	int countLen = Integer.parseInt(dataCountLen);
							            	byte[] countBuffer = new byte[countLen];
	
											BufferedInputStream bis = null; 
											try {
												bis = new BufferedInputStream (new FileInputStream(file));
												bis.skip(countPos);
												bis.read(countBuffer);
											} catch (Exception e) {
												logger.error(e.getMessage(), e);
											}finally{
												if ( bis != null )
													bis.close();
											}
	
								            int countInfo = Integer.parseInt(new String(countBuffer));
								            //파일 수 확인을 위해 정보 저장
								            ldatavo.setDataCountInfo(countInfo);// 20250910 npe 경고
								            
	
								            if (executeDataCount < countInfo){
								            	logger.info("[스케쥴 핸들러]    * 대기작업정보 ["+ (i+1) +"]: "+ info.getUUID() +" | "+ info.getProcessCode() +" | "+ info.getInstitutionCode() +" | "+" | "+ info.getFileName() +" | 전송되어야할 데이터파일 수 ["+ countInfo +"]보다  전송된 파일수  [ "+ executeDataCount+"]가  작아서  다음 Timer Event 시 다시 확인합니다. ");
								            	continue;			//실행된 데이터 파일이 모두 전송 안되었으면...
								            }
					        			}
									}
								}
								
								
							}
							String delay = telegramInfo.getProperty("delaysec");
							if (delay != null && delay.length() > 0){
								int delaysec = Integer.parseInt(delay);
								
								if (delaysec > 0){
									if (priorityList != null && priorityList.length >0){//우선순위 플래그가 Y이면서 우선순위 목록이 있으면..

										BatchJobPriorityVO lastvo = jobpmanager.getPriorityDatabyBizCode(priorityList[0], info.getBizCode());
									
										if (lastvo != null){
											long lastcall = lastvo.getFirstCallForDelayTime();
											if (lastcall == 0){
												lastvo.setFirstCallForDelayTime(System.currentTimeMillis()); //우선순위 작업이 처음 호출된 시간을 저장한다.
												continue;
											}else{
												int time = (int)((System.currentTimeMillis() - lastcall)/1000);
												if (delaysec > time){//설정된 지연시간보다 첫 호출 후 경과시간이 작으면 작업을 진행하지 않는다.
													logger.info("[스케쥴 핸들러]    * 대기작업정보 ["+ (i+1) +"]: "+ info.getUUID() +" | "+ info.getProcessCode() +" | "+ info.getInstitutionCode() +" | "+" | "+ info.getFileName() +" | "+ delaysec +"보다  경과시간  "+ time+"이 작아서  다음 Timer Event 시 다시 확인합니다. ");
													continue;
												}else {//다음 지연시간 설정을 위해 타임스탬프 초기화..
													lastvo.setFirstCallForDelayTime(System.currentTimeMillis()); //우선순위 작업이 처음 호출된 시간을 저장한다.
												}
											}
										}
									}
									else{//우선순위 플래그가 Y이면서 우선순위 목록이 없으면..
										if (jobpmanager != null){
											
											if (jobpmanager.hasDelaySec(info.getUUID())){
												long lastcall = jobpmanager.getDelaySec(info.getUUID());
												
												if (lastcall >= 0){
													
													int time = (int)((System.currentTimeMillis() - lastcall)/1000);
													if (delaysec > time){//설정된 지연시간보다 첫 호출 후 경과시간이 작으면 작업을 진행하지 않는다.
														logger.info("[스케쥴 핸들러]    * 대기작업정보  Set DelaySec ["+ (i+1) +"]: "+ info.getUUID() +" | "+ info.getProcessCode() +" | "+ info.getInstitutionCode() +" | "+" | "+ info.getFileName() +" | "+ delaysec +"보다  경과시간  "+ time+"이 작아서  다음 Timer Event 시 다시 확인합니다. ");
														continue;
													}else {//다음 지연시간 설정을 위해 타임스탬프 초기화..
														jobpmanager.removeDelaySec(info.getUUID());
													}
		
												}
											}else {//초기화..
												jobpmanager.setDelaySec(info.getUUID(), System.currentTimeMillis()); 
												continue;
											}
										}

									}
								}
							}
						}else{//업무구분이 달라지면 이전 우선순위값 초기화
							prePriority    = info.getPriority();
							preProcessCode = info.getProcessCode();
						}
					}else { //우선순위가 없는 작업중 delay time이 설정된 작업 처리..
						if (telegramInfo != null ){
							String delay = telegramInfo.getProperty("delaysec");
							if (delay != null && delay.length() > 0){
								int delaysec = Integer.parseInt(delay);
								
								if (delaysec > 0){
									
									if (jobpmanager != null){
										
										if (jobpmanager.hasDelaySec(info.getUUID())){
											long lastcall = jobpmanager.getDelaySec(info.getUUID());
											
											if (lastcall > 0){
												
												int time = (int)((System.currentTimeMillis() - lastcall)/1000);
												if (delaysec > time){//설정된 지연시간보다 첫 호출 후 경과시간이 작으면 작업을 진행하지 않는다.
													logger.info("[스케쥴 핸들러]    * 대기작업정보  Set DelaySec ["+ (i+1) +"]: "+ info.getUUID() +" | "+ info.getProcessCode() +" | "+ info.getInstitutionCode() +" | "+" | "+ info.getFileName() +" | "+ delaysec +"보다  경과시간  "+ time+"이 작아서  다음 Timer Event 시 다시 확인합니다. ");
													continue;
												}else {//다음 지연시간 설정을 위해 타임스탬프 초기화..
													jobpmanager.removeDelaySec(info.getUUID());
												}
	
											}
										}else {//초기화..
											jobpmanager.setDelaySec(info.getUUID(), System.currentTimeMillis()); 
											continue;
										}
									}
								}
							}
						}
						
					}
		        }catch (Exception pex){
		        	logger.error("[스케쥴 핸들러]    * 대기작업정보 ["+ (i+1) +"]: "+ info.getUUID() +" | "+ info.getProcessCode() +" | "+ info.getInstitutionCode() +" | "+ info.getFileName() +" 우선순위 작업 처리시 에러가 발생했습니다. 이후 작업은 무시하고 진행됩니다. - "+pex.getMessage());
		        }
			}catch (Exception e){
			}
//	      2009.8.13 우선순위 작업이 설정된  업무에 우선순위가 다른 작업이 동시에 들어오면 한번의 타이머 이벤트에 같은 우선순위의 작업만 진행함.
//	      end - by kscheon		        
			
            //logger.debug("[스케쥴 핸들러]    ★★★ 영업일 체크 안함 ("+ info.getProcessCode() +" | "+ info.getInstitutionCode() +" | "+ info.getScheduleCode()+") ★★★");
			//3-1. 해당 작업의 대외기관 연결정보 조회
			DemandTargetVO[] sysConnList = FlowControllerManager.getInstance().getRemoteHostInfo(info.getProcessCode(), info.getInstitutionCode());
			if (sysConnList == null || sysConnList.length == 0) {
				//logger.info("[스케쥴 핸들러]    * 대기작업정보 ["+ (i+1) +"]: "+ info.getUUID() +" | "+ info.getProcessCode() +" | "+ info.getInstitutionCode() +" | "+ info.getFilePath() +" | "+ info.getFileName() +"  ==> 해당 작업의 요구송수신 연결정보가 존재하지 않음 !! ★★ 연결정보 체크 필요 ★★");
				continue;
			}

			//3-2. 사용하지 않는 대외기관 연결정보ID 할당
			int j=0;
			for (j=0; j<sysConnList.length; j++) {
                //대외기관 연결상태가 장애이면 다음 연결정보 조회
				if (sysConnList[j].getOrganSocketStatus().equals(DemandTargetVO.ORGAN_SOCKET_STATUS_ERROR)) {
                    logger.info("[스케쥴 핸들러]    ★★★ 대외기관 연결정보가 '장애' 상태입니다. ("+ info.getProcessCode() +" | "+ info.getInstitutionCode() +" | "+ sysConnList[j].getSystemConnCode() +" | "+ sysConnList[j].getIpAddress() +" | "+ sysConnList[j].getPortNumber() +") ★★★");
                    continue;
                }

				//해당 연결정보가 사용중이지 않으면 해당 연결정보 할당
				if (!hSet.contains(sysConnList[j].getSystemConnCode())) {
					info.setSystemConnCode  (sysConnList[j].getSystemConnCode());
					info.setRempoteIP       (sysConnList[j].getIpAddress());
					info.setRemotePort      (sysConnList[j].getPortNumber());
					info.setRemoteUserID    (sysConnList[j].getSocketID());
					info.setRemoteUserPW    (sysConnList[j].getSocketPwd());
					info.setTelegramTimeout (sysConnList[j].getTimeoutInterval());

					hSet.add(sysConnList[j].getSystemConnCode());  //설정한 열결ID를 사용중목록에 추가
					break;
				}
				//logger.info("[스케쥴 핸들러]   ..."+ (j+1) +" 번째 연결정보 사용중 ("+ sysConnList[j].getSystemConnCode() +" | "+ sysConnList[j].getIpAddress() +" | "+ sysConnList[j].getPortNumber() +" | "+ sysConnList[j].getTimeoutInterval() +")");
			}
			if (info.getSystemConnCode().trim().equals("")) {
				logger.info("[스케쥴 핸들러]    * 대기작업정보 ["+ (i+1) +"]: "+ info.getUUID() +" | "+ info.getProcessCode() +" | "+ info.getInstitutionCode() +" | "+ info.getFilePath() +" | "+ info.getFileName() +"  ==> 해당 작업의 연결정보가 모두 사용중 임 !! -> 다음 Timer Event 시 연결정보 Release 된 경우 수행됨.");
				continue;
			}
			logger.info("[스케쥴 핸들러]    * 대기작업정보 ["+ (i+1) +"]: "+ info.getUUID() +" | "+ info.getProcessCode() +" | "+ info.getInstitutionCode() +" | "+ info.getFilePath() +" | "+ info.getFileName() +"  ==> 사용 가능한 연결정보 설정됨 !! ("+ sysConnList[j].getSystemConnCode() +" | "+ sysConnList[j].getIpAddress() +" | "+ sysConnList[j].getPortNumber() +" | "+ sysConnList[j].getTimeoutInterval() +")");
			executeList.add(info);
			
			//setInProcess(true);

		}

		return executeList;
	}

    public int moveJobQueueToHandle(SchedulerMessageVO info) throws Exception
    {
		SchedulerMessageDAO dao = (SchedulerMessageDAO)DAOFactory.newInstance().create(SchedulerMessageDAO.class);
		ArrayList<SchedulerMessageVO> alist = new ArrayList<SchedulerMessageVO>();
		alist.add(info);
        return dao.moveExecutableJodToProcessing(alist);
    }

    public int moveJobQueueToHandleWithNewID(SchedulerMessageVO info, String strUUID, String strSubUUID) throws Exception
    {
		SchedulerMessageDAO dao = (SchedulerMessageDAO)DAOFactory.newInstance().create(SchedulerMessageDAO.class);
        return dao.moveExecutableJodToProcessingWithNewID(info,strUUID,strSubUUID);
    }

	public ArrayList<SchedulerMessageVO> getExecutableRecvScheduleList() throws Exception
	{
		SchedulerMessageDAO dao = (SchedulerMessageDAO)DAOFactory.newInstance().create(SchedulerMessageDAO.class);
		return dao.getExecutableRecvScheduleList();
	}

	public SchedulerMessageVO getExecutableJobFromQueue( String processCode, String institutionCode) throws Exception
	{
		SchedulerMessageDAO dao = (SchedulerMessageDAO)DAOFactory.newInstance().create(SchedulerMessageDAO.class);

		// Job_Queue 테이블에서 수행 할 작업 (현재시간에 걸려있는 작업) 목록 조회 (순서: 우선순위 > 파일생성일 > 스케쥴시작시간)
		ArrayList<SchedulerMessageVO> aList = dao.getExecutableJobList();
		//logger.info("[스케쥴 핸들러]A  - 작업 대기목록의 수행대상 작업 건수: "+  aList.size());
		if (aList.size() == 0) return null;

		for (int i=0; i<aList.size(); i++) {
			SchedulerMessageVO info = (SchedulerMessageVO)aList.get(i);

			if (    processCode.equals(info.getProcessCode()) 
					 && institutionCode.equals(info.getInstitutionCode())
			   ) {				//휴일 처리 타입에 따라 
				String holyProcessGb = info.getHolyProcessGb();
				if ( holyProcessGb.equals("1")){ // 영업일이 아니면 처리하지 않음
					if ( HolidayManager.getInstance().isAllHoliday(DatetimeUtil.getCurrentDate())){
	                    logger.info("[스케쥴 핸들러]    ★★★ 영업일이 아니므로 처리 하지 않습니다.(토요일도 처리 안함) ("+ info.getProcessCode() +" | "+ info.getInstitutionCode()+" | "+ info.getScheduleCode() +") ★★★");
	                    continue;
					}
	                logger.debug("[스케쥴 핸들러]    ★★★ 오늘은 영업일.(토요일도 처리 안함) ("+ info.getProcessCode() +" | "+ info.getInstitutionCode() +" | "+ info.getScheduleCode()+") ★★★");
				}else if ( holyProcessGb.equals("2")){ // 영업일이 아니어도 토요일이 공휴일이 아닌경우에 처리함
					if ( HolidayManager.getInstance().isHoliday(DatetimeUtil.getCurrentDate())){
	                    logger.info("[스케쥴 핸들러]    ★★★ 영업일이 아니므로 처리 하지 않습니다.(토요일은 처리함) ("+ info.getProcessCode() +" | "+ info.getInstitutionCode() +" | "+ info.getScheduleCode()+") ★★★");
	                    continue;
					}
	                //logger.debug("[스케쥴 핸들러]    ★★★ 오늘은 영업일.(토요일은 처리함) ("+ info.getProcessCode() +" | "+ info.getInstitutionCode() +" | "+ info.getScheduleCode()+") ★★★");
				}else{
		            //logger.debug("[스케쥴 핸들러]    ★★★ 영업일 체크 안함 ("+ info.getProcessCode() +" | "+ info.getInstitutionCode() +" | "+ info.getScheduleCode()+") ★★★");
				}
				
				return info;
			}
		}

		return null;
	}
	
	public SchedulerMessageVO getSendJobFromQueue( String processCode, String institutionCode) throws Exception
	{
		SchedulerMessageDAO dao = (SchedulerMessageDAO)DAOFactory.newInstance().create(SchedulerMessageDAO.class);

		// Job_Queue 테이블에서 수행 할 작업 (현재시간에 걸려있는 작업) 목록 조회 (순서: 우선순위 > 파일생성일 > 스케쥴시작시간)
		ArrayList<SchedulerMessageVO> aList = dao.getExecutableJobListWithSuspendedJob();
		//logger.info("[스케쥴 핸들러]A  - 작업 대기목록의 수행대상 작업 건수: "+  aList.size());
		if (aList.size() == 0) return null;

		for (int i=0; i<aList.size(); i++) {
			SchedulerMessageVO info = (SchedulerMessageVO)aList.get(i);

			if (    processCode.equals(info.getProcessCode()) 
					 && institutionCode.equals(info.getInstitutionCode())
					 && info.getProcessType().equals("RS")
			   ) {				//휴일 처리 타입에 따라 
				String holyProcessGb = info.getHolyProcessGb();
				if ( holyProcessGb.equals("1")){ // 영업일이 아니면 처리하지 않음
					if ( HolidayManager.getInstance().isAllHoliday(DatetimeUtil.getCurrentDate())){
	                    logger.info("[스케쥴 핸들러]    ★★★ 영업일이 아니므로 처리 하지 않습니다.(토요일도 처리 안함) ("+ info.getProcessCode() +" | "+ info.getInstitutionCode()+" | "+ info.getScheduleCode() +") ★★★");
	                    continue;
					}
	                logger.debug("[스케쥴 핸들러]    ★★★ 오늘은 영업일.(토요일도 처리 안함) ("+ info.getProcessCode() +" | "+ info.getInstitutionCode() +" | "+ info.getScheduleCode()+") ★★★");
				}else if ( holyProcessGb.equals("2")){ // 영업일이 아니어도 토요일이 공휴일이 아닌경우에 처리함
					if ( HolidayManager.getInstance().isHoliday(DatetimeUtil.getCurrentDate())){
	                    logger.info("[스케쥴 핸들러]    ★★★ 영업일이 아니므로 처리 하지 않습니다.(토요일은 처리함) ("+ info.getProcessCode() +" | "+ info.getInstitutionCode() +" | "+ info.getScheduleCode()+") ★★★");
	                    continue;
					}
	                //logger.debug("[스케쥴 핸들러]    ★★★ 오늘은 영업일.(토요일은 처리함) ("+ info.getProcessCode() +" | "+ info.getInstitutionCode() +" | "+ info.getScheduleCode()+") ★★★");
				}else{
		            //logger.debug("[스케쥴 핸들러]    ★★★ 영업일 체크 안함 ("+ info.getProcessCode() +" | "+ info.getInstitutionCode() +" | "+ info.getScheduleCode()+") ★★★");
				}
				
				return info;
			}
		}

		return null;
	}
	
	public SchedulerMessageVO getSendJobFromQueue( String processCode, String institutionCode, String fileName) throws Exception
	{
		SchedulerMessageDAO dao = (SchedulerMessageDAO)DAOFactory.newInstance().create(SchedulerMessageDAO.class);

		// Job_Queue 테이블에서 수행 할 작업 (현재시간에 걸려있는 작업) 목록 조회 (순서: 우선순위 > 파일생성일 > 스케쥴시작시간)
		ArrayList<SchedulerMessageVO> aList = dao.getExecutableJobListWithSuspendedJob();
		//logger.info("[스케쥴 핸들러]A  - 작업 대기목록의 수행대상 작업 건수: "+  aList.size());
		if (aList.size() == 0) return null;

		for (int i=0; i<aList.size(); i++) {
			SchedulerMessageVO info = (SchedulerMessageVO)aList.get(i);

			if (    processCode.equals(info.getProcessCode()) 
					 && institutionCode.equals(info.getInstitutionCode())
					 && info.getProcessType().equals("RS")
			   ) {				//휴일 처리 타입에 따라 
				String holyProcessGb = info.getHolyProcessGb();
				if ( holyProcessGb.equals("1")){ // 영업일이 아니면 처리하지 않음
					if ( HolidayManager.getInstance().isAllHoliday(DatetimeUtil.getCurrentDate())){
	                    logger.info("[스케쥴 핸들러]    ★★★ 영업일이 아니므로 처리 하지 않습니다.(토요일도 처리 안함) ("+ info.getProcessCode() +" | "+ info.getInstitutionCode()+" | "+ info.getScheduleCode() +") ★★★");
	                    continue;
					}
	                logger.debug("[스케쥴 핸들러]    ★★★ 오늘은 영업일.(토요일도 처리 안함) ("+ info.getProcessCode() +" | "+ info.getInstitutionCode() +" | "+ info.getScheduleCode()+") ★★★");
				}else if ( holyProcessGb.equals("2")){ // 영업일이 아니어도 토요일이 공휴일이 아닌경우에 처리함
					if ( HolidayManager.getInstance().isHoliday(DatetimeUtil.getCurrentDate())){
	                    logger.info("[스케쥴 핸들러]    ★★★ 영업일이 아니므로 처리 하지 않습니다.(토요일은 처리함) ("+ info.getProcessCode() +" | "+ info.getInstitutionCode() +" | "+ info.getScheduleCode()+") ★★★");
	                    continue;
					}
	                //logger.debug("[스케쥴 핸들러]    ★★★ 오늘은 영업일.(토요일은 처리함) ("+ info.getProcessCode() +" | "+ info.getInstitutionCode() +" | "+ info.getScheduleCode()+") ★★★");
				}else{
		            //logger.debug("[스케쥴 핸들러]    ★★★ 영업일 체크 안함 ("+ info.getProcessCode() +" | "+ info.getInstitutionCode() +" | "+ info.getScheduleCode()+") ★★★");
				}
				if ( info.getFileName().startsWith(fileName)){
					return info;
				}
			}
		}

		return null;
	}

	public SchedulerMessageVO getRecvJobFromQueue( String processCode, String institutionCode) throws Exception
	{
		SchedulerMessageDAO dao = (SchedulerMessageDAO)DAOFactory.newInstance().create(SchedulerMessageDAO.class);

		// Job_Queue 테이블에서 수행 할 작업 (현재시간에 걸려있는 작업) 목록 조회 (순서: 우선순위 > 파일생성일 > 스케쥴시작시간)
		ArrayList<SchedulerMessageVO> aList = dao.getExecutableJobListWithSuspendedJob();
		//logger.info("[스케쥴 핸들러]A  - 작업 대기목록의 수행대상 작업 건수: "+  aList.size());
		if (aList.size() == 0) return null;

		for (int i=0; i<aList.size(); i++) {
			SchedulerMessageVO info = (SchedulerMessageVO)aList.get(i);

			if (    processCode.equals(info.getProcessCode()) 
					 && institutionCode.equals(info.getInstitutionCode())
					 && info.getProcessType().equals("RR")
			   ) {				//휴일 처리 타입에 따라 
				String holyProcessGb = info.getHolyProcessGb();
				if ( holyProcessGb.equals("1")){ // 영업일이 아니면 처리하지 않음
					if ( HolidayManager.getInstance().isAllHoliday(DatetimeUtil.getCurrentDate())){
	                    logger.info("[스케쥴 핸들러]    ★★★ 영업일이 아니므로 처리 하지 않습니다.(토요일도 처리 안함) ("+ info.getProcessCode() +" | "+ info.getInstitutionCode()+" | "+ info.getScheduleCode() +") ★★★");
	                    continue;
					}
	                logger.debug("[스케쥴 핸들러]    ★★★ 오늘은 영업일.(토요일도 처리 안함) ("+ info.getProcessCode() +" | "+ info.getInstitutionCode() +" | "+ info.getScheduleCode()+") ★★★");
				}else if ( holyProcessGb.equals("2")){ // 영업일이 아니어도 토요일이 공휴일이 아닌경우에 처리함
					if ( HolidayManager.getInstance().isHoliday(DatetimeUtil.getCurrentDate())){
	                    logger.info("[스케쥴 핸들러]    ★★★ 영업일이 아니므로 처리 하지 않습니다.(토요일은 처리함) ("+ info.getProcessCode() +" | "+ info.getInstitutionCode() +" | "+ info.getScheduleCode()+") ★★★");
	                    continue;
					}
	                //logger.debug("[스케쥴 핸들러]    ★★★ 오늘은 영업일.(토요일은 처리함) ("+ info.getProcessCode() +" | "+ info.getInstitutionCode() +" | "+ info.getScheduleCode()+") ★★★");
				}else{
		            //logger.debug("[스케쥴 핸들러]    ★★★ 영업일 체크 안함 ("+ info.getProcessCode() +" | "+ info.getInstitutionCode() +" | "+ info.getScheduleCode()+") ★★★");
				}
				
				return info;
			}
		}

		return null;
	}
	
	public int requestRecieveJob(SchedulerMessageVO info) throws Exception
	{

		logger.debug("[SchedulerMessageManager:requestRecieveJob] =========================================================================");
		logger.debug("[SchedulerMessageManager:requestRecieveJob] ■ 요구수신 스케쥴 정보.");
		logger.debug("[SchedulerMessageManager:requestRecieveJob] - 스케쥴코드       : " + info.getScheduleCode());
		logger.debug("[SchedulerMessageManager:requestRecieveJob] - 시작시간(시분): " + info.getStartTime());
		logger.debug("[SchedulerMessageManager:requestRecieveJob] - 종료시간(시분): " + info.getEndTime());
		logger.debug("[SchedulerMessageManager:requestRecieveJob] =========================================================================");

		//1. 요구수신 스케쥴러 단계 DB로그를 위한 BatchMsg 객체 생성
		BatchDoc batchMsgDoc = EAIBatchMsgManager.createBatchMsgWithUUID(false);  //UUID 생성, false:초기화안함
		EAIBatchMsgManager.updateBatchMsgWithStageTime(batchMsgDoc, CommonKeys.LAYER_SCHEDULER, CommonKeys.SUB_LAYER_SCHEDULER_QUEUE);  // layer, flowPahse, 시작시간 설정

        batchMsgDoc.getBatchMsg().getHeader().setProcessCode(info.getProcessCode());
		batchMsgDoc.getBatchMsg().getHeader().setProcessType(info.getProcessType());
        batchMsgDoc.getBatchMsg().getHeader().setInstitutionCode(info.getInstitutionCode());
		batchMsgDoc.getBatchMsg().getHeader().setJobCode(info.getJobCode());
		batchMsgDoc.getBatchMsg().getHeader().setBizCode(info.getBizCode());
		batchMsgDoc.getBatchMsg().getHeader().setScheduleCode(info.getScheduleCode());
		batchMsgDoc.getBatchMsg().getHeader().setScheduleSTime(info.getStartTime());
		batchMsgDoc.getBatchMsg().getHeader().setScheduleETime(info.getEndTime());
		batchMsgDoc.getBatchMsg().getHeader().setFileName(info.getFileName());
		batchMsgDoc.getBatchMsg().getBody().setRetryCount(info.getRecvFailCnt());  //요구수신 재시도회수

		//2. 요구수신 디렉토리 설정 (수신_Real/업무구분/대외기관/..)
		//요구수신 기준 디렉토리 --> 배치업무유형코드(ProcessCode) 와 대외기관코드 (InstitutionCode) 로 계산.
		String strPathName = BatchDirUtil.getResponseRealDir();
		if ( strPathName.charAt(strPathName.length()-1) != '/' ) {
			strPathName = strPathName + '/';
		}

		StringBuffer sb = new StringBuffer();
		sb.append(info.getProcessCode());
		sb.append("/");
		sb.append(info.getInstitutionCode());
		strPathName = strPathName + sb.toString();

		info.setFilePath(strPathName);
		batchMsgDoc.getBatchMsg().getHeader().setFilePath(strPathName);


		//3. UUID 설정 및 스케쥴 송수신 시각 설정
		info.setUUID           (batchMsgDoc.getBatchMsg().getHeader().getUUID());
		info.setSubUUID        (batchMsgDoc.getBatchMsg().getBody().getSubUUID());
    	int startDelay = info.getStartDelayDays();
    	int endDelay   = info.getEndDelayDays();

		if (!info.getStartTime().equals("")) info.setStartTime(DatetimeUtil.getDayAddedDate( DatetimeUtil.getCurrentDate(), startDelay ) + info.getStartTime() +"00" + "000");
		if (!info.getEndTime()  .equals("")) info.setEndTime  (DatetimeUtil.getDayAddedDate( DatetimeUtil.getCurrentDate(), endDelay   ) + info.getEndTime()   +"00" + "000");

		//4. Job_Queue 테이블에 등록
		logger.info("[SchedulerMessageManager:requestRecieveJob] =========================================================================");
		logger.info("[SchedulerMessageManager:requestRecieveJob] ■ 요구수신 스케쥴 정보를 Job_Queue 테이블에 추가......");
		int resultCnt = SchedulerMessageManager.getInstance().insertJobToQueue(info);
		logger.info("[SchedulerMessageManager:requestRecieveJob] ==> 배치 Job_Queue 테이블 INSERT 건수: "+ resultCnt);
		logger.info("[SchedulerMessageManager:requestRecieveJob] =========================================================================");

		batchMsgDoc.getBatchMsg().getBody().setPhaseEndTime(CalendarUtil.getCurrentTimeNoDash());
        LogUtil.setStartLog(batchMsgDoc);
        
        return resultCnt;

	}
	public int requestRecieveJob(String scheduleCode, String reqYYYYMMDD) throws Exception
	{
		SchedulerMessageDAO dao = (SchedulerMessageDAO)DAOFactory.newInstance().create(SchedulerMessageDAO.class);
		SchedulerMessageVO info = dao.getScheduleByScheduleCode(scheduleCode);
	    info.setFileName(info.getBizCode() + reqYYYYMMDD);
	    return requestRecieveJob( info );
	}

	public SchedulerMessageVO getReqRecvSchedule(String scheduleId) throws Exception
    {
		SchedulerMessageDAO dao = (SchedulerMessageDAO)DAOFactory.newInstance().create(SchedulerMessageDAO.class);
        return dao.getScheduleInfo(scheduleId);
    }
    
	
	public SchedulerMessageVO getReqSendSchedule(String scheduleId) throws Exception
	{
		SchedulerMessageDAO dao = (SchedulerMessageDAO)DAOFactory.newInstance().create(SchedulerMessageDAO.class);
        return dao.getReqSendScheduleInfo(scheduleId);
	}
		
//	public synchronized void setInProcess( boolean flag ){
//		inProcess = flag;
//		if ( inProcess ) lastProcess = System.currentTimeMillis();
//	}

	
	public JobMasterLogVO getJobLogByKey(String uuid,String subUuid) throws Exception {
		SchedulerMessageDAO dao = (SchedulerMessageDAO)DAOFactory.newInstance().create(SchedulerMessageDAO.class);
        return dao.getJobLogByKey(uuid, subUuid);
	}

	public JobFileTransferVO getFileLogList(String uuid,String subUuid, String fileName) throws Exception {
		SchedulerMessageDAO dao = (SchedulerMessageDAO)DAOFactory.newInstance().create(SchedulerMessageDAO.class);
        return dao.getFileLogList(uuid, subUuid, fileName);
	}
	
	public ArrayList<JobFileTransferVO> getScheduledJobList(ArrayList<String> basisList ) throws Exception {
		SchedulerMessageDAO dao = (SchedulerMessageDAO)DAOFactory.newInstance().create(SchedulerMessageDAO.class);
        return dao.getScheduledJobList(basisList);
	}
	
	public ArrayList<JobProcessingVO> getProcessinbgJobList() throws Exception {
		SchedulerMessageDAO dao = (SchedulerMessageDAO)DAOFactory.newInstance().create(SchedulerMessageDAO.class);
		return dao.getProcessinbgJobList();

	}
	
    public int deleteJobFromProcessing(String uuid) throws Exception
    {
		SchedulerMessageDAO dao = (SchedulerMessageDAO)DAOFactory.newInstance().create(SchedulerMessageDAO.class);
        return dao.deleteJobFromProcessing(uuid);
    }

	
}
