package kr.co.udapsoft.ebid.buyer.common.file;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import net.sf.jazzlib.ZipEntry;
import net.sf.jazzlib.ZipFile;
import net.sf.jazzlib.ZipOutputStream;

public class ZipUtils2 {
	public ZipUtils2() {}

	private String realZipFileName;

	/**
	* ZipFile로 압축한다.
	* 
	* @param zipFileName ZIP파일명
	* @param zipFileDirectory ZIP으로 압축할 디렉토리 
	*/
	public void makeZipFile(String zipFileName, String zipFileDirectory) throws Exception{
		File zipFile = new File(zipFileDirectory);
	
		if (!zipFile.exists())throw new Exception("백업할 파일을 찾지 못했습니다.");
		realZipFileName = zipFileName.substring(zipFileName.lastIndexOf("/") + 1);
		makeZipFile(zipFileName, zipFile);
	}

	/**
	* ZipFile로 압축한다.
	* 
	* @param zipFileName ZIP파일명
	* @param zipFileDirectory ZIP으로 압축할 디렉토리 
	*/ 
	public void makeZipFile(String zipFileName, File zipFileDirectory)throws Exception{
		boolean exception = false;
		FileOutputStream fos = null;
		ZipOutputStream zos = null;
		String directoryName = zipFileDirectory.getPath()+"/";
		try {
	
			fos = new FileOutputStream(directoryName + zipFileName);
			zos = new ZipOutputStream(fos);
		
			if (zipFileDirectory.isFile()){
				addFile(zos, zipFileDirectory, "", true);
			}
		
			if (zipFileDirectory.isDirectory()){
				addDirectory(zos, zipFileDirectory, "");
			}
			if (zos != null) zos.close();
			if (fos != null) fos.close();
		}catch (Exception e){
			e.printStackTrace();
			exception = true;
		}finally{
			if (exception) throw new Exception("백업에 실패했습니다.");
		}
	}

	/**
	* ZipFile로 압축한다.
	* 
	* @param zos ZIP파일을 기록하는 OutputStream
	* @param zipFile ZIP에 등록할 Entry
	* @param parentDirectory ZIP파일 내부의 상대경로
	* @param file Entry가 file인지 directory인지 구분
	*/ 
	private void addFile(ZipOutputStream zos, File zipFile, String parentDirectory, boolean file) throws Exception{
		ZipEntry entry = null;
		String parentPath = (parentDirectory.equals("")) ? "" : parentDirectory + "/";
	
		if (!file){
			entry = new ZipEntry(parentPath + zipFile.getName() + "/");
			zos.putNextEntry(entry);
		}else{
			entry = new ZipEntry(parentPath + zipFile.getName());
			zos.putNextEntry(entry);
		
			boolean exception = false;
		
			FileInputStream fis = null;
		
			try {
				byte[] fileContents = new byte[1024];
				int readLength = 0;
				fis = new FileInputStream(zipFile);
			
				while ((readLength = fis.read(fileContents)) != -1){
					if (readLength != 1024){
						byte[] realFileContents = new byte[readLength];
						System.arraycopy(fileContents, 0, realFileContents, 0, readLength);
						zos.write(realFileContents);
					}else{
						zos.write(fileContents);
					}
				}
			}catch (Exception e){
				exception = true;
			}finally{
				if (fis != null) fis.close();
				if (exception) throw new Exception("백업에 실패했습니다.");
			}
		}
	}

	/**
	* 디렉토리를 재귀적으로 호출하면서 하위 파일들을 압축한다.
	* 
	* @param zos ZIP파일을 기록하는 OutputStream
	* @param zipFile ZIP에 등록할 Entry
	* @param parentDirectory ZIP파일 내부의 상대경로
	* @throws Exception
	*/
	private void addDirectory(ZipOutputStream zos, File zipFile, String parentDirectory) throws Exception
	{
		File[] files = zipFile.listFiles();
		String parentPath = parentDirectory;

		for (int i = 0; i < files.length; i++) 
		{
			if (files[i].isFile())
			{
				if (!files[i].getName().equals(realZipFileName)) 
				addFile(zos, files[i], parentPath, true);
			}
			else 
			{
				if (!parentPath.equals("")) {
					if (parentPath.endsWith("/")){
						addDirectory(zos, files[i], parentPath + files[i].getName() + "/");
					}else{
						addDirectory(zos, files[i], parentPath + "/" + files[i].getName() + "/");
					}
				} else{ 
					addDirectory(zos, files[i], files[i].getName() + "/");
				}
			}
		}
	}

	/**
	* ZIP파일의 압축을 푼다.
	* 
	* @param zipFileName ZIP파일명
	* @param targetDirectoryName 압축을 풀 디렉토리명
	* @throws Exception 
	*/
	public void unZipFile(String zipFileName, String targetDirectoryName) throws Exception{
		this.unZipFile(zipFileName, targetDirectoryName,null);
	}

	/**
	* 엔트리에 들어있는 개별 파일의 압축을 푼다.
	* 
	* @param compressEntity Zip파일 Entry
	* @param compreeFile Zip파일
	* @throws Exception
	*/
	private void unCompressZipFile(ZipEntry compressEntry,ZipFile compressFile,File targetDirectory) throws Exception{
		this.unCompressZipFile(compressEntry, compressFile, targetDirectory, null);
	}

	/**
	* ZIP파일과 타겟 디렉토리를 검증한다.
	* 타겟디렉토리가 없을 경우, 새로운 디렉토리를 만들어 준다.
	* 
	* @param zipFile ZIP파일
	* @param targetDirectory 압푹을 풀 디렉토리
	*/
	private void checkUnZipFile(File zipFile, File targetDirectory) throws Exception {
		if (zipFile.exists() && zipFile.canRead()) {
			if (!targetDirectory.exists()) {
				if (!targetDirectory.mkdirs()){
					throw new Exception("타겟 디렉토리 작성에 실패했습니다.");
				}
			} else if (!targetDirectory.canWrite()){
				throw new Exception("타겟 디렉토리를 사용할 수 있는 권한이 없습니다.");
			}
		} else {
			throw new Exception("ZIP파일이 존재하지 않습니다.");
		}
	}
	
	public void unZipFile(String zipFileName, String targetDirectoryName,List rfileList) throws Exception{
		File zipFile = new File(zipFileName);
		File targetDirectory = new File(targetDirectoryName);
		ArrayList errorMessage = new ArrayList();
		checkUnZipFile(zipFile, targetDirectory);
		ZipFile compressFile =null;
		
		try {
			compressFile = new ZipFile(zipFileName);
			
			Enumeration compressEntrys = compressFile.entries();
			while (compressEntrys.hasMoreElements()) {
				ZipEntry compressEntry = (ZipEntry) compressEntrys.nextElement();
			
				if (compressEntry.getCrc() > 0 || compressEntry.isDirectory() || compressEntry.getSize() == 0){
					unCompressZipFile(compressEntry, compressFile,targetDirectory,rfileList); 
				}else{
					throw new Exception(zipFileName + " CRC ERROR");
				}
			}
		}catch (Exception e){
			throw new Exception(e.getMessage());
		}finally{
			if(compressFile != null)try{compressFile.close();}catch(Exception iex){}
		}
	}

	private void unCompressZipFile(ZipEntry compressEntry,ZipFile compressFile,File targetDirectory,List rfileList) throws Exception{

		BufferedOutputStream fileOut = null;
		BufferedInputStream fileIn = null;
		File unCompressFile = new File(targetDirectory.getAbsoluteFile() + "/" + (new String(compressEntry.getName().getBytes("8859_1"),"KSC5601")));
		
		try{
			if (compressEntry.isDirectory()){
				if (!unCompressFile.mkdirs()){
					throw new Exception(compressEntry.getName() + "의 압축을 푸는 데 실패했습니다.");
				}
			}else{
				if (!unCompressFile.getParentFile().exists()){
					if (!unCompressFile.getParentFile().mkdirs()){
						throw new Exception(compressEntry.getName() +"의 압축을 푸는 데 실패했습니다.");
					}
				}
			
				fileOut = new BufferedOutputStream(new FileOutputStream(unCompressFile));
				fileIn = new BufferedInputStream(compressFile.getInputStream(compressEntry));
			
				byte[] fileContents = new byte[1024];
				int readLength = 0;
			
				while ((readLength = fileIn.read(fileContents)) != -1) {
					fileOut.write(fileContents, 0, readLength);
				}
				if(rfileList!=null) rfileList.add(unCompressFile);
			}
		} catch (Exception e) {
			throw new Exception(e.getMessage());
		} finally {
			try {
				if (fileIn != null) fileIn.close();
				if (fileOut != null) fileOut.close();
			} catch (Exception e) {}
		}
	}

}
