/*******************************************************************************
 * This file is part of GECAMed.
 * 
 * GECAMed is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License (L-GPL) as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * GECAMed is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License (L-GPL)
 * along with GECAMed.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * GECAMed is Copyrighted by the Centre de Recherche Public Henri Tudor (http://www.tudor.lu)
 * (c) CRP Henri Tudor, Luxembourg, 2008
 *******************************************************************************/
package lu.tudor.santec.gecamed.core.gui.utils;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.Vector;

import lu.tudor.santec.gecamed.core.ejb.session.beans.ChunkedFileManagerBean;
import lu.tudor.santec.gecamed.core.ejb.session.interfaces.ChunkedFileManager;
import lu.tudor.santec.gecamed.core.utils.ManagerFactory;

import org.apache.log4j.Logger;

/**
 * Class to write files to the server. It uses chunked writing to also transfer large files
 * 
 * 
 * @author martin.heinemann@tudor.lu
 * 24.07.2008
 * 11:25:18
 *
 *
 * @version
 * <br>$Log: ChunkedFileSender.java,v $
 * <br>Revision 1.7  2013-12-27 18:09:26  donak
 * <br>Cleanup of imports
 * <br>
 * <br>Revision 1.6  2013-07-15 06:18:34  ferring
 * <br>logging changed
 * <br>
 * <br>Revision 1.5  2013-02-22 07:34:06  kutscheid
 * <br>remove filesize from the xml as it is anyway read from the file itself
 * <br>store patient files to a zip file and read form this file again when importing
 * <br>
 * <br>Revision 1.4  2013-02-21 14:29:57  kutscheid
 * <br>fixes for the export and import of patients
 * <br>added zipping up of a patient's files
 * <br>
 * <br>Revision 1.3  2008-10-13 09:26:47  heinemann
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.2  2008-09-25 09:43:06  heinemann
 * <br>fixed copyrights
 * <br>
 * <br>Revision 1.1  2008-07-25 14:42:42  heinemann
 * <br>*** empty log message ***
 * <br>
 *   
 */
public class ChunkedFileSender {

	/**
	 * Chunk size, 1 MB
	 */
	private static final int CHUNKSIZE = 1000000; 

	/** the logger Object for this class */
	private static Logger logger = Logger.getLogger(ChunkedFileSender.class.getName());
	
	
	private InputStream input;
	private String fileName;
	private Integer patientId;
	private String  newFilename = null;

    private Vector<IChangeListener> listeners = new Vector<IChangeListener>();

    private int progress = 0;

    
    /**
     * @param file the file, will be copied to a temp file
     * @throws FileNotFoundException If the given file could not be found
     */
    public ChunkedFileSender(File file) throws FileNotFoundException {
        /* ================================================== */
        this(file, null);
        /* ================================================== */
    }
    
    
	/**
	 * @param file The file to store on the server
	 * @param patientId of the patient that owns the file
	 * @throws FileNotFoundException If the given file could not be found
	 */
	public ChunkedFileSender(File file, Integer patientId) throws FileNotFoundException {
		/* ================================================== */
		this(new FileInputStream(file), file.getName(), patientId);
		/* ================================================== */
	}
	
	/**
	 * @param inputStream The InputStream to store on the server
	 * @param patientId of the patient that owns the file
	 * @param fileName The filename of the file to be transferred (the display name for the content of the input stream)
	 */
	public ChunkedFileSender(InputStream inputStream, String fileName, Integer patientId) {
		/* ================================================== */
		this.input = inputStream;
		this.fileName = fileName;
		this.patientId = patientId;
		/* ================================================== */
	}
	
	public void startSending() throws Exception {
		/* ================================================== */
		try {
			/* ------------------------------------------------------- */
			// open an Buffered inputstream
			/* ------------------------------------------------------- */
			BufferedInputStream bin = new BufferedInputStream(input);
			
			byte[] b = new byte[CHUNKSIZE];
			/* ------------------------------------------------------- */
			// open the connection to the stateful session bean
			/* ------------------------------------------------------- */
			ChunkedFileManager chunkManager = (ChunkedFileManager) ManagerFactory.getStatefulRemote(ChunkedFileManagerBean.class);
			
			chunkManager.createFile(patientId, fileName);
			logger.info("Start transfering file "+fileName +" .....");
//			TimeTracker.start("SAVE");
//			TimeTracker.lapTime("SAVE", file.getName());
			/* ------------------------------------------------------- */
			// read in a loop
			/* ------------------------------------------------------- */
			int readSize   = 0;
			long size      = bin.available();
			long readBytes = 0;
			do {
				/* ------------------------------------------------------- */
//			    TimeTracker.lapTime("SAVE", "start reading chunk...");
				readSize = bin.read(b);
//				TimeTracker.lapTime("SAVE", "finish reading chunk...");
				/* ------------------------------------------------------- */
				// send the bytes to the server
				/* ------------------------------------------------------- */
//				TimeTracker.lapTime("SAVE", "start sending chunk...");
				chunkManager.recieve(b);
//				TimeTracker.lapTime("SAVE", "finish reading chunk...");
				/* ------------------------------------------------------- */
				// set the progress
				/* ------------------------------------------------------- */
				readBytes += CHUNKSIZE;
				this.setProgress((int) (size/readBytes));
				/* ------------------------------------------------------- */
				// inform the listener
				/* ------------------------------------------------------- */
				informListener();
				/* ------------------------------------------------------- */
			} while (readSize > -1);
			/* ------------------------------------------------------- */
			// close the file
			/* ------------------------------------------------------- */
			newFilename = chunkManager.closeFile();
//			TimeTracker.finish("SAVE");
			logger.info("Finished transfer of file "+ fileName + 
						". Thank you for travelling with Deutsche Bahn!");
			/* ------------------------------------------------------- */
		} catch (Exception e) {
            /* --------------------------------------------- */
            e.printStackTrace();
            logger.info("Error during transfer");
            throw e;
            /* --------------------------------------------- */
        } finally {
        	if(input != null) {
            	input.close();
            }
        }
		
		input.close();
		/* ================================================== */
	}

	
	/**
	 * Returns the filename of the stored file.
	 * 
	 * @return
	 */
	public String getFilename() {
		/* ================================================== */
		return this.newFilename;
		/* ================================================== */
	}
	
	/**
	 * 
	 */
	private void informListener() {
	    /* ================================================== */
	    if (listeners.size() < 1)
	        return;
	    /* ------------------------------------------------------- */
	    Thread t = new Thread() {

            @Override
            public void run() {
                /* ====================================================== */
                for (IChangeListener l : listeners) {
                    l.fireEvent();
                }
                /* ====================================================== */
            }
	        
	    };
	    t.start();
	    /* ================================================== */
	}
	
	
	/**
	 * @param l
	 */
	public void addStateListener(IChangeListener l) {
	    /* ================================================== */
	    this.listeners.add(l);
	    /* ================================================== */
	}
	
	/**
	 * @param p
	 */
	private synchronized void setProgress(int p) {
	    /* ================================================== */
	    this.progress  = p;
	    /* ================================================== */
	}
	
	/**
	 * Returns the progress as %
	 * 
	 * @return
	 */
	public synchronized int getProgress() {
	    /* ================================================== */
	    return this.progress;
	    /* ================================================== */
	}
	
	
}
