package com.eactive.eai.batch.job.jobHandle;

import java.util.ArrayList;
import java.util.HashMap;

import com.eactive.eai.batch.job.jobModule.Component.JobComponentService;

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.message.EAIBatchMsgManager;
import com.eactive.eai.batch.rule.nodeinfo.NodeInfoManager;
import com.eactive.eai.batch.rule.nodeinfo.NodeInfoVO;
import com.eactive.eai.batch.rule.phaseinfo.PhaseInfoManager;
import com.eactive.eai.batch.rule.phaseinfo.PhaseInfoVO;
import com.eactive.eai.batch.running.BatchRunningJobManager;
import com.eactive.eai.common.exception.ExceptionUtil;
import com.eactive.eai.common.routing.Process;
import com.eactive.eai.common.util.Logger;

public class FlowController extends Process
{
    public BatchDoc batchDoc;

	// FileLoger
    protected Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);

    public String       logHeader;

    private String strUUID;


    public final static String PHASECODE_START      = "START";
    public final static String PHASECODE_NORMAL_END = "END";
    public final static String PHASECODE_ERROR_END  = "EEND";

    public BatchDoc clientRequestwithReturn( BatchDoc batchDoc)
    {
    	this.batchDoc = batchDoc;
    	logger = batchDoc.getLogger();
	
    	try {
			init();
			while (checkNextStage()) {
				excutePhaseStage();
			}
			end();
		} catch (Exception e) {
			handleException(e);
		}
    	
        return batchDoc;
    }
    
    public void clientRequest() {
    	// do noting
    }

    public void init() throws Exception
    {
        batchDoc.getBatchMsg().getBody().setPhaseStartTime(CalendarUtil.getCurrentTimeNoDash());

		logHeader = "["+ batchDoc.getBatchMsg().getHeader().getUUID() +" / "+
		                 batchDoc.getBatchMsg().getHeader().getProcessCode() +" / "+
		                 batchDoc.getBatchMsg().getHeader().getInstitutionName() +" / "+
		                 batchDoc.getBatchMsg().getHeader().getProcessType() +" / "+
		                 batchDoc.getBatchMsg().getHeader().getFileName() +" / "+
		                 batchDoc.getBatchMsg().getHeader().getRuleCode() +"] ";

		strUUID = batchDoc.getBatchMsg().getHeader().getUUID();

        logger.info("{} ================================================================================================="+ logHeader);
        logger.info("{} ▷▷▷▷▷ FlowController 시작 : "+ batchDoc.getBatchMsg().getHeader().getRuleCode() +" ("+ batchDoc.getBatchMsg().getHeader().getRuleDesc() +")"+ logHeader);
        logger.info("{} ================================================================================================="+ logHeader);
        //logger.info("{} ▷ Batch Socket Count     : " + BatchRunningJobManager.getInstance().getRunningJobCount(), logHeader);
        //logger.info("{} ▷ Job Process Data Count : " + JobProcessData.getInstance().getBatchStageDataCount(), logHeader);
        //logger.info("{} =================================================================================================", logHeader);
        
        //BatchRunningJobManager 에서 진행중인 BatchMessageDocument 를 update
        BatchRunningJobManager.getInstance().updateRunningDocument(strUUID, batchDoc);

        //LayerCode, SubLayerCode 설정
        EAIBatchMsgManager.updateBatchMsg(batchDoc, CommonKeys.LAYER_FLOW_CONTROLLER, CommonKeys.SUB_LAYER_FLOW_CONTROLLER);

        // Flow Rule 의 시작단계를 지정한다. (반드시 'START' 이어야함)
        batchDoc.getBatchMsg().getBody().setNextPhaseCode(PHASECODE_START);
    }

    /**
     * WhleDo 문의 조건처리 함수
     */
    public boolean checkNextStage()
    {
		//단계 시작시간 설정
		batchDoc.getBatchMsg().getBody().setPhaseStartTime(CalendarUtil.getCurrentTimeNoDash());

        String nextPhaseCode = batchDoc.getBatchMsg().getBody().getNextPhaseCode();
        logger.info("{} ▶ 다음 수행 단계: ["+ nextPhaseCode +"]"+ logHeader);

		if (nextPhaseCode.equalsIgnoreCase(PHASECODE_NORMAL_END)) {
			new JobFinishHandler(batchDoc).handleSuccessEnd();
            logger.info("{} ▷ 정상종료 단계("+ PHASECODE_NORMAL_END +"') 가 수행되었습니다. FlowController 를 종료합니다."+ logHeader);
			return false;

		} else if (nextPhaseCode.equalsIgnoreCase(PHASECODE_ERROR_END)) {
			new JobFinishHandler(batchDoc).handleErrorEnd("");
            logger.info("{} ▷ 에러종료 단계("+ PHASECODE_ERROR_END +") 가 수행되었습니다. FlowController 를 종료합니다."+ logHeader);
			return false;

		} else {
			return true;
		}
    }

    public void excutePhaseStage() throws Exception
    {
		String strRuleCode   = batchDoc.getBatchMsg().getHeader().getRuleCode();
        String execPhaseCode = batchDoc.getBatchMsg().getBody().getNextPhaseCode();

		try {

            // 이전 단계 정보를 유지하기위해 현 단계 정보를 저장한다. (현재는 이전 단계 정보를 사용하는 곳 없음)
            batchDoc.getBatchMsg().getBody().setLastPhaseCode     (batchDoc.getBatchMsg().getBody().getFlowPhaseCode());
            batchDoc.getBatchMsg().getBody().setLastPhaseType     (batchDoc.getBatchMsg().getBody().getPhaseType());
            batchDoc.getBatchMsg().getBody().setLastPhaseStartTime(batchDoc.getBatchMsg().getBody().getPhaseStartTime());
            batchDoc.getBatchMsg().getBody().setLastPhaseEndTime  (batchDoc.getBatchMsg().getBody().getPhaseEndTime());

            // 다음 단계 코드를 이용하여 필요한 정보를 조회하고 저장해 준다.
            logger.debug("{} ■ 단계 Logging 시작... [RuleCode: " + strRuleCode + "] [NextPhaseCode: "+ execPhaseCode +"]"+ logHeader);

			logger.debug("{} >> Node Manager (TSEAIBR03) 에서 해당 Node 정보 조회...\r\n"+logHeader);
            NodeInfoVO  nvo = NodeInfoManager.getInstance().getNodeInfo(strRuleCode, execPhaseCode);
            if (nvo == null) {
				//실행대상 단계코드를 TSEAIBR03 테이블에서 찾을 수 없음 (RuleCode:{1}, PhaseCode:{2})
                String errMsg = ExceptionUtil.getErrorCode("BECEAIFJH002", new String[] {strRuleCode, execPhaseCode});
				throw new Exception(errMsg);
                //setNextPhaseAsError(strRuleCode, errMsg);  //다음 단계를 'EEND' 노드로 설정
            }
            String execPhaseClassName = nvo.getFlowComp();
			logger.debug("{}  => [RuleCode: "+ nvo.getRuleCode() +"] "+
			                    "[PhaseCode: "+ nvo.getPhaseCode() +"] "+
								"[PhaseType: "+ nvo.getPhaseType() +"] "+
								"[ClassName: "+ nvo.getFlowComp() +"] "+
								"[TelegramID: "+ nvo.getTelegramID() +"]"+ logHeader);

			//Flow Component 클래스명 값이 있는지 체크
            if (execPhaseClassName==null || execPhaseClassName.equals("")) {
                // 실행대상 단계코드의 해당 FlowComponent 클래스이름이 지정되어 있지 않음 (RuleCode:{1}, PhaseCode:{2})
                String errMsg = ExceptionUtil.getErrorCode("BECEAIFJH003", new String[] {strRuleCode, execPhaseCode});
                throw new Exception(errMsg);
				//setNextPhaseAsError(strRuleCode, errMsg);  //다음 단계를 'EEND' 노드로 설정

            } else {
                // body 쪽 NEXT 설정
                batchDoc.getBatchMsg().getBody().setFlowPhaseCode(execPhaseCode);
                batchDoc.getBatchMsg().getBody().setPhaseType(nvo.getPhaseType());  //단계 타입 (SEND, RECV, END, EEND 중 하나)

                // PhaseInfo 설정 by Node Info
				batchDoc.getBatchMsg().getPhaseinfo().setRuleCode          (strRuleCode);
				batchDoc.getBatchMsg().getPhaseinfo().setPhaseCode         (execPhaseCode);
				batchDoc.getBatchMsg().getPhaseinfo().setPhaseType         (nvo.getPhaseType());
				batchDoc.getBatchMsg().getPhaseinfo().setFlowClassName     (nvo.getFlowComp());
				batchDoc.getBatchMsg().getPhaseinfo().setLengthFieldIndex  (nvo.getLengthField());
				batchDoc.getBatchMsg().getPhaseinfo().setLengthTelegramID  (nvo.getTelegramID());
				batchDoc.getBatchMsg().getPhaseinfo().setLengthClassName   ("");
				batchDoc.getBatchMsg().getPhaseinfo().setLengthMsgCode     ("");
				batchDoc.getBatchMsg().getPhaseinfo().setPhaseSeq          ("");
				batchDoc.getBatchMsg().getPhaseinfo().setPhaseTelegramID   ("");
				batchDoc.getBatchMsg().getPhaseinfo().setPhaseClassName    ("");
				batchDoc.getBatchMsg().getPhaseinfo().setPhaseMsgCode      ("");
				batchDoc.getBatchMsg().getPhaseinfo().setTelegramTypeValue ("");
				batchDoc.getBatchMsg().getPhaseinfo().setBizCodeValue      ("");
				batchDoc.getBatchMsg().getPhaseinfo().setControlCodeValue  ("");
				batchDoc.getBatchMsg().getPhaseinfo().setResponseCodeValue ("");
            }


            // 1. JobProcessData와 관련된 작업 수행
            // 1-1  JobProcessData에서 이전단계에서 사용했던 '다음단계'결정용 Stage변환룰 정보를 삭제해준다.
			HashMap<String, Object> curr_job_data = JobProcessData.getInstance().getBatchStageData(strUUID);
			//Flow 최초 단계 수행시 해당 UUID의 HashMap 생성함
			if (curr_job_data == null) curr_job_data = new HashMap<String, Object>();
            curr_job_data.remove(JobProcessData.PROCDATA_NEXTSTAGE);

            // 1-2  TSEAIBR02 테이블에서 현재 Node의 가능 Stage를 모두 구해서 JobProcessData에 할당
            //      Next Possible Stage 가 없으면, 현재 지정된 단계가 최종이라는 뜻.
            logger.debug("{} >> Phase Manager (TSEAIBR02) 에서 해당 Node의 수행 가능 Phase 목록 조회... [RuleCode: " + strRuleCode + "] [PhaseCode: "+ execPhaseCode +"]"+ logHeader);
            ArrayList<PhaseInfoVO> al = PhaseInfoManager.getInstance().getPhaseInfo(strRuleCode, execPhaseCode);
			if (al.size() > 0) {
				//NEXT Stage 목록 debug logging...
				for (int i=0; i< al.size(); i++) {
					PhaseInfoVO pvo = (PhaseInfoVO) al.get(i);
					logger.debug("{}   => "+ (i+1) +". [PhaseCode: "+ pvo.getPhaseCode() +"] [PhaseSeq: "+ pvo.getPhaseSeq() +"] [TelegramID: "+ pvo.getTelegramID() +"] [NextPahseCode: "+ pvo.getNextPhaseCode() +"]"+ logHeader);
				}

			// END, EEND 단계인 경우 TSEAIBR02 테이블에 단계 정보가 없어도 해당 단계를 수행함
            //} else if (execPhaseCode.trim().equalsIgnoreCase("END") || execPhaseCode.trim().equalsIgnoreCase("EEND")) {
            //    logger.debug("{}   => 'END' 또는 'EEND' 단계임. 해당 종료 단계를 수행 후 배치 작업을 종료함.", logHeader);

            // NEXT Stage가 없고, "END" 나 "EEND" 가 아닌 경우에는 에러 처리함
			} else {
				//실행대상 단계코드를 TSEAIBR02 (Phase) 테이블에서 찾을 수 없음 (RuleCode:{1}, PhaseCode:{2})
				String errMsg = ExceptionUtil.getErrorCode("BECEAIFJH004", new String[] {strRuleCode, execPhaseCode});
				throw new Exception(errMsg);
				//setNextPhaseAsError(strRuleCode, errMsg);  //다음 단계를 'EEND' 노드로 설정
			}

            curr_job_data.put(JobProcessData.PROCDATA_NEXTSTAGE, al);
            JobProcessData.getInstance().setBatchStageData(strUUID, curr_job_data);

            // 2. 클래스 생성
            logger.info("{} >> 수행할 단계 클래스 생성 및 execute() 호출...\r\n"+ logHeader);
            JobComponentService phaseStage = (JobComponentService)Class.forName(execPhaseClassName).newInstance();
            phaseStage.setEAIBatchMessage(batchDoc);

            // 3. 해당 클래스를 호출한다.
            logger.info("{}   => ############################ ClassName: [" + execPhaseClassName + "] ############################"+ logHeader);
            //========================================================================
            batchDoc = phaseStage.execute();
            //========================================================================
			logHeader = "["+ batchDoc.getBatchMsg().getHeader().getUUID() +" / "+
							 batchDoc.getBatchMsg().getHeader().getProcessCode() +" / "+
							 batchDoc.getBatchMsg().getHeader().getInstitutionName() +" / "+
							 batchDoc.getBatchMsg().getHeader().getProcessType() +" / "+
							 batchDoc.getBatchMsg().getHeader().getFileName() +" / "+
							 batchDoc.getBatchMsg().getHeader().getRuleCode() +"] ";

			//단계 종료시간 설정
			batchDoc.getBatchMsg().getBody().setPhaseEndTime(CalendarUtil.getCurrentTimeNoDash());
			//현 단계 DB 로깅
			LogUtil.setLog(batchDoc);

            logger.debug("{} ■ 단계 Logging 종료 !! [RuleCode: " + strRuleCode + "] [PhaseCode: "+ execPhaseCode +"]"+ logHeader);


		} catch (Exception ex) {
			//FlowController 에서 해당 단계 수행시 Exception 발생. (RuleCode:{1}, PhaseCode:{2})
            String errMsg = ExceptionUtil.getErrorCode(ex, "BECEAIFJH001", new String[] {strRuleCode, execPhaseCode});
            throw new Exception(errMsg);
			//setNextPhaseAsError(strRuleCode, errMsg);  //다음 단계를 'EEND' 노드로 설정
        }
    }

    public void end() throws Exception
    {
        logger.info("{} ================================================================================================="+ logHeader);
        logger.info("{} ▷▷▷▷▷ FlowController 종료 : "+ batchDoc.getBatchMsg().getHeader().getRuleCode() +" ("+ batchDoc.getBatchMsg().getHeader().getRuleDesc() +")"+ logHeader);
        logger.info("{} ================================================================================================="+ logHeader);
        //logger.info("{} ▷ Batch Socket Count     : " + BatchRunningJobManager.getInstance().getRunningJobCount(), logHeader);
        //logger.info("{} ▷ Job Process Data Count : " + JobProcessData.getInstance().getBatchStageDataCount(), logHeader);
        //logger.info("{} =================================================================================================", logHeader);
    }

    public void handleException(Exception ex)
    {
        try {
			//Exception ex = this.context.getExceptionInfo().getException();

            // 파일로그 처리
            String errMsg = ExceptionUtil.getErrorCode(ex, "BECEAIFJH006");
            logger.error("{"+logHeader+"}" + " {"+"logHeader"+"}" + errMsg, ex);

            // DB로그 처리
			new JobFinishHandler(batchDoc).handleErrorEnd(errMsg);

        } catch (Exception e) {
            logger.error("{"+logHeader+"} ★★★★★ FlowController JPD 예외 처리 중 에러 !! ★★★★★\r\n"+ e.getMessage());
        }
    
    }
    
/*
    private void setNextPhaseAsError(String strRuleCode, String strErrMsg) throws Exception
    {
        NodeInfoVO  err_nvo = NodeInfoManager.getInstance().getNodeInfo(strRuleCode, this.PHASECODE_ERROR_END);
        // body 쪽 NEXT 설정
        batchDoc.getEAIBatchMessage().getBody().setFlowPhaseCode(this.PHASECODE_ERROR_END);
        batchDoc.getEAIBatchMessage().getBody().setPhaseType("");
        // PhaseInfo 설정 by Node Info
		batchDoc.getEAIBatchMessage().getPhaseinfo().setRuleCode          (strRuleCode);
		batchDoc.getEAIBatchMessage().getPhaseinfo().setPhaseCode         (this.PHASECODE_ERROR_END);
		batchDoc.getEAIBatchMessage().getPhaseinfo().setPhaseType         (err_nvo.getPhaseType());
		batchDoc.getEAIBatchMessage().getPhaseinfo().setFlowClassName     (err_nvo.getFlowComp());
		batchDoc.getEAIBatchMessage().getPhaseinfo().setLengthFieldIndex  (err_nvo.getLengthField());
		batchDoc.getEAIBatchMessage().getPhaseinfo().setLengthTelegramID  (err_nvo.getTelegramID());
		batchDoc.getEAIBatchMessage().getPhaseinfo().setLengthClassName   ("");
		batchDoc.getEAIBatchMessage().getPhaseinfo().setLengthMsgCode     ("");
		batchDoc.getEAIBatchMessage().getPhaseinfo().setPhaseSeq          ("");
		batchDoc.getEAIBatchMessage().getPhaseinfo().setPhaseTelegramID   ("");
		batchDoc.getEAIBatchMessage().getPhaseinfo().setPhaseClassName    ("");
		batchDoc.getEAIBatchMessage().getPhaseinfo().setPhaseMsgCode      ("");
		batchDoc.getEAIBatchMessage().getPhaseinfo().setTelegramTypeValue ("");
		batchDoc.getEAIBatchMessage().getPhaseinfo().setBizCodeValue      ("");
		batchDoc.getEAIBatchMessage().getPhaseinfo().setControlCodeValue  ("");
		batchDoc.getEAIBatchMessage().getPhaseinfo().setResponseCodeValue ("");

        Logging(ERROR, strErrMsg);
		batchDoc.getEAIBatchMessage().getBody().setErrorMsg(strErrMsg);
    }
*/


	public void procFileEvent(String recvdirPath, String recvfileName, String lines)
			throws Exception {
	}
    
}
