package com.eactive.eai.batch.ftp;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.net.ftp.FTPFile;

import com.eactive.eai.adapter.ftp.Transfer;
import com.eactive.eai.batch.common.StringUtil;
import com.eactive.eai.batch.doc.BatchDoc;
import com.eactive.eai.common.util.Logger;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.ChannelSftp.LsEntry;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpATTRS;
import com.jcraft.jsch.SftpException;

import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.connection.Connection;
import net.schmizz.sshj.sftp.RemoteResourceInfo;
import net.schmizz.sshj.sftp.SFTPClient;
import net.schmizz.sshj.transport.TransportException;
import net.schmizz.sshj.transport.verification.PromiscuousVerifier;

public class SFTPAdaptor {
	private static void loadKnownHosts(SSHClient ssh) throws IOException {
		// Host key 검사없이 인증
		ssh.addHostKeyVerifier(new PromiscuousVerifier());
	}


	protected static FTPFile[] listFiles(String server, int port, String username, String password, String path, HashMap<Integer, String> extmap, Logger logger, String authType) throws IOException {
		logger.debug("[SFHT connect]server  -->" + server);
		logger.debug("[SFHT connect]port    -->" + port);
		logger.debug("[SFHT connect]username-->" + username);
		logger.debug("[SFHT connect]password-->" + password);
		logger.debug("[SFHT listFiles]path-->" + path);


		SSHClient ssh = new SSHClient();
		try {
			loadKnownHosts(ssh);
			ssh.connect(server, port);
			
			// id 인증
			if (StringUtils.equals(authType, Transfer.AUTH_TYPE_ID)) {
				ssh.authPassword(username, password);
			// key 방식 인증
			} else if (StringUtils.equals(authType, Transfer.AUTH_TYPE_KEY)) {
				if (StringUtils.isEmpty(password)) {
					ssh.authPublickey(username); // 사용자 기본
				} else {
					ssh.authPublickey(username, new File(password).getPath());	// private key 지정
				}
			}
			
	        SFTPClient sftp = ssh.newSFTPClient();
	        logger.debug("sftp = " + sftp);
	        try{
				FTPFile[] files = null;
				List<RemoteResourceInfo> list = sftp.ls(path);
				// 라인이 너무 길어서 vi가 열리지 않음, 개발 라인으로 로깅
//		        logger.debug("list = " + list);
				files = new FTPFile[list.size()];
				for (int inx = 0; inx < list.size(); inx++) {
					RemoteResourceInfo info =list.get(inx);
					logger.debug("{"+info+"}");
					FTPFile file = new FTPFile();
					file.setName(info.getName());
					file.setSize(info.getAttributes().getSize());
					if ( info.isRegularFile() )
						file.setType(FTPFile.FILE_TYPE);
					else if ( info.isDirectory() )
						file.setType(FTPFile.DIRECTORY_TYPE);
					else
						file.setType(FTPFile.UNKNOWN_TYPE);
						
					files[inx] = file;
				}
				return files;
	        }finally{
	        	sftp.close();
	        }



		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			throw new IOException(e.getMessage());
		} finally {
			if ( ssh != null && ssh.isConnected())
				ssh.close();
		}

	}
//	protected static FTPFile[] listFiles_jsch(String server, int port, String username, String password, String path, HashMap<Integer, String> extmap) throws IOException {
//		logger.debug("[SFHT connect]server  -->" + server);
//		logger.debug("[SFHT connect]port    -->" + port);
//		logger.debug("[SFHT connect]username-->" + username);
//		logger.debug("[SFHT connect]password-->" + password);
//		logger.debug("[SFHT listFiles]path-->" + path);
//
//		Session session = null;
//		ChannelSftp sftpChannel = null;
//		String[] serverList = server.split(",");
//		try {
//
//			for (int inx = 0; inx < serverList.length; inx++) {
//				try {
//					JSch jsch = new JSch();
//
//					session = jsch.getSession(username, serverList[inx], port);
//					session.setPassword(password.getBytes());
//					session.setConfig("StrictHostKeyChecking", "no");
//					session.setTimeout(10 * 1000);
//					session.connect();
//
//				} catch (JSchException e) {
//					if ( inx == serverList.length -1)
//						throw e;
//				}
//			}
//			sftpChannel = (ChannelSftp) session.openChannel("sftp");
//			sftpChannel.connect();
//
//			FTPFile[] files = null;
//			if (extmap == null) {
//				@SuppressWarnings("unchecked")
//				Vector<LsEntry> v = sftpChannel.ls(path);
//				files = new FTPFile[v.size()];
//				int inx = 0;
//				for (Iterator<LsEntry> iterator = v.iterator(); iterator.hasNext();) {
//					LsEntry object = (LsEntry) iterator.next();
//					FTPFile file = new FTPFile();
//					SftpATTRS att = object.getAttrs();
//					file.setName(object.getFilename());
//					if (!att.isDir() && !att.isLink()) {
//						file.setType(0);
//					}
//					file.setSize(att.getSize());
//					Calendar cal = new GregorianCalendar();
//					cal.setTime(new Date(att.getMTime()));
//					files[inx++] = file;
//				}
//			} else {
//				ArrayList<FTPFile> list = new ArrayList<FTPFile>();
//				for (int inx = 0; inx < extmap.size(); inx++) {
//					String ext = extmap.get(inx);
//					@SuppressWarnings("unchecked")
//					Vector<LsEntry> v = sftpChannel.ls(path + "/*." + ext);
//					for (Iterator<LsEntry> iterator = v.iterator(); iterator.hasNext();) {
//						LsEntry object = (LsEntry) iterator.next();
//						FTPFile file = new FTPFile();
//						SftpATTRS att = object.getAttrs();
//						file.setName(object.getFilename());
//						if (!att.isDir() && !att.isLink()) {
//							file.setType(0);
//						}
//						file.setSize(att.getSize());
//						Calendar cal = new GregorianCalendar();
//						cal.setTime(new Date(att.getMTime()));
//						list.add(file);
//					}
//				}
//				files = new FTPFile[list.size()];
//				list.toArray(files);
//			}
//			return files;
//
//		} catch (JSchException e) {
//			logger.error(e.getMessage(), e);
//			throw new IOException(e.getMessage());
//		} catch (SftpException e) {
//			logger.error(e.getMessage(), e);
//			throw new IOException(e.getMessage());
//		} finally {
//			if (sftpChannel != null)
//				sftpChannel.disconnect();
//			if (session != null)
//				session.disconnect();
//		}
//
//	}

	/**
	 * 사용자 전송중지 기능 구현으로 JSch 사용 20220704
	 *  bandwidth 기능 구현
	 * @param batchDoc
	 * @param server
	 * @param port
	 * @param username
	 * @param password
	 * @param remote
	 * @param localPath
	 * @param connTimeout
	 * @param setTimeout
	 * @param bandWidth : bit unit
	 * @param logger
	 * @return
	 * @throws Exception
	 */
	protected static long retrieveJSCH(BatchDoc batchDoc, String server, int port, String username,
			String password, String remote, String remoteFileName, String localPath, int connTimeout, int setTimeout, int bandWidthLimit, Logger logger, String authType) throws Exception {

		logger.debug("[SFHT retrieve]remote-->" + remote);
		logger.debug("[SFHT retrieve]localPath-->" + localPath);
		
		Session session = null;
		ChannelSftp sftpChannel = null;
		
		try {
			
			JSch jsch = new JSch();
			
			// 키 인증 방식인 경우
			if (StringUtils.equals(authType, Transfer.AUTH_TYPE_KEY)) {
				jsch.addIdentity(password);
			}
			
			session = jsch.getSession(username, server, port);
			
			// 키 인증 방식이 아닌 경우
			if (!StringUtils.equals(authType, Transfer.AUTH_TYPE_KEY)) {
				session.setPassword(password);
			}
			session.setConfig("StrictHostKeyChecking", "no");
			session.setConfig("max_input_buffer_size","increased_size");

			if (connTimeout > 0)session.setTimeout(connTimeout*1000);
			session.connect();
			if(setTimeout > 0) session.setTimeout(setTimeout * 1000);
			
			sftpChannel = (ChannelSftp) session.openChannel("sftp");
			//sftpChannel.setPtySize(arg0, arg1, arg2, arg3);

			sftpChannel.connect();
			logger.debug("SFTPAdaptor] ChannelSftp connected!! server:"+server+", port:"+port);
			
			if (batchDoc.isUserCancel()){
				throw new Exception("사용자가 전송을 중지 했습니다.");
			}
			
			batchDoc.setSftpChannel(sftpChannel);
			sftpChannel.cd(remote);			
			
			retrieveJSCH(batchDoc, sftpChannel, remoteFileName, localPath, bandWidthLimit, logger);
		} catch (JSchException e) {
			logger.error(e.getMessage(), e);
			throw e;
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			throw new Exception(e.getMessage());
		} finally {
			
			batchDoc.setSftpChannel(null);
			if (sftpChannel != null) sftpChannel.disconnect();
			if (session != null) session.disconnect();
			
		}
		return 0;
	}
	
	protected static long retrieve(String server, int port, String username,
			String password, String remote, String localPath, Logger logger, String authType) throws Exception {

		logger.debug("[SFHT retrieve]remote-->" + remote);
		logger.debug("[SFHT retrieve]localPath-->" + localPath);

		SSHClient ssh = new SSHClient();
		try {
			loadKnownHosts(ssh);
			ssh.connect(server, port);
			// id 인증
			if (StringUtils.equals(authType, Transfer.AUTH_TYPE_ID)) {
				ssh.authPassword(username, password);
			// key 방식 인증
			} else if (StringUtils.equals(authType, Transfer.AUTH_TYPE_KEY)) {
				if (StringUtils.isEmpty(password)) {
					ssh.authPublickey(username); // 사용자 기본
				} else {
					ssh.authPublickey(username, new File(password).getPath());	// private key 지정
				}
			}
	        SFTPClient sftp = ssh.newSFTPClient();
			File chkDir = new File(localPath);
			String delimiter = StringUtil.getDirDelimiter(localPath);
			String fName = remote.substring(remote.lastIndexOf(delimiter) + 1);
			logger.debug("[retrieve]fName  ------->" + fName);

			if (!chkDir.exists() && !chkDir.mkdirs()) {
				logger.error("[수신 경로를 생성할 수 없습니다.]");
				return -1;
			}

			try{
				sftp.get(remote,localPath);
			}catch ( IOException ie){
				logger.error(ie.getMessage(), ie);
				throw new Exception(ie.getMessage());
			}finally{
				sftp.close();
			}
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			throw new Exception(e.getMessage());
		} finally {
			if ( ssh != null && ssh.isConnected())
				ssh.close();
		}
		return 0;
	}

	/**
	 * bandWidth제한만큼 버퍼사이즈 설정해서 1초단위로 전송한다.
	 * @param batchDoc
	 * @param server
	 * @param port
	 * @param username
	 * @param password
	 * @param remote
	 * @param remoteFileName
	 * @param localFileName
	 * @param connTimeout
	 * @param setTimeout
	 * @param bandWidthLimit
	 * @param logger
	 * @return
	 * @throws Exception
	 */
	protected static boolean storeJSCH(BatchDoc batchDoc, String server, int port, String username,
			String password, String remote, String remoteFileName, String localFileName, int connTimeout, int setTimeout, int bandWidthLimit, Logger logger, String authType) throws Exception {

		logger.debug("[SFHT storeJSCH]remote-->" + remote);
		logger.debug("[SFHT storeJSCH]remoteFileName-->" + remoteFileName);
		logger.debug("[SFHT storeJSCH]localFileName-->" + localFileName);
		
		Session session = null;
		ChannelSftp sftpChannel = null;
		BufferedOutputStream bos = null;
		BufferedInputStream bis = null;
		FileInputStream is = null;
		
		try {
			
			JSch jsch = new JSch();
			
			// 키 인증 방식인 경우
			if (StringUtils.equals(authType, Transfer.AUTH_TYPE_KEY)) {
				jsch.addIdentity(password);
			}
			
			session = jsch.getSession(username, server, port);
			
			// 키 인증 방식이 아닌 경우
			if (!StringUtils.equals(authType, Transfer.AUTH_TYPE_KEY)) {
				session.setPassword(password);
			}

			session.setConfig("StrictHostKeyChecking", "no");
			session.setConfig("max_input_buffer_size","increased_size");

			if (connTimeout > 0)session.setTimeout(connTimeout*1000);
			session.connect();
			if(setTimeout > 0) session.setTimeout(setTimeout * 1000);
			
			sftpChannel = (ChannelSftp) session.openChannel("sftp");
			//sftpChannel.setPtySize(arg0, arg1, arg2, arg3);

			sftpChannel.connect();
			logger.debug("SFTPAdaptor] ChannelSftp connected!! server:"+server+", port:"+port);
			
			storeJSCH(batchDoc, sftpChannel, remote, remoteFileName, localFileName, bandWidthLimit, logger);
		} catch (JSchException e) {
			logger.error(e.getMessage(), e);
			throw e;
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			throw new Exception(e.getMessage());
		} finally {

			batchDoc.setSftpChannel(null);
			if (sftpChannel != null) sftpChannel.disconnect();
			if (session != null) session.disconnect();
			
		}
		return true;
	}
	
	protected static boolean store(String server, int port, String username,
			String password, String remote, String localPath, Logger logger, String authType) throws Exception {

		logger.debug("[SFHT store]remote-->" + remote);
		logger.debug("[SFHT store]localPath-->" + localPath);

		SSHClient ssh = new SSHClient();
		try {
			loadKnownHosts(ssh);
			try{
				ssh.connect(server, port);
			}catch(TransportException e){
				String msg = e.getMessage();
				String[] split = msg.split("`");
				String vc = split[3];
				ssh.close();
				ssh = new SSHClient();
				ssh.addHostKeyVerifier(vc);
				ssh.connect(server, port);
				
				//20220531 대역폭 제한
				//window size scale factor가 얼마인지는?? window size x scale factor = real window size
				Connection conn = ssh.getConnection();
				logger.debug("[SFHT store]conn.getMaxPacketSize()-->" + conn.getMaxPacketSize());
				logger.debug("[SFHT store]conn.getWindowSize()-->" + conn.getWindowSize());
				logger.debug("[SFHT store]conn.getTimeoutMs()-->" + conn.getTimeoutMs());
			}
			// id 인증
			if (StringUtils.equals(authType, Transfer.AUTH_TYPE_ID)) {
				ssh.authPassword(username, password);
			// key 방식 인증
			} else if (StringUtils.equals(authType, Transfer.AUTH_TYPE_KEY)) {
				if (StringUtils.isEmpty(password)) {
					ssh.authPublickey(username); // 사용자 기본
				} else {
					ssh.authPublickey(username, new File(password).getPath());	// private key 지정
				}
			}
	        SFTPClient sftp = ssh.newSFTPClient();

//			String delimiter  = StringUtil.getDirDelimiter(remote);
//			String remotePath = remote.substring(0, remote.lastIndexOf(delimiter));
//			String fileName   = remote .substring(remote.lastIndexOf(delimiter) + 1);

//			logger.debug("[SFHT store]remotePath-->" + remotePath);
//			logger.debug("[SFHT store]fileName-->" + fileName);

			try{
				sftp.put(localPath, remote);
			}catch ( IOException ie){
				logger.error(ie.getMessage(), ie);
				throw new Exception(ie.getMessage());
			}finally{
				sftp.close();
			}


		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			throw new Exception(e.getMessage());
		} finally {
			if ( ssh != null && ssh.isConnected())
				ssh.close();
		}

		return true;
	}
	
	
	protected static boolean rename(String server, int port, String username,
			String password, String srcname, String tarname, Logger logger, String authType) throws Exception{

		logger.debug("[SFHT rename]srcname-->" + srcname);
		logger.debug("[SFHT rename]tarname-->" + tarname);

		SSHClient ssh = new SSHClient();
		try {
			loadKnownHosts(ssh);
			ssh.connect(server, port);
			// id 인증
			if (StringUtils.equals(authType, Transfer.AUTH_TYPE_ID)) {
				ssh.authPassword(username, password);
			// key 방식 인증
			} else if (StringUtils.equals(authType, Transfer.AUTH_TYPE_KEY)) {
				if (StringUtils.isEmpty(password)) {
					ssh.authPublickey(username); // 사용자 기본
				} else {
					ssh.authPublickey(username, new File(password).getPath());	// private key 지정
				}
			}
	        SFTPClient sftp = ssh.newSFTPClient();

			try{
				sftp.rename(srcname, tarname);
			}catch ( IOException ie){
				logger.error(ie.getMessage(), ie);
				throw new Exception(ie.getMessage());
			}finally{
				sftp.close();
			}
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			throw new Exception(e.getMessage());
		} finally {
			if ( ssh != null && ssh.isConnected())
				ssh.close();
		}
		return true;
	}	
	
	/**
	 * for KED backup, after mkdir, move file
	 */
	protected static boolean renameWithMkdirs(String server, int port, String username,
			String password, String remote, String backupPath, String filename, Logger logger, String authType) throws Exception {

		logger.debug("[SFHT rename]remote-->" + remote);
		logger.debug("[SFHT rename]remoteBackup-->" + backupPath + filename);

		SSHClient ssh = new SSHClient();
		try {
			loadKnownHosts(ssh);
			try{
				ssh.connect(server, port);
			}catch(TransportException e){
				String msg = e.getMessage();
				String[] split = msg.split("`");
				String vc = split[3];
				ssh.close();
				ssh = new SSHClient();
				ssh.addHostKeyVerifier(vc);
				ssh.connect(server, port);
			}
			// id 인증
			if (StringUtils.equals(authType, Transfer.AUTH_TYPE_ID)) {
				ssh.authPassword(username, password);
			// key 방식 인증
			} else if (StringUtils.equals(authType, Transfer.AUTH_TYPE_KEY)) {
				if (StringUtils.isEmpty(password)) {
					ssh.authPublickey(username); // 사용자 기본
				} else {
					ssh.authPublickey(username, new File(password).getPath());	// private key 지정
				}
			}
	        SFTPClient sftp = ssh.newSFTPClient();

//			String delimiter  = StringUtil.getDirDelimiter(remote);
//			String remotePath = remote.substring(0, remote.lastIndexOf(delimiter));
//			String fileName   = remote .substring(remote.lastIndexOf(delimiter) + 1);

//			logger.debug("[SFHT rename]remote-->" + remote);
//			logger.debug("[SFHT rename]remoteBackup-->" + remoteBackup);

			try{
				sftp.mkdirs(backupPath);
				sftp.rename(remote, backupPath + filename);
			}catch ( IOException ie){
				logger.error(ie.getMessage(), ie);
				throw new Exception(ie.getMessage());
			}finally{
				sftp.close();
			}
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			throw new Exception(e.getMessage());
		} finally {
			if ( ssh != null && ssh.isConnected())
				ssh.close();
		}

		return true;
	}

//	protected static boolean rename(String server, int port, String username,
//			String password, String srcname, String tarname) throws Exception {
//		Session session = null;
//		ChannelSftp sftpChannel = null;
//		String[] serverList = server.split(",");
//		
//		try {
//
//			for (int inx = 0; inx < serverList.length; inx++) {
//				try {
//					JSch jsch = new JSch();
//
//					session = jsch.getSession(username, serverList[inx], port);
//					session.setPassword(password);
//					session.setConfig("StrictHostKeyChecking", "no");
//					session.setTimeout(10 * 1000);
//					session.connect();
//
//				} catch (JSchException e) {
//					if ( inx == serverList.length -1)
//						throw e;
//				}
//			}
//
//			sftpChannel = (ChannelSftp) session.openChannel("sftp");
//			sftpChannel.connect();
//
//			sftpChannel.rename(srcname, tarname);
//			sftpChannel.quit();
//
//		} catch (JSchException e) {
//			//logger.error(e.getMessage(), e);
//			throw new Exception(e.getMessage());
//		} catch (SftpException e) {
//			//logger.error(e.getMessage(), e);
//			throw new Exception(e.getMessage());
//		} finally {
//			if (sftpChannel != null)
//				sftpChannel.disconnect();
//			if (session != null)
//				session.disconnect();
//		}
//		return true;
//	}
//
//	protected static boolean delete(String server, int port, String username,
//			String password, String srcname) throws Exception {
//		Session session = null;
//		ChannelSftp sftpChannel = null;
//		String[] serverList = server.split(",");
//		try {
//
//			for (int inx = 0; inx < serverList.length; inx++) {
//				try {
//					JSch jsch = new JSch();
//
//					session = jsch.getSession(username, serverList[inx], port);
//					session.setPassword(password);
//					session.setConfig("StrictHostKeyChecking", "no");
//					session.setTimeout(10 * 1000);
//					session.connect();
//
//				} catch (JSchException e) {
//					if ( inx == serverList.length -1)
//						throw e;
//				}
//			}
//
//			sftpChannel = (ChannelSftp) session.openChannel("sftp");
//			sftpChannel.connect();
//
//			sftpChannel.rm(srcname);
//			sftpChannel.quit();
//
//		} catch (JSchException e) {
//			logger.error(e.getMessage(), e);
//			throw new Exception(e.getMessage());
//		} catch (SftpException e) {
//			logger.error(e.getMessage(), e);
//			throw new Exception(e.getMessage());
//		} finally {
//			if (sftpChannel != null)
//				sftpChannel.disconnect();
//			if (session != null)
//				session.disconnect();
//		}
//		return true;
//	}
	public static void main(String[] args) throws Exception {
//		String[] serverList = "10.0.249.35".split(",");
//		for (int inx = 0; inx < serverList.length; inx++) {
//			System.out.println("serverList-->" + serverList[inx]);
//		}
//
//		FTPFile[]  list = listFiles("172.31.32.111", 22, "eaiadm", "!2345Trewq", "/home/eaiadm/src", null );
//		for ( int inx= 0; inx < list.length; inx++){
//			FTPFile file = list[inx];
//			
//		}
		String filename = "NICE_CSALINB_F13.160809.AA";
		System.out.println("NICE_CSALINB_F13.160809.AA-->" + filename.indexOf("."));
	}


	/**
	 * 재사용할 Session 객체를 얻는다. 
	 * @return
	 * @throws JSchException
	 */
	public static Session getSessionJSCH(String server, int port, String username, String password, String authType, int connTimeout, int setTimeout, Logger logger) throws JSchException {
		JSch jsch = new JSch();
		Session session = null;
		// 키 인증 방식인 경우
		try {
			if (StringUtils.equals(authType, Transfer.AUTH_TYPE_KEY)) {
				jsch.addIdentity(password);
			}
	
			session = jsch.getSession(username, server, port);
	
			// 키 인증 방식이 아닌 경우
			if (!StringUtils.equals(authType, Transfer.AUTH_TYPE_KEY)) {
				session.setPassword(password);
			}
	
			session.setConfig("StrictHostKeyChecking", "no");
			session.setConfig("max_input_buffer_size", "increased_size");
	
			if (connTimeout > 0)
				session.setTimeout(connTimeout * 1000);
			session.connect();
			if (setTimeout > 0)
				session.setTimeout(setTimeout * 1000);
			
			return session;
		} catch (JSchException e) {
			logger.error(e.getLocalizedMessage(), e);
			if (session != null) session.disconnect();
			throw e;
		}
	}
	
	/**
	 * 파일을 저장한다.
	 */
	public static boolean storeJSCH(BatchDoc batchDoc, ChannelSftp channelSftp, String remote, String remoteFileName, String localFileName, int bandWidthLimit, Logger logger) throws Exception {

		logger.debug("[SFHT storeJSCH]remote-->" + remote);
		logger.debug("[SFHT storeJSCH]remoteFileName-->" + remoteFileName);
		logger.debug("[SFHT storeJSCH]localFileName-->" + localFileName);
		
		BufferedOutputStream bos = null;
		BufferedInputStream bis = null;
		FileInputStream is = null;
		
		try {
			if (batchDoc.isUserCancel()){
				throw new Exception("사용자가 전송을 중지 했습니다.");
			}
			
			batchDoc.setSftpChannel(channelSftp);
			
			try {
				channelSftp.cd(remote);
			}catch (SftpException e) {
				logger.debug("SFTPAdaptor]"+ remote  +" 찾기 오류 발생!!!");
				try {
					String dir = remote.substring(0, remote.lastIndexOf("/"));
					String path = remote.replaceAll(dir + "/", "");
					channelSftp.cd(dir);
					channelSftp.mkdir(path);
					logger.debug("SFTPAdaptor]"+ path  +" 생성");
					channelSftp.cd(path);
					
				}catch (SftpException ex) {
					throw new Exception(remote + " 접근에 실패 하였습니다.");
				}
			}
			
			is = new FileInputStream(new File(localFileName));
			
			if(bandWidthLimit > 0){
				/**
				 * 대역폭 콘트롤 bis 512bps -> 512/8=64bytes/second, (1Byte=8bit),
				 */
				//Buffer buffer = new Buffer(1024);
				//int bandWidth = 1024*8;
				long bufferSize = bandWidthLimit/8;
				byte[] bufferArr = new byte[(int)bufferSize];
				
				try {
						bos = new BufferedOutputStream(channelSftp.put(remoteFileName));
						bis = new BufferedInputStream(is);
						batchDoc.setIOStream(bos);
						
						int readCount = 0;
						int availableCount = 0;
						int lootCnt = 0;
						
						long[] timeMillis = new long[2];
						timeMillis[0] = System.currentTimeMillis();
						
						long rtt = 0;//round trip time 1회 전송시간 millisecond
						long rttMax = 0;
						long rttMin = 0;
						double rttAve = 0;
						long rttSum = 0;
						
						long realBandWidth = 0;
						long realBandWidthMax = 0;
						long realBandWidthMin = 0;
						double realBandWidthAve = 0;
						long realBandWidthSum = 0;
						long writeByteSum = 0;
						
						long sizeStartTime = 0;
						long sizePerSecond = 0;

						while (true){
							
							lootCnt++;
							
							
							//강제중지 설정시
							if (batchDoc.isUserCancel()){
								//throw new Exception("사용자가 전송을 중지 했습니다.");
								logger.debug("InputFileName : " + localFileName+ " User canceled");
								break;
							}
							
							availableCount = bis.available();
							if(availableCount < 1) {
								logger.debug("InputFileName : " + localFileName+ " Reached end of file.");
								break;
							}
														
							readCount = bis.read(bufferArr);
							
							if(readCount<=0) {
								logger.debug("InputFileName : " + localFileName+ " read count less than 0.");
								break;
							}
							
							
							//****sftp send......
							timeMillis[0] = System.currentTimeMillis();
							bos.write(bufferArr,0,readCount);
							timeMillis[1] = System.currentTimeMillis();
							
							writeByteSum += readCount;
							batchDoc.setCurrIODataByte(writeByteSum);
							
							if (sizePerSecond > 0){
								rtt = timeMillis[1] - sizeStartTime;
							} else {
								rtt = timeMillis[1] - timeMillis[0];
							}
							
							if(rtt<=0){
								if(sizeStartTime==0)sizeStartTime = timeMillis[0];		
								sizePerSecond += readCount;
								realBandWidth = 0;
							}else {
								//rtt가 0보다 커야 bandwidth계산
								realBandWidth = (long) ((sizePerSecond+bufferSize) * 8  * 1000 / rtt);
								
								sizeStartTime = 0;
								sizePerSecond = 0;
							}
							
							realBandWidthSum += realBandWidth;
							if(realBandWidth>realBandWidthMax)realBandWidthMax=realBandWidth;
							else if(realBandWidth<realBandWidthMin)realBandWidthMin=realBandWidth;
							
							rttSum += rtt;
							if (rtt > rttMax)rttMax = rtt;
							else if (rtt < rttMin)rttMin = rtt;
							
							//1초이내로 처리가 된경우sleep,, 여유 10milli
							if(rtt < 990){ //여유 10milli
								logger.debug("SFTPAdaptor] remoteFileName ["+remoteFileName+"]"+ "loopCnt["+(lootCnt)+"] readCount["+readCount+"] availableCount["+availableCount+"] writeByteSum["+writeByteSum+"] rtt["+rtt+"] bandWidthLimit["+bandWidthLimit+"] realBandWidth["+realBandWidth+"] bufferSize["+bufferSize+"]sleep["+(990-rtt)+"]");
								
								Thread.sleep((long) (990-rtt));
								
								//1초를 채웠으므로 초기화 한다.
								sizeStartTime = 0;
								sizePerSecond = 0;
								
							} else {
								logger.debug("SFTPAdaptor] remoteFileName ["+remoteFileName+"]"+ "loopCnt["+(lootCnt)+"] readCount["+readCount+"] availableCount["+availableCount+"] writeByteSum["+writeByteSum+"] rtt["+rtt+"] bandWidthLimit["+bandWidthLimit+"] realBandWidth["+realBandWidth+"] bufferSize["+bufferSize+"]sleep[0]");											
							}
							
						}
						
						realBandWidthAve = realBandWidthSum/lootCnt;
						rttAve = rttSum/lootCnt;
						
						logger.info("SFTPAdaptor] remoteFileName ["+remoteFileName+"]"+"####Statistics : bufferSize["+bufferSize
								+"] loopCnt["+lootCnt
								+"] writeByteSum["+writeByteSum
								+"] rttAve["+rttAve
								+"] rttMax["+rttMax
								+"] rttMin["+rttMin
								+"] bandWidthLimit["+bandWidthLimit
								+"] realBandWidthAve["+realBandWidthAve
								+"] realBandWidthMax["+realBandWidthMax
								+"] realBandWidthMin["+realBandWidthMin
								+"]");

				} finally {
					try {
						if (bos != null){
							bos.flush();
							bos.close();
						}
						
						if (bis != null)bis.close();
						if(is!=null) is.close();
					} catch (Throwable t){
						logger.error("Closing Error:"+t.getMessage());
					}
				}
			} else {			
				channelSftp.put(is, remoteFileName);
				
				try {// 20250910 obl
					if(is!=null) is.close();
				} catch (Throwable t){
					logger.error("is Closing Error:"+t.getMessage());
				}
			}
			
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			throw e;
		} finally {
			batchDoc.setSftpChannel(null);
		}
		return true;
	}
	
	/**
	 * 사용자 전송중지 기능 구현으로 JSch 사용 20220704
	 *  bandwidth 기능 구현
	 *  remote 경로로 cd한 다음 호출해야 한다.
	 *  
	 */
	public static long retrieveJSCH(BatchDoc batchDoc, ChannelSftp channelSftp, String remoteFileName, String localPath, int bandWidthLimit, Logger logger) throws Exception {
		logger.debug("[SFHT retrieve]remote-->{"+remoteFileName+"}");
		logger.debug("[SFHT retrieve]localPath-->" + localPath);
		
		BufferedOutputStream bos = null;
		BufferedInputStream bis = null;
		FileOutputStream os = null;
		boolean fileReceived = false;
		File receiveFile = null;
		String localFileName = "";
		try {
			batchDoc.setSftpChannel(channelSftp);
			
			File chkDir = new File(localPath);
			if (!chkDir.exists() && !chkDir.mkdirs()) {
				logger.error("[수신 경로를 생성할 수 없습니다.]");
				return -1;
			}
			
			String delimiter = StringUtil.getDirDelimiter(localPath);

			//sftpChannel.get(remote,localPath);
			if (localPath.endsWith(delimiter))localFileName = localPath+remoteFileName;
			else localFileName = localPath+delimiter+remoteFileName;
			logger.debug("[retrieve]localFileName  ------->" + localFileName);
			
			receiveFile = new File(localFileName);
			os = new FileOutputStream(receiveFile);
			
			if(bandWidthLimit > 0){
				/**
				 * 대역폭 콘트롤 bis 512bps -> 512/8=64bytes/second, (1Byte=8bit),
				 */
				//Buffer buffer = new Buffer(1024);
				//int bandWidth = 1024*8;
				long bufferSize = bandWidthLimit/8;
				byte[] bufferArr = new byte[(int)bufferSize];
				
				try {
						bos = new BufferedOutputStream(os);
						bis = new BufferedInputStream(channelSftp.get(remoteFileName));
						fileReceived = true;
						batchDoc.setIOStream(bis);
						
						int readCount = 0;
						int lootCnt = 0;
						
						long[] timeMillis = new long[2];
						timeMillis[0] = System.currentTimeMillis();
						
						long rtt = 0;//round trip time 1회 전송시간 millisecond
						long rttMax = 0;
						long rttMin = 0;
						double rttAve = 0;
						long rttSum = 0;
						
						long realBandWidth = 0;
						long realBandWidthMax = 0;
						long realBandWidthMin = 0;
						double realBandWidthAve = 0;
						long realBandWidthSum = 0;
						long readByteSum = 0;
						
						long sizeStartTime = 0;
						long sizePerSecond = 0;
						int sleepTimeCriteria = 990;

						while (true){
							lootCnt++;
							sleepTimeCriteria = 990; //여유 10milli
							
							//강제중지 설정시
							if (batchDoc.isUserCancel()){
								throw new Exception("사용자가 전송을 중지 했습니다.");
							}
							
							timeMillis[0] = System.currentTimeMillis();
							readCount = bis.read(bufferArr);
							timeMillis[1] = System.currentTimeMillis();
							if(readCount<=0) break;
							
							bos.write(bufferArr,0,readCount);
							
							readByteSum += readCount;
							batchDoc.setCurrIODataByte(readByteSum);
							
							if (sizePerSecond > 0){
								rtt = timeMillis[1] - sizeStartTime;
							} else {
								rtt = timeMillis[1] - timeMillis[0];
							}
							
							if(rtt<=0 ){
								//rtt=1;//local test시 0이 발생 divide by zero오류 발생, 방어코드
								if(sizeStartTime==0)sizeStartTime = timeMillis[0];
								sizePerSecond += readCount;
								
								if(sizePerSecond >= bufferArr.length){
									logger.debug("SFTPAdaptor]lootCnt["+(lootCnt)+"] rtt<=0 SLEEP 990 sec,  readCount["+readCount+"]sizePerSecond["+sizePerSecond+"]readByteSum["+readByteSum+"] rtt["+rtt+"] sizeStartTime[,"+sizeStartTime+"] bufferSize["+bufferSize+"]");
									Thread.sleep(990);
									sizePerSecond = 0;
									sizeStartTime = 0;
								} else {
									logger.debug("SFTPAdaptor]lootCnt["+(lootCnt)+"] rtt<=0 SLEEP 0 sec, readCount["+readCount+"]sizePerSecond["+sizePerSecond+"] readByteSum["+readByteSum+"] rtt["+rtt+"] sizeStartTime[,"+sizeStartTime+"] bufferSize["+bufferSize+"]");
								}
								continue;
							} 
							
							if ( readCount < bufferArr.length || sizePerSecond> 0){
								if(sizeStartTime==0)sizeStartTime = timeMillis[0];
								sizePerSecond += readCount;
								
								if(sizePerSecond>0 && sizePerSecond < bufferArr.length){
									logger.debug("SFTPAdaptor]lootCnt["+(lootCnt)+"] SLEEP 0 sec readCount["+readCount+"] sizePerSecond["+sizePerSecond+"] readByteSum["+readByteSum+"] rtt["+rtt+"] bandWidthLimit["+bandWidthLimit+"] realBandWidth["+realBandWidth+"] bufferSize["+bufferSize+"]");
									continue;			
								}
							}
							
							if (sizePerSecond > 0){
								realBandWidth = (long) (sizePerSecond * 8  * 1000 / rtt);
							} else {
								realBandWidth = (long) (bufferSize * 8  * 1000 / rtt);
							}
							
							//min, max, sum
							rttSum += rtt;
							if (rtt > rttMax)rttMax = rtt;
							else if (rtt < rttMin)rttMin = rtt;
							
							realBandWidthSum += realBandWidth;
							if(realBandWidth>realBandWidthMax)realBandWidthMax=realBandWidth;
							else if(realBandWidth<realBandWidthMin)realBandWidthMin=realBandWidth;
							
							if (sizePerSecond > 0 && sizePerSecond>bufferSize){
								sleepTimeCriteria = (int) (sleepTimeCriteria*sizePerSecond/bufferSize);								
							} 
							
							if(rtt < sleepTimeCriteria){ 
								logger.debug("SFTPAdaptor]lootCnt["+(lootCnt)+"] readCount["+readCount+"]  readByteSum["+readByteSum+"] sizePerSecond["+sizePerSecond+"] rtt["+rtt+"] bandWidthLimit["+bandWidthLimit+"] realBandWidth["+realBandWidth+"] bufferSize["+bufferSize+"]sleep["+(sleepTimeCriteria-rtt)+"]sleepTimeCriteria["+sleepTimeCriteria+"]");
								Thread.sleep((long) (sleepTimeCriteria-rtt));
							} else {
								logger.debug("SFTPAdaptor]lootCnt["+(lootCnt)+"] readCount["+readCount+"] readByteSum["+readByteSum+"] sizePerSecond["+sizePerSecond+"] rtt["+rtt+"] bandWidthLimit["+bandWidthLimit+"] realBandWidth["+realBandWidth+"] bufferSize["+bufferSize+"]sleep[0]sleepTimeCriteria["+sleepTimeCriteria+"]");
							}
							
							sizePerSecond = 0;
							sizeStartTime = 0;
						}
						
						realBandWidthAve = realBandWidthSum/lootCnt;
						rttAve = rttSum/lootCnt;
						
						logger.info("SFTPAdaptor] ####Statistics : bufferSize["+bufferSize
								+"] loopCnt["+lootCnt
								+"] readCountSum["+readByteSum
								+"] rttAve["+rttAve
								+"] rttMax["+rttMax
								+"] rttMin["+rttMin
								+"] bandWidthLimit["+bandWidthLimit
								+"] realBandWidthAve["+realBandWidthAve
								+"] realBandWidthMax["+realBandWidthMax
								+"] realBandWidthMin["+realBandWidthMin
								+"]");

				} finally {
					try {
						if (bos != null) {
							bos.flush();
							bos.close();
						}

						if (bis != null) bis.close();
						if (os != null) os.close();
					} catch (Throwable e) {
						logger.error("Closing Error:" + e.getMessage());
					}
				}
			} else {			
				channelSftp.get(remoteFileName, os);
				fileReceived = true;
				
				try {// 20250910 obl
					if (os != null) os.close();
				} catch (Throwable e) {
					logger.error("os Closing Error:" + e.getMessage());
				}
			}
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			throw e;
		} finally {
			batchDoc.setSftpChannel(null);
			if (!fileReceived && receiveFile != null && receiveFile.exists()) {
				try {
					Files.delete(receiveFile.toPath());
				} catch (Exception e) {
					logger.error("fail to delete {}", localFileName, e);
				}
			}
		}
		return 0;
	}
	
	/**
	 * 경로를 ls 한 결과를 얻는다.
	 * @param channelSftp
	 * @param remotePath
	 * @param remoteFileName
	 * @param logger
	 * @return
	 * @throws SftpException
	 */
	public static FTPFile[] lsJSCH(ChannelSftp channelSftp, String remotePath, String remoteFileName,
			Logger logger) throws SftpException {
		logger.debug("remotePath  --> {"+ remotePath+"}");
		logger.debug("remoteFileName  --> {"+ remoteFileName+"}");

		FTPFile[] files = null;
		Vector<LsEntry> list = channelSftp.ls(remotePath + remoteFileName);
		files = new FTPFile[list.size()];
		for (int inx = 0; inx < list.size(); inx++) {
			LsEntry info = list.get(inx);
			logger.debug("{"+"info.getFilename()"+"} " +"{"+"info.getAttrs().toString()"+"}");
			
			FTPFile file = new FTPFile();
			file.setName(info.getFilename());
			file.setSize(info.getAttrs().getSize());
			if (info.getAttrs().isReg())
				file.setType(FTPFile.FILE_TYPE);
			else if (info.getAttrs().isDir())
				file.setType(FTPFile.DIRECTORY_TYPE);
			else
				file.setType(FTPFile.UNKNOWN_TYPE);

			files[inx] = file;
		}
		return files;
	}

	/**
	 * lstat 결과를 얻는다.
	 * 
	 * @param channelSftp
	 * @param remotePath
	 * @param remoteFileName
	 * @return lstat 결과
	 * @throws SftpException
	 */
	public static SftpATTRS lstatJSCH(ChannelSftp channelSftp, String remotePath, String remoteFileName) throws SftpException {
		return channelSftp.lstat(remotePath + remoteFileName);
	}

	/**
	 * 경로를 remotePath로 변경한다.
	 * 
	 * @param channelSftp
	 * @param remotePath
	 * @throws SftpException
	 */
	public static void cdJSCH(ChannelSftp channelSftp, String remotePath) throws SftpException {
		channelSftp.cd(remotePath);
	}
	
	
	
	protected static FTPFileObject[] listFilesJSCH(String server, int port, String username, String password, String path, Logger logger, String authType) throws IOException {
		logger.debug("[SFHT connect]server  -->" + server);
		logger.debug("[SFHT connect]port    -->" + port);
		logger.debug("[SFHT connect]username-->" + username);
		logger.debug("[SFHT connect]password-->" + password);
		logger.debug("[SFHT listFiles2]path-->" + path);

		Session session = null;
		ChannelSftp sftpChannel = null;
		String[] serverList = server.split(",");
		try {

			for (int inx = 0; inx < serverList.length; inx++) {
				try {
					JSch jsch = new JSch();
					
					//JSch.setLogger(new SFTPLogger());
					
					session = jsch.getSession(username, serverList[inx], port);
					session.setPassword(password);
					session.setConfig("StrictHostKeyChecking", "no");
					session.setConfig("PreferredAuthentications","password");
					session.setTimeout(300 * 1000);
					session.connect();
					break;

				} catch (JSchException e) {
					if ( inx == serverList.length -1)
						throw e;
				}
			}
			sftpChannel = (ChannelSftp) session.openChannel("sftp");
			sftpChannel.connect(300 * 1000);
			
			try {
				sftpChannel.ls(path);
			}catch(SftpException e) {
				return null;
			}
			FTPFile[] files = null;
			
			@SuppressWarnings("unchecked")
			Vector<LsEntry> v = sftpChannel.ls(path);
			files = new FTPFile[v.size()];
			int inx = 0;
			for (Iterator<LsEntry> iterator = v.iterator(); iterator.hasNext();) {
				LsEntry object = (LsEntry) iterator.next();
				FTPFile file = new FTPFile();
				SftpATTRS att = object.getAttrs();
				file.setName(object.getFilename());
				if (!att.isDir() && !att.isLink()) 
					file.setType(FTPFile.FILE_TYPE);
				else if(att.isDir()) 
					file.setType(FTPFile.DIRECTORY_TYPE);
				else 
					file.setType(FTPFile.UNKNOWN_TYPE);
				
				file.setSize(att.getSize());
				Calendar cal = new GregorianCalendar();
				cal.setTime(new Date(att.getMTime()));
				files[inx++] = file;
			}
			
			ArrayList<FTPFileObject> oFiles = new ArrayList<FTPFileObject>();
			FTPFileObject[] rFiles = new FTPFileObject[files.length];
			FTPFileObject nFile;
			for(FTPFile file :files){
				nFile = new FTPFileObject();
				nFile.setFile(file.isFile());
				nFile.setName(file.getName());
				nFile.setSize(file.getSize());
				oFiles.add(nFile);
			}
			oFiles.toArray(rFiles);
			
			return rFiles;

		} catch (JSchException e) {
			logger.error(e.getMessage(), e);
			throw new IOException(e.getMessage());
		} catch (SftpException e) {
			logger.error(e.getMessage(), e);
			throw new IOException(e.getMessage());
		} finally {
			if (sftpChannel != null)
				sftpChannel.disconnect();
			if (session != null)
				session.disconnect();
		}

	}
}
