
package kcg.imis.cmmn.egov.file;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import kcg.imis.cmmn.CommConstants;
import kcg.imis.cmmn.egov.util.EgovStringUtil;
import kcg.imis.cmmn.egov.util.EgovWebUtil;
import kcg.imis.cmmn.egov.vo.FileVO;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.multipart.MultipartFile;

import egovframework.rte.fdl.property.EgovPropertyService;

/**
 * @Class Name : EgovFileMngUtil.java
 * @Description : 메시지 처리 관련 유틸리티
 * @Modification Information
 *
 *               수정일 수정자 수정내용 ------- -------- ---------------------------
 *               2009.02.13 이삼섭 최초 생성 2011.08.31 JJY 경량환경 템플릿 커스터마이징버전 생성
 *               2016.09.22 임새미 라인 83~85, 126~136 2016.10.21 하영지 동적으로 폴더를 생성하는
 *               경우가 있어서 폴더명 파라미터 추가parseFileInf Override, deleteFile Override
 *
 * @author 공통 서비스 개발팀 이삼섭
 * @since 2009. 02. 13
 * @version 1.0
 * @see
 *
 */
@Component("EgovFileMngUtil")
public class EgovFileMngUtil {

	public static final int BUFF_SIZE = 2048;

	@Resource(name = "propertiesService")
	protected EgovPropertyService propertyService;

	private static final Logger LOGGER = LoggerFactory.getLogger(EgovFileMngUtil.class);

	/**
	 * 파일이 존재하면 규칙에 의해 파일명을 수정한다.
	 * <pre>
	 * 규칙 : {파일명}_{숫자}.{확장자}
	 * </pre>
	 * @param filePath 파일경로
	 * @param fileNameExt (확장자를 포함한) 파일명
	 * @return 파일명
	 */
	public String makeNewFileName(final String filePath, final String fileNameExt) {
		int idx = 0;
		String fileExt = "";
		String fileName = "";
		String newFileName = "";
		String filePathName = filePath + fileNameExt;
		String newFileNameExt = fileNameExt;

		if (!StringUtils.isBlank(filePathName)) {
			fileExt = FilenameUtils.getExtension(fileNameExt);
			fileName = FilenameUtils.getName(fileNameExt);
			fileName = FilenameUtils.removeExtension(fileName);
			newFileName = fileName;
			while (true) {
				File f = new File(filePathName);
				if (f.isFile()) {
					newFileName = String.format("%s_%d", fileName, ++idx);
					filePathName = String.format("%s%s.%s", filePath, newFileName, fileExt);
				} else {
					break;
				}
			}

			newFileNameExt = newFileName;
			if (!StringUtils.isBlank(fileExt)) {
				newFileNameExt += "." + fileExt;
			}

		}

		return newFileNameExt;
	}

	/**
	 * 파일을 저장하고 저장된 파일정보 목록을 취득한다.
	 * @param files 파일객체
	 * @param keyStr 구분 값
	 * @param fileKeyParam 파일순번
	 * @param atchFileId 파일ID
	 * @param storePath 저장경로
	 * @return 저장된 파일정보 List
	 * @throws Exception 기본 예외 처리
	 */
	public List<FileVO> parseFileInf(final Map<String, MultipartFile> files, final String keyStr, final int fileKeyParam, final String atchFileId, final String storePath) throws Exception {
		return parseFileInf(files, keyStr, fileKeyParam, atchFileId, storePath, null);
	}

	/**
	 * 첨부파일을 서버에 저장한다.
	 *
	 * @param file
	 * @param newName
	 * @param stordFilePath
	 * @throws Exception
	 */
	protected void writeUploadedFile(MultipartFile file, String newName, String stordFilePath) throws Exception {
		InputStream stream = null;
		OutputStream bos = null;
		String stordFilePathReal = (stordFilePath == null ? "" : stordFilePath).replaceAll("..", "");
		try {
			stream = file.getInputStream();
			File cFile = new File(stordFilePathReal);

			if (!cFile.isDirectory()) {
				boolean flag = cFile.mkdir();
				if (!flag) {
					throw new IOException("Directory creation Failed ");
				}
			}

			bos = new FileOutputStream(stordFilePathReal + File.separator + newName);

			int bytesRead = 0;
			byte[] buffer = new byte[BUFF_SIZE];

			while ((bytesRead = stream.read(buffer, 0, BUFF_SIZE)) != -1) {
				bos.write(buffer, 0, bytesRead);
			}
		} catch (FileNotFoundException fnfe) {
			LOGGER.debug("fnfe: {}", fnfe);
		} catch (IOException ioe) {
			LOGGER.debug("ioe: {}", ioe);
		} catch (Exception e) {
			LOGGER.debug("e: {}", e);
		} finally {
			if (bos != null) {
				try {
					bos.close();
				} catch (Exception ignore) {
					LOGGER.debug("IGNORED: {}", ignore.getMessage());
				}
			}
			if (stream != null) {
				try {
					stream.close();
				} catch (Exception ignore) {
					LOGGER.debug("IGNORED: {}", ignore.getMessage());
				}
			}
		}
	}

	/**
	 * 서버의 파일을 다운로드한다.
	 *
	 * @param request
	 * @param response
	 * @throws Exception
	 */
	public static void downFile(HttpServletRequest request, HttpServletResponse response) throws Exception {

		String downFileName = EgovStringUtil.isNullToString(request.getAttribute("downFile")).replaceAll("..", "");
		String orgFileName = EgovStringUtil.isNullToString(request.getAttribute("orgFileName")).replaceAll("..", "");

		/*
		 * if ((String)request.getAttribute("downFile") == null) { downFileName
		 * = ""; } else { downFileName =
		 * EgovStringUtil.isNullToString(request.getAttribute("downFile")); }
		 */

		/*
		 * if ((String)request.getAttribute("orgFileName") == null) {
		 * orgFileName = ""; } else { orgFileName =
		 * (String)request.getAttribute("orginFile"); }
		 */

		File file = new File(downFileName);

		if (!file.exists()) {
			throw new FileNotFoundException(downFileName);
		}

		if (!file.isFile()) {
			throw new FileNotFoundException(downFileName);
		}

		byte[] b = new byte[BUFF_SIZE]; // buffer size 2K.
		String fName = (new String(orgFileName.getBytes(), "UTF-8")).replaceAll("\r\n", "");
		response.setContentType("application/x-msdownload");
		response.setHeader("Content-Disposition:", "attachment; filename=" + fName);
		response.setHeader("Content-Transfer-Encoding", "binary");
		response.setHeader("Pragma", "no-cache");
		response.setHeader("Expires", "0");

		BufferedInputStream fin = null;
		BufferedOutputStream outs = null;

		try {
			fin = new BufferedInputStream(new FileInputStream(file));
			outs = new BufferedOutputStream(response.getOutputStream());
			int read = 0;

			while ((read = fin.read(b)) != -1) {
				outs.write(b, 0, read);
			}
		} finally {
			if (outs != null) {
				try {
					outs.close();
				} catch (Exception ignore) {
					LOGGER.debug("IGNORED: {}", ignore.getMessage());
				}
			}
			if (fin != null) {
				try {
					fin.close();
				} catch (Exception ignore) {
					LOGGER.debug("IGNORED: {}", ignore.getMessage());
				}
			}
		}
	}


	/**
	 * 파일을 실제 물리적인 경로에 생성한다.
	 *
	 * @param file
	 * @param newName
	 * @param stordFilePath
	 * @throws Exception
	 */
	protected static void writeFile(MultipartFile file, String newName, String stordFilePath) throws Exception {
		InputStream stream = null;
		OutputStream bos = null;
		newName = EgovWebUtil.filePathBlackList(newName);
		stordFilePath = EgovWebUtil.filePathBlackList(stordFilePath);
		try {
			stream = file.getInputStream();
			File cFile = new File(stordFilePath);

			if (!cFile.isDirectory()) {
				cFile.mkdir();
			}

			bos = new FileOutputStream(stordFilePath + File.separator + newName);

			int bytesRead = 0;
			byte[] buffer = new byte[BUFF_SIZE];

			while ((bytesRead = stream.read(buffer, 0, BUFF_SIZE)) != -1) {
				bos.write(buffer, 0, bytesRead);
			}
		} catch (FileNotFoundException fnfe) {
			LOGGER.debug("fnfe: {}", fnfe);
		} catch (IOException ioe) {
			LOGGER.debug("ioe: {}", ioe);
		} catch (Exception e) {
			LOGGER.debug("e: {}", e);
		} finally {
			if (bos != null) {
				try {
					bos.close();
				} catch (Exception ignore) {
					LOGGER.debug("IGNORED: {}", ignore.getMessage());
				}
			}
			if (stream != null) {
				try {
					stream.close();
				} catch (Exception ignore) {
					LOGGER.debug("IGNORED: {}", ignore.getMessage());
				}
			}
		}
	}

	/**
	 * 서버 파일에 대하여 다운로드를 처리한다.
	 *
	 * @param response
	 * @param streFileNm : 파일저장 경로가 포함된 형태
	 * @param orignFileNm
	 * @throws Exception
	 */
	public void downFile(HttpServletResponse response, String streFileNm, String orignFileNm) throws Exception {
		// String downFileName =
		// EgovStringUtil.isNullToString(request.getAttribute("downFile")).replaceAll("..","");
		// String orgFileName =
		// EgovStringUtil.isNullToString(request.getAttribute("orgFileName")).replaceAll("..","");
		String downFileName = EgovStringUtil.isNullToString(streFileNm).replaceAll("..", "");
		String orgFileName = EgovStringUtil.isNullToString(orignFileNm).replaceAll("..", "");

		File file = new File(downFileName);
		// log.debug(this.getClass().getName()+" downFile downFileName "+downFileName);
		// log.debug(this.getClass().getName()+" downFile orgFileName "+orgFileName);

		if (!file.exists()) {
			throw new FileNotFoundException(downFileName);
		}

		if (!file.isFile()) {
			throw new FileNotFoundException(downFileName);
		}

		// byte[] b = new byte[BUFF_SIZE]; //buffer size 2K.
		int fSize = (int) file.length();
		if (fSize > 0) {
			BufferedInputStream in = null;

			try {
				in = new BufferedInputStream(new FileInputStream(file));

				String mimetype = "text/html"; // "application/x-msdownload"

				response.setBufferSize(fSize);
				response.setContentType(mimetype);
				response.setHeader("Content-Disposition:", "attachment; filename=" + orgFileName);
				response.setContentLength(fSize);
				// response.setHeader("Content-Transfer-Encoding","binary");
				// response.setHeader("Pragma","no-cache");
				// response.setHeader("Expires","0");
				FileCopyUtils.copy(in, response.getOutputStream());
			} finally {
				if (in != null) {
					try {
						in.close();
					} catch (Exception ignore) {
						LOGGER.debug("IGNORED: {}", ignore.getMessage());
					}
				}
			}
			response.getOutputStream().flush();
			response.getOutputStream().close();
		}

	}

	/**
	 * 곹오컴포넌트 EgovFileTool 일부 발췌&변경
	 * <pre>
	 * Comment : 파일을 삭제한다.
	 * </pre>
	 * @param savedFile 삭제할 파일명
	 * @param storePath 파일위치
	 * @return 성공하면 삭제된 파일의 절대경로, 아니면블랭크
	 */
	public String deleteFile(final String savedFile, final String storePath) {
		if (StringUtils.isBlank(savedFile)) {
			return "";
		}

		String storePathString = "";
		if (StringUtils.isBlank(storePath)) {
			storePathString = propertyService.getString("Globals.fileStorePath");
		} else {
			storePathString = propertyService.getString(storePath);
		}
		String fileDeletePath = storePathString + File.separator + savedFile;
		// 인자값 유효하지 않은 경우 블랭크 리턴
		if (fileDeletePath == null || fileDeletePath.equals("")) {
			return "";
		}
		String result = "";
		File file = new File(EgovWebUtil.filePathBlackList(fileDeletePath));
		if (file.isFile()) {
			result = deletePath(file);
		} else {
			result = "";
		}

		return result;
	}

	private String deletePath(File file) {
		String result = "";
		if (file.exists()) {
			result = file.getAbsolutePath();
			if (!file.delete()) {
				result = "";
			}
		}
		return result;
	}

	/**
	 * 파일을 저장하고 저장된 파일정보 목록을 취득한다.
	 * @param files 파일객체
	 * @param keyStr 구분 값
	 * @param fileKeyParam 파일순번
	 * @param atchFileId 파일ID
	 * @param storePath저장경로
	 * @param folderNm 폴더명
	 * @return 저장된 파일정보 List
	 * @throws Exception 기본 예외 처리
	 */
	public List<FileVO> parseFileInf(final Map<String, MultipartFile> files, final String keyStr, final int fileKeyParam, final String atchFileId, final String storePath, String folderNm)
			throws Exception {
		int fileKey = fileKeyParam;

		String baseStorePathString = "";
		String atchFileIdString = "";

		if (StringUtils.isBlank(storePath)) {
			baseStorePathString = propertyService.getString(storePath, "Globals.fileStorePath");
		} else {
			baseStorePathString = propertyService.getString(storePath);
		}


		if (StringUtils.isBlank(atchFileId)) {
			atchFileIdString = atchFileId;
		} else {
			atchFileIdString = atchFileId;
		}

		List<FileVO> result = new ArrayList<FileVO>();
		if (files != null) {
			Iterator<Entry<String, MultipartFile>> itr = files.entrySet().iterator();
			MultipartFile file;
			String filePath = "";
			FileVO fvo;
			while (itr.hasNext()) {
				Entry<String, MultipartFile> entry = itr.next();
				file = entry.getValue();
				String storePathString = "";
				
				if(folderNm != null){
					
					String id = file.getName();
					if(id.startsWith(CommConstants.FILE_TYPE_IMG))
						folderNm = CommConstants.FILE_TYPE_IMG;
					else if(id.startsWith(CommConstants.FILE_TYPE_DOC)) 
						folderNm = CommConstants.FILE_TYPE_DOC;
					
					storePathString = baseStorePathString + folderNm + File.separator;
					
					File saveFolder = new File(storePathString);
					
					if (!saveFolder.exists() || saveFolder.isFile()) {
						saveFolder.mkdirs();
					}
				} else {
					
					storePathString = baseStorePathString + CommConstants.FILE_TYPE_DOC + File.separator;
					
					File saveFolder = new File(storePathString);
						
					if (!saveFolder.exists() || saveFolder.isFile()) {
						saveFolder.mkdirs();
					}
					
				}
				
				String orginFileName = file.getOriginalFilename();

				// --------------------------------------
				// 원 파일명이 없는 경우 처리
				// (첨부가 되지 않은 input file type)
				// --------------------------------------
				if ("".equals(orginFileName)) {
					continue;
				}
				// //------------------------------------

				int index = orginFileName.lastIndexOf(".");
				String fileExt = orginFileName.substring(index + 1);
				String newName = orginFileName;
				// KeyStr이 없으면 원래 파일명을 사용한다. by KNM
				if (!StringUtils.isBlank(keyStr)) {
					newName = keyStr + EgovStringUtil.getTimeStamp() + fileKey;
				}

				long fileSize = file.getSize();
				// 같은 경로에 파일이 존재하면 파일 명을 수정한다.
				String tempFileName = makeNewFileName(storePathString, newName);
				if (!newName.equalsIgnoreCase(tempFileName)) {
					newName = tempFileName;
				}

				if (StringUtils.isNotBlank(orginFileName)) {
					filePath = storePathString + File.separator + newName;
					file.transferTo(new File(filePath));
				}

				fvo = new FileVO();
				fvo.setFileExtsn(fileExt);
				fvo.setFileStreCours(storePathString);
				fvo.setFileMg(Long.toString(fileSize));
				fvo.setOrignlFileNm(orginFileName);
				fvo.setStreFileNm(newName);
				fvo.setAtchFileId(atchFileIdString);
				fvo.setFileSn(entry.getKey());

				writeFile(file, newName, storePathString);
				result.add(fvo);

				fileKey++;
			}
		}

		return result;
	}

	/**
	 * 공통컴포넌트 EgovFileTool 일부 발췌&변경
	 * 
	 * <pre>
	 * Comment : 파일을 삭제한다.
	 * </pre>
	 * 
	 * @param savedFile
	 *            삭제할 파일명
	 * @param storePath
	 *            루트 디렉토리
	 * @param folderNm
	 *            폴더명
	 * @return 성공하면 삭제된 파일의 절대경로, 아니면블랭크
	 */
	public String deleteFile(final String savedFile, final String storePath, final String folderNm) {
		if (StringUtils.isBlank(savedFile)) {
			return "";
		}

		String storePathString = "";
		if (StringUtils.isBlank(storePath)) {
			storePathString = propertyService.getString("Globals.fileStorePath");
		} else {
			storePathString = propertyService.getString(storePath);
		}

		String fileDeletePath = storePathString + File.separator + folderNm + File.separator + savedFile;
		// 인자값 유효하지 않은 경우 블랭크 리턴
		if (fileDeletePath == null || fileDeletePath.equals("")) {
			return "";
		}
		String result = "";
		File file = new File(EgovWebUtil.filePathBlackList(fileDeletePath));
		if (file.isFile()) {
			result = deletePath(file);
		} else {
			result = "";
		}

		return result;
	}

}
