
package jp.riken.brain.ni.samuraigraph.application;

import java.awt.Component;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipOutputStream;

import javax.swing.JFileChooser;

import jp.riken.brain.ni.samuraigraph.base.SGDrawingWindow;
import jp.riken.brain.ni.samuraigraph.base.SGExtensionFileFilter;
import jp.riken.brain.ni.samuraigraph.base.SGFileChooser;
import jp.riken.brain.ni.samuraigraph.base.SGIConstants;


/**
 * Create an archive file.
 */
public class SGArchiveFileCreator
	implements SGIConstants, SGIArchiveFileConstants
{

	/**
	 * Current file path and name
	 */
	private String mCurrentFilePath;
	private String mCurrentFileName;
	
	/**
	 * Constant value of End of File
	 */
	protected static final int EOF = -1;
	
	
	/**
	 * Constructs an object.
	 *
	 */
	public SGArchiveFileCreator()
	{
		this.init();
	}
	
	private void init()
	{
		// set default directory name and file name of the property file
		String path = USER_HOME;
		String md = MY_DOCUMENTS; // for windows
		File home = new File( path );
		String[] fList = home.list();
		if( fList==null )
		{
			throw new Error();
		}
		// for windows
		for( int ii=0; ii<fList.length; ii++ )
		{
			if( fList[ii].endsWith(md) )
			{
				path += FILE_SEPARATOR + md;
				break;
			}
		}
		
		this.mCurrentFilePath = path;
		this.mCurrentFileName = DEFAULT_ARCHIVE_FILE_NAME + "." + ARCHIVE_FILE_EXTENSION;		
	}



	/**
	 * Create a dataset archvie file.
	 */
	public int create( final SGDrawingWindow wnd, final String rootdir )
	{
		// show a file chooser and get selected file
		File zfile = this.getArchiveFileFromFileChooser( wnd );
		if( zfile==null )
			return CANCEL_OPTION;
		
		return this._create( rootdir, zfile );
	}

	/**
	 * Create a dataset archive file.
	 */
	public int create( final SGDrawingWindow wnd, final String rootdir, final String pathName )
	{
		File zfile = new File( pathName );
		return this._create( rootdir, zfile );
	}

	//
	private int _create( final String rootdir, final File zfile )
	{
		final File root = new File(rootdir);
		if(! root.isDirectory() ) return -1;

		// get file list
		ArrayList flist = getFileList( root );
		if (flist.size() == 0)
			return -2;

		final String rootname = root.getAbsolutePath() + File.separator;
		
		ByteArrayOutputStream baos = null;
		ZipOutputStream zos = null;

		FileOutputStream fos = null;
		BufferedOutputStream bos = null;
		
		try {
			// check file size 
			baos = new ByteArrayOutputStream();
			zos = new ZipOutputStream( baos );

			ArrayList zental = new ArrayList(); // array list of ZipEntry
			for(int ii=0; ii<flist.size(); ii++){
				File file = (File)flist.get(ii);
				String fname = file.getAbsolutePath();
				String entryname = fname.substring( rootname.length() );
				ZipEntry zent = new ZipEntry( entryname );
				zental.add( zent );
				writeFileEntry( zos, file, zent );
			}
			zos.close();
			baos.close();
			
			// create zip file image
			baos = new ByteArrayOutputStream();
			zos = new ZipOutputStream( baos );
			
			for( int ii=0; ii<flist.size(); ii++ ) {
				File file = (File)flist.get(ii);
				ZipEntry zent = (ZipEntry) zental.get(ii);
				writeFileEntry( zos, file, zent );
			}
			zos.finish();
			
			// get compressed data and write zip file
			byte [] bufResult = baos.toByteArray();
			fos = new FileOutputStream( zfile );
			bos = new BufferedOutputStream( fos );
			bos.write( bufResult, 0, bufResult.length );
		}
		catch( FileNotFoundException ex ) {
			ex.printStackTrace();
			return -1;
		}
		catch( ZipException ex ) {
			ex.printStackTrace();
			return -1;			
		}
		catch( IOException ex ) {
			ex.printStackTrace();
			return -1;			
		}
		finally {
			try {
				zos.close();
			} catch( Exception e ) {
			}
			try {
				baos.close();
			} catch( Exception e ) {
			}
			try {
				bos.close();
			} catch( Exception e ) {
			}
			try {
				fos.close();
			} catch( Exception e ) {
			}
		}
		return OK_OPTION;
	}
	
	// get file bytes
	private static byte[] _getFileBytes( final File file )
	{
		FileInputStream fis = null;
		BufferedInputStream bis = null;
		try {
			int len = (int)file.length();
			fis = new FileInputStream( file );
			bis = new BufferedInputStream( fis, len );
			byte buf[] = new byte[len];
			bis.read( buf, 0, len );
			return buf;
		} catch ( IOException ex ) {
			ex.printStackTrace();
		} finally {
			try {
				if ( bis != null ) {
					bis.close();
				}
			} catch ( Exception ex ) {
				ex.printStackTrace();
			}
			try {
				if (fis != null ) {
					fis.close();
				}
			} catch ( Exception ex ) {
				ex.printStackTrace();
			}
		}
		return null;
	}



	/**
	 * 
	 * @param wnd
	 * @return
	 */
	private File getArchiveFileFromFileChooser( final Component parent )
	{
		JFileChooser chooser = new SGFileChooser();

		// set the file extension filter
		SGExtensionFileFilter ff = new SGExtensionFileFilter();
		ff.setDescription( ARCHIVE_FILE_DESCRIPTION );
		ff.addExtension( ARCHIVE_FILE_EXTENSION );
		chooser.setFileFilter(ff);
		
		// set current directory
		chooser.setCurrentDirectory( new File(this.mCurrentFilePath) );

		// create the full path name
		String path = this.mCurrentFilePath + FILE_SEPARATOR + this.mCurrentFileName;
		File f = new File(path);

		// set selected file
		chooser.setSelectedFile(f);

		// show save dialog
		final int ret = chooser.showSaveDialog(parent);

		File file = null;
		if( ret == JFileChooser.APPROVE_OPTION )
		{
			file = chooser.getSelectedFile();
			this.mCurrentFilePath = file.getParent();
			this.mCurrentFileName = file.getName();
		}

		return file;
	}


	/**
	 * 
	 * @return
	 */
	public File getSelectedFile()
	{
		return new File( this.mCurrentFilePath, this.mCurrentFileName);
	}


	/**
	 * Set the file name to the file chooser.
	 * @param dir - directory name
	 * @param name - file name
	 * @return a File object
	 */
	public File setFile( final String dir, String name )
	{
		if( name!=null )
		{
			this.mCurrentFileName = name;
		}
		this.mCurrentFilePath = dir;
		
		// create the full path name
		String path = this.mCurrentFilePath + FILE_SEPARATOR + this.mCurrentFileName;
		File f = new File(path);

		return f;
	}

	
	/**
	 * Get the target file list
	 * @param dirname : root directory 
	 */
	private static ArrayList _getDirFileList( final String dirname )
	{
		ArrayList list = new ArrayList();
		File dir = new File(dirname);
		if(!dir.isDirectory())	return list;
		File [] files = dir.listFiles();
		for(int ii=0; ii<files.length; ii++){
			if (files[ii].isFile())
				list.add(files[ii]);
			else if(files[ii].isDirectory()){
				ArrayList sublist = _getDirFileList( dirname + File.separator + files[ii].getName() );
				list.addAll(sublist);
			}
		}
		return list;
	}
	
	/**
	 * Get the correct target file list
	 * @param dir : root directory 
	 */
	private static ArrayList getFileList( final File dir )
	{
		String absdirname = dir.getAbsolutePath();
		ArrayList list = new ArrayList();
		// get file list
		ArrayList flist = _getDirFileList( absdirname );
		// check file list
		for( int ii=0; ii<flist.size(); ii++ ) {
			File file = (File) flist.get( ii );
			String absfname = file.getAbsolutePath();
			if( !file.isFile() )
				continue; // this is not file
			if(!absfname.startsWith( absdirname ))
				continue; // this file not exist in rootdir
			file = new File( absfname );
			list.add( file );
		}
		return list;
	}
	
	/**
	 * Write the file and zip entry to zip stream.
	 * @param zos : archive file output stream 
	 * @param file : target file
	 * @param zentry : Zip Entry
	 * @throws ZipException
	 * @throws IOException	 */
 	private static void writeFileEntry(final ZipOutputStream zos, final File file, final ZipEntry zentry )
		throws ZipException,IOException
	{
		byte[] buf = _getFileBytes( file );
 		zos.putNextEntry( zentry );  // start writing target file
 		zos.write( buf, 0, buf.length );
		zos.closeEntry();  // end of writing target file
	}
 	
}