package com.eactive.eai.batch.bokex;

import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashSet;

import com.eactive.eai.batch.flowController.DemandTargetVO;
import com.eactive.eai.batch.flowController.FlowControllerManager;
import com.eactive.eai.batch.scheduler.SchedulerMessageDAO;
import com.eactive.eai.common.dao.DAOFactory;
import com.eactive.eai.common.exception.ExceptionUtil;
import com.eactive.eai.common.util.Logger;

public class BOKEXAddTelegramManager {

    private static BOKEXAddTelegramManager instance = new BOKEXAddTelegramManager();
    public static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
    
    public static BOKEXAddTelegramManager getInstance() {
        return instance;
    }    
    private Socket socket = null;
    private DataInputStream din = null;
    private DataOutputStream dout = null;
    private String     	userId;
    private String      password;



    private BOKEXAddTelegramManager(){
    }

    public String doTelegram( String reqData ){
    	String resData = "";
    	if ( socket != null){
    		return "";
    	}
    	
    	try {
        	if ( !connect()) {
        		return "";
        	}
        	
            din = new DataInputStream(socket.getInputStream());
            dout = new DataOutputStream(socket.getOutputStream());
            
            //                 "FOREXFTP2FXB110406000010  N000200902261755301104FTP00000000000011104FTP@        0030B0N";
            byte[] loginData = "FOREXFTP2FXB113706000010  N000                                                  0030B0N".getBytes();
            //
            SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss");
            String curDate = formatter.format(Calendar.getInstance().getTime());

            System.arraycopy( curDate.getBytes()  , 0, loginData, 30, 14);
            System.arraycopy( userId.getBytes()   , 0, loginData, 44, userId.getBytes().length);
            System.arraycopy( password.getBytes() , 0, loginData, 64, password.getBytes().length);

            String loginResult = getRecvData( new String(loginData) );
            if ( !loginResult.substring(27,30).equals("000")  ){
            	return loginResult;
            }
            resData = getRecvData(reqData);
            //                 "FOREXFTP2FXB110406000040  N00020090226175530";
            byte[] looutData = "FOREXFTP2FXB113706000040  N000              ".getBytes();
            System.arraycopy( curDate.getBytes()  , 0, looutData, 30, 14);
            getRecvData(new String (looutData));


		} catch (Exception e) {
			logger.error( e.getMessage(), e);
		} finally {
			if ( socket != null ){
				try {
					socket.close();
				} catch (Exception e) {}
			}
			socket = null;
		}
    	return resData;
    }
    private String getRecvData( String sendData ){
    	String recvData = null;
    	
    	try {

    		int sendLen = sendData.getBytes().length;
    		byte[] sendBuf = new byte[sendLen + 4];
    		System.arraycopy( ( ""+(sendLen + 10000)).getBytes(), 1, sendBuf, 0, 4 );
    		System.arraycopy( sendData.getBytes(), 0, sendBuf, 4, sendLen);
            dout.write(sendBuf);

            byte[] recvBuf = new byte[4096];
            ByteArrayOutputStream boutll = new ByteArrayOutputStream();
            
            ArrayList<String> al = new ArrayList<String>();

            boolean contFlag = false;
            do {
                // LL 필드 수신
                int nRemainBytes = 4;
                while (nRemainBytes > 0) {
                    int nReadBytes = recvData(din, 60, recvBuf, nRemainBytes);  //한번에 필드 길이만큼 읽지 않을수도 있음
                    boutll.write(recvBuf, 0, nReadBytes);
                    nRemainBytes -= nReadBytes;
                }
                String strll = new String(boutll.toByteArray());
                nRemainBytes = Integer.parseInt(strll); 

                ByteArrayOutputStream bout = new ByteArrayOutputStream();

                while (nRemainBytes > 0) {
                    int nReadBytes = recvData(din, 60, recvBuf, nRemainBytes);  //한번에 필드 길이만큼 읽지 않을수도 있음
                    bout.write(recvBuf, 0, nReadBytes);
                    nRemainBytes -= nReadBytes;
                }
                String resTelegram = new String(bout.toByteArray()); 
                al.add(resTelegram);
                String resTelegramType = getTelegramType(resTelegram);
                if ( resTelegramType.equals("02100010")){
                	if (resTelegram.substring(50,51).equals("Y") ){
                		contFlag = true;
                	}else{
                		contFlag = false;
                	}
                	if ( recvData != null ){
                		recvData = recvData + resTelegram.substring(51);
                	}else{
                		recvData = resTelegram;
                	}
                }else if ( resTelegramType.equals("02100020")){
                	if (resTelegram.substring(42,43).equals("Y") ){
                		contFlag = true;
                	}else{
                		contFlag = false;
                	}

                	if ( recvData != null ){
                		recvData = recvData + resTelegram.substring(43);
                	}else{
                		recvData = resTelegram;
                	}
                }else if ( resTelegramType.equals("02100030")){
                	if (resTelegram.substring(50,51).equals("Y") ){
                		contFlag = true;
                	}else{
                		contFlag = false;
                	}

                	if ( recvData != null ){
                		recvData = recvData + resTelegram.substring(51);
                	}else{
                		recvData = resTelegram;
                	}

                } else {
                	recvData = resTelegram;
                }

            } while (contFlag);

		} catch (Exception e) {
			logger.error( e.getMessage(), e);
    		return "";
		}
    	return recvData;
    }
    
    private boolean connect() throws Exception{
    	DemandTargetVO vo = getSysConnList();
    	if ( vo == null ){
    		return false;
    	}

    	userId = vo.getSocketID();
    	password = vo.getSocketPwd();
    	int nPortNum = -1;
		try {
			nPortNum = Integer.parseInt(vo.getPortNumber());

		} catch (Exception ex) {
			String errMsg = ExceptionUtil.getErrorCode(ex, "BECEAIFJC002", new String[] {vo.getPortNumber()});
			throw new Exception(errMsg);
		}

		socket = socketInit(vo.getIpAddress(), nPortNum);
		if (socket == null) {
			String errMsg = ExceptionUtil.getErrorCode("BECEAIFJC003");
			throw new Exception(errMsg);
		}

    	return true;
    }
    
    private String getTelegramType(String str){
    	byte[] telegramType = new byte[8];
    	System.arraycopy( str.getBytes(), 16, telegramType, 0, 8);
    	return new String(telegramType);
    }

    private Socket socketInit(String remoteIP, int remotePort) throws Exception
    {
        try {
            return new Socket(remoteIP, remotePort);
        
        } catch (UnknownHostException ex) {
            String errMsg = ExceptionUtil.getErrorCode(ex, "BECEAIASC001", new String[] {remoteIP, Integer.toString(remotePort)});
            throw new Exception(errMsg);	//Socket 연결 에러 (IP: %1, Port: %2), 서버를 찾을 수 없습니다.
        
        } catch (Exception ex) {
            String errMsg = ExceptionUtil.getErrorCode(ex, "BECEAIASC002", new String[] {remoteIP, Integer.toString(remotePort)});
            throw new Exception(errMsg);	//Socket 연결 에러 (IP: %1, Port: %2), I/O Exception이 발생했습니다.
        }
    }

    private DemandTargetVO getSysConnList() {
    	
    	DemandTargetVO dtVO = null;
    	try {
			//1. 해당 작업의 대외기관 연결정보 조회
			DemandTargetVO[] sysConnList = FlowControllerManager.getInstance().getRemoteHostInfo(BOKEXKeys.PROC_CODE, BOKEXKeys.INSTI_CODE);
			if (sysConnList == null || sysConnList.length == 0) {
				logger.info("[BOKEXAddTelegramManager:checkSysConnList]["+ BOKEXKeys.PROC_CODE +" | "+ BOKEXKeys.INSTI_CODE +"]  ==> 해당 작업의 요구송수신 연결정보가 존재하지 않음 !! ★★ 연결정보 체크 필요 ★★");
				return null;
			}
    		//1. Job_Processing 테이블에서 모든 연결정보ID 조회
    		SchedulerMessageDAO dao = (SchedulerMessageDAO)DAOFactory.newInstance().create(SchedulerMessageDAO.class);
        	HashSet<String> runsysConnList = dao.getExecutingSysConnList();
        	
			for (int inx=0; inx < sysConnList.length; inx++) {
                //대외기관 연결상태가 장애이면 다음 연결정보 조회
				if (sysConnList[inx].getOrganSocketStatus().equals(DemandTargetVO.ORGAN_SOCKET_STATUS_ERROR)) {
                    continue;
                }
				//해당 연결정보가 사용중이지 않으면 해당 연결정보 할당
				if (!runsysConnList.contains(sysConnList[inx].getSystemConnCode())) {
					dtVO = sysConnList[inx];
					break;
				}
			}
			if ( dtVO == null ) {
                logger.info("[BOKEXAddTelegramManager:checkSysConnList]["+ BOKEXKeys.PROC_CODE +" | "+ BOKEXKeys.INSTI_CODE +"]★★★  ==> 해당 작업의 연결정보가 모두 사용중 임 !! -> 다음 Timer Event 시 연결정보 Release 된 경우 수행됨.");
				return null;
			}

		} catch (Exception e) {
			logger.warn( "진행작업 조회 중 오류 발생");
			return null;
		}
		return dtVO;
    }
    private int recvData(DataInputStream din, int timeoutMilisec,  byte[] buffer, int len) throws Exception
    {
        if (buffer == null || buffer.length == 0) throw new Exception("");
        
        int readBytes = 0;
        int timoutCount = 0;
        
        while (true) {
            try {
                readBytes = din.read(buffer, 0, len);
                if (readBytes < 0) {
                    String errMsg = ExceptionUtil.getErrorCode("BECEAIFJM010");
                    throw new Exception(errMsg);  //대외기관으로부터 전문 수신 시 End-Of-Stream 에 도착하여 읽어들일 데이터가 없습니다. 해당 대외기관에 연락바랍니다.
                }
                return readBytes;  //socket 에서 읽어들인 byte 수 리턴 (1 이상)
                
            } catch (SocketTimeoutException ex) {
                String errMsg = ExceptionUtil.getErrorCode("BECEAIFJM008", new String[] { timoutCount+"",  (timeoutMilisec/1000)+"" });
                throw new Exception(errMsg);  //대외기관으로부터 전문 수신 시 Timeout 이 발생하였습니다 (읽어들인 데이터 없음). 해당 대외기관에 연락바랍니다. [Timeout회수:{1}, soTimeout:{2}초]
            
            } catch (Exception ex) {  //throws IOException, NullPointerException, IndexOutOfBoundsException 
                String errMsg = ExceptionUtil.getErrorCode(ex, "BECEAIFJM011");
                throw new Exception(errMsg);  //대외기관으로부터 전문 수신 시 Socket 오류가 발생하여 데이터를 읽어들일 수 없습니다.
            }
        }
    }

}
