/*******************************************************************************
 * 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.dicom.gui;

import ij.ImagePlus;

import java.awt.Cursor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileNotFoundException;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;

import lu.tudor.santec.dicom.DicomOpener;
import lu.tudor.santec.dicom.gui.header.DicomHeader;
import lu.tudor.santec.gecamed.core.gui.GECAMedColors;
import lu.tudor.santec.gecamed.core.gui.GECAMedFonts;
import lu.tudor.santec.gecamed.core.gui.GECAMedIconNames;
import lu.tudor.santec.gecamed.core.gui.GECAMedModule;
import lu.tudor.santec.gecamed.core.gui.IconFetcher;
import lu.tudor.santec.gecamed.core.gui.MainFrame;
import lu.tudor.santec.gecamed.core.gui.widgets.GECAMedBaseDialogImpl;
import lu.tudor.santec.gecamed.dicom.ejb.entity.beans.DCMSeries;
import lu.tudor.santec.gecamed.dicom.ejb.entity.beans.DCMStudy;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.Patient;
import lu.tudor.santec.i18n.Translatrix;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.dcm4che2.data.Tag;

import com.jgoodies.forms.builder.ButtonBarBuilder;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;

/**
 * JDialog that manages the import of nesw images.
 * It shows a status label, a progress bar and buttons 
 * to cancel or do the import.
 * 
 *
 * @author Johannes Hermen johannes.hermen(at)tudor.lu
 *
 * @version
 * <br>$Log: DCMImportDialog.java,v $
 * <br>Revision 1.11  2013-12-27 18:09:23  donak
 * <br>Cleanup of imports
 * <br>
 * <br>Revision 1.10  2013-07-15 06:18:35  ferring
 * <br>logging changed
 * <br>
 * <br>Revision 1.9  2013-07-02 09:21:14  ferring
 * <br>removing invalid symbols from DICOM header tags
 * <br>
 * <br>Revision 1.8  2013-06-27 13:40:23  ferring
 * <br>dicom viewer updated
 * <br>
 * <br>Revision 1.7  2008-09-25 09:43:07  heinemann
 * <br>fixed copyrights
 * <br>
 * <br>Revision 1.6  2008-09-15 10:07:23  hermen
 * <br>fixed import status dialog
 * <br>
 * <br>Revision 1.5  2008-07-10 08:59:17  hermen
 * <br>changed saving of dicom files
 * <br>
 * <br>Revision 1.4  2008-04-08 13:15:32  hermen
 * <br>added dicom entry to medical history
 * <br>
 * <br>Revision 1.3  2008-04-07 12:05:13  hermen
 * <br>translations...
 * <br>
 * <br>Revision 1.2  2008-03-05 07:27:39  hermen
 * <br>a lot of improvements and bugfixes
 * <br>
 * <br>Revision 1.1  2008-02-26 10:45:51  hermen
 * <br>initial checkin of the dicom module
 * <br>
 *
 */
public class DCMImportDialog extends JDialog implements ActionListener {

	private static final long serialVersionUID = 1L;
	
	/**
	 * static logger for this class
	 */
	private static Logger logger = Logger.getLogger(DCMImportDialog.class
			.getName());
	
	private JProgressBar progressBar;
	private JButton cancelButton;
	private JButton goonButton;
	private JLabel iconLabel;
	private JLabel messageLabel;
	private DCMImporter dcmImporter;

	private ImporterThread importerThread;

	private boolean finished;
	
	private PatientDicomPanel panel;
	
	private int notImported;
	private int imported;

	
	/**
	 * create the Dialog
	 */
	public DCMImportDialog() {
		super(MainFrame.getInstance());
		
		this.setModal(true);
		
		this.getContentPane().setBackground(GECAMedColors.c_GECAMedBackground);
		
		CellConstraints cc = new CellConstraints();
		
		this.setLayout(new FormLayout(
				"3dlu, 40dlu, 3dlu, 160dlu, 3dlu",
				"3dlu, 90dlu, 3dlu, pref, 3dlu, pref, 3dlu"));
		
		this.dcmImporter = new DCMImporter();
		
		this.iconLabel = new JLabel(
				IconFetcher.getIcon(PatientDicomPanel.class, PatientDicomPanel.ICON_INFO));
		this.iconLabel.setVerticalAlignment(JLabel.TOP);
		
		this.add(iconLabel, cc.xy(2,2));
		
		this.messageLabel = new JLabel();
		this.messageLabel.setFont(GECAMedFonts.TEXTFIELD_FONT);
		this.add(messageLabel, cc.xy(4,2));
		
		this.progressBar = new JProgressBar();
		this.progressBar.setMinimum(0);
		this.progressBar.setStringPainted(true);

		this.add(this.progressBar, cc.xyw(2, 4, 3));
		
		ButtonBarBuilder bbb = new ButtonBarBuilder();
		bbb.addGlue();
		
		this.cancelButton = new JButton(Translatrix.getTranslationString("core.cancel"));
		this.cancelButton.addActionListener(this);
		bbb.addGridded(this.cancelButton);
		
		bbb.addRelatedGap();
		
		this.goonButton = new JButton(Translatrix.getTranslationString("dcm.continue"));
		this.goonButton.addActionListener(this);
		this.goonButton.setEnabled(false);
		bbb.addGridded(this.goonButton);
		
		JPanel bPanel = bbb.getPanel();
		bPanel.setOpaque(false);
		this.add(bPanel, cc.xyw(2, 6, 3));

		this.pack();
		
	}
	
	/**
	 * import the supplied files....
	 * @param files array of files to import
	 * @param patient patient to import the files  to.
	 */
	public void importFiles(File[] files, Patient patient, PatientDicomPanel panel) {
		
		this.panel = panel;
		
		this.finished = false;
		
		this.setLocationRelativeTo(MainFrame.getInstance());
		
		this.progressBar.setMaximum(files.length);
		this.progressBar.setValue(0);
		
		this.importerThread = new ImporterThread(files, patient);
		this.importerThread.start();
		
		this.setVisible(true);
		
	}

	/* (non-Javadoc)
	 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
	 */
	public void actionPerformed(ActionEvent e) {
		if (e.getSource().equals(this.cancelButton)) {
			this.importerThread.cancelImport();
			this.setVisible(false);
		} else if (e.getSource().equals(this.goonButton)) {
			if (finished) {
				this.setVisible(false);	
			} else {
				this.goonButton.setEnabled(false);
				importing();
				this.importerThread.resumeImport();
			}
		}
	}
	
	
	/**
	 * called when importing is started or resumed
	 */
	private void importing() {
		this.iconLabel.setIcon(IconFetcher.getIcon(PatientDicomPanel.class, PatientDicomPanel.ICON_PROGRESS));
		String[] args = {
				importerThread.getDcmPatientName()
		};
		this.messageLabel.setText(Translatrix.getTranslationString("dcm.importing", args));
	}

	/**
	 * called when the import detects a new patient name in the images
	 */
	private void newPatient() {
		this.setWaitCursor(false);
		String[] args = {
				importerThread.getDcmPatientName(),
				importerThread.getPatient().toString()
		};
		
		this.iconLabel.setIcon(IconFetcher.getIcon(PatientDicomPanel.class, PatientDicomPanel.ICON_INFO));
		this.messageLabel.setText(Translatrix.getTranslationString("dcm.importPatientChanged", args));
		this.goonButton.setEnabled(true);
	}

	/**
	 * called when the import detects that the images are allready linked to another patient
	 */
	private void allreadyLinked(DCMStudy study) {
		this.setWaitCursor(false);
		String[] sArr = {
				study.getInstanceUID(), 
				dcmImporter.getPatientName(study.getPatientID())
				};	
		this.iconLabel.setIcon(IconFetcher.getIcon(PatientDicomPanel.class, PatientDicomPanel.ICON_INFO));
		this.messageLabel.setText(Translatrix.getTranslationString("dcm.studyLinkedText", sArr));
		this.progressBar.setValue(0);
		this.goonButton.setEnabled(false);
	}
	
	/**
	 * Thread that hanles the image import.
	 * it can be suspended  and canceled by the Dialog.
	 * 
	 *
	 * @author Johannes Hermen johannes.hermen(at)tudor.lu
	 *
	 * @version
	 * <br>$Log: DCMImportDialog.java,v $
	 * <br>Revision 1.11  2013-12-27 18:09:23  donak
	 * <br>Cleanup of imports
	 * <br>
	 * <br>Revision 1.10  2013-07-15 06:18:35  ferring
	 * <br>logging changed
	 * <br>
	 * <br>Revision 1.9  2013-07-02 09:21:14  ferring
	 * <br>removing invalid symbols from DICOM header tags
	 * <br>
	 * <br>Revision 1.8  2013-06-27 13:40:23  ferring
	 * <br>dicom viewer updated
	 * <br>
	 * <br>Revision 1.7  2008-09-25 09:43:07  heinemann
	 * <br>fixed copyrights
	 * <br>
	 * <br>Revision 1.6  2008-09-15 10:07:23  hermen
	 * <br>fixed import status dialog
	 * <br>
	 * <br>Revision 1.5  2008-07-10 08:59:17  hermen
	 * <br>changed saving of dicom files
	 * <br>
	 * <br>Revision 1.4  2008-04-08 13:15:32  hermen
	 * <br>added dicom entry to medical history
	 * <br>
	 * <br>Revision 1.3  2008-04-07 12:05:13  hermen
	 * <br>translations...
	 * <br>
	 * <br>Revision 1.2  2008-03-05 07:27:39  hermen
	 * <br>a lot of improvements and bugfixes
	 * <br>
	 * <br>Revision 1.1  2008-02-26 10:45:51  hermen
	 * <br>initial checkin of the dicom module
	 * <br>
	 *
	 */
	private class ImporterThread extends Thread {
		
		private File[] files;
		private boolean stopped = false;
		private boolean canceled = false;
		private Patient patient;
		private String dcmPatientName ;

		public ImporterThread(File[] files, Patient p) {
			this.files = files;
			this.patient = p;
		}
		
		/* (non-Javadoc)
		 * @see java.lang.Thread#run()
		 */
		public void run() {
			
			setWaitCursor(true);
			notImported = 0;
			imported = 0;
			
			for (int i = 0; i < files.length; i++) {
				
				try
				{
					// get the image and dicom header
					ImagePlus ip = DicomOpener.loadImage(files[i]);
					DicomHeader dh = new DicomHeader(files[i]);
					if (dh.isEmpty() || "".equals(DCMImporter.cleanValue(dh.getHeaderStringValue(Tag.SOPInstanceUID)))) {
					        notImported++;
						logger.info("File " + files[i].getAbsolutePath() + " is not a dicom file");
						continue;
					}
					
					String newDcmPatientName = DCMImporter.cleanValue(dh.getHeaderStringValue(Tag.PatientName));
					
					
					// check if we import files from a new patient
					if (! newDcmPatientName.equals(this.dcmPatientName)) {
						// stop import and show message
						this.dcmPatientName = newDcmPatientName;
						this.stopImport();
						newPatient();
					}
					
					try {
						if (canceled ) {
							// exit here
							return;
						} 
						if (stopped)
							// wait loop 
							synchronized(this) {
								while (stopped)
									Thread.sleep(100);
							}
							// import the file
							setWaitCursor(true);
							progressBar.setValue(i+1);
							saveImage(files[i], ip, dh, patient);
							imported++;
		            } catch (Exception e){
		            	logger.log(Level.ERROR, "Error while importing DICOM file \""+files[i].getAbsolutePath()+"\"", e);
		            	
		            	if (GECAMedBaseDialogImpl.showMessageDialog(
								MainFrame.getInstance(), 
								Translatrix.getTranslationString("dcm.imageError"), 
								Translatrix.getTranslationString("dcm.imageErrorText") + 
								"\n" + e.getMessage(), 
								GECAMedBaseDialogImpl.OK_CANCEL_BUTTON_MODE,
								GECAMedModule.getBigIcon(GECAMedIconNames.ERROR)) == GECAMedBaseDialogImpl.CANCEL_OPTION)
							canceled = true;
		            }
				}
				catch (FileNotFoundException e1)
				{
					logger.log(Level.ERROR, e1.getMessage(), e1);
				}
			}		
			// set status to finished
			importFinished();
		}

		/**
		 * stops the import.
		 */
		public void stopImport() {
			this.stopped = true;
		}
	
		/**
		 * resumes the import
		 */
		public void resumeImport() {
			this.stopped = false;
		}
		
		/**
		 * cancels the import
		 */
		public void cancelImport() {
			this.canceled = true;
		}
		
		/**
		 * saves one single image to the DB.
		 * @param file
		 * @throws Exception 
		 */
		public void saveImage(File file, ImagePlus ip, DicomHeader dh, Patient patient) throws Exception {

			if (dcmImporter.stopImport) 
				cancelImport();
			
			// handle STUDY
			DCMStudy study = dcmImporter.findOrMakeStudy(dh, patient);
			if (! patient.getId().equals(study.getPatientID())) {
				System.out.println(study.getPatientID() + " " + patient.getId());
				cancelImport();
				allreadyLinked(study);
			}
			
			//	handle SERIES
			DCMSeries series = dcmImporter.findOrMakeSeries(study, dh);
			
			// handle IMAGE
			dcmImporter.findOrMakeImage(patient, series.getId(), dh, ip, file);
			
			if (dcmImporter.stopImport) 
				cancelImport();
			
		}

		/**
		 * @return the patient
		 */
		public Patient getPatient() {
			return patient;
		}

		/**
		 * @param patient the patient to set
		 */
		public void setPatient(Patient patient) {
			this.patient = patient;
		}

		/**
		 * @return the dcmPatientName
		 */
		public String getDcmPatientName() {
			return dcmPatientName;
		}

		/**
		 * @param dcmPatientName the dcmPatientName to set
		 */
		public void setDcmPatientName(String dcmPatientName) {
			this.dcmPatientName = dcmPatientName;
		}
		
	
	}

	/**
	 * called when the import has finished 
	 */
	private void importFinished() {
		this.setWaitCursor(false);
		this.progressBar.setValue(this.progressBar.getMaximum());
		if (imported == 0) {
		    this.iconLabel.setIcon(IconFetcher.getIcon(PatientDicomPanel.class, PatientDicomPanel.ICON_WARN));
		} else {
		    this.iconLabel.setIcon(IconFetcher.getIcon(PatientDicomPanel.class, PatientDicomPanel.ICON_DONE));
		}
		String[] params1 = {
			imported + "",
		};
		String s = Translatrix.getTranslationString("dcm.importDone", params1);
		String[] params2 = {
			notImported + ""
		};
		if (notImported > 0) {
		    s += Translatrix.getTranslationString("dcm.importNotDone", params2);
		}
		this.messageLabel.setText(s);
		this.goonButton.setText(Translatrix.getTranslationString("core.ok"));		    
		this.goonButton.setEnabled(true);
		this.finished = true;
		this.panel.reloadTable();
	}
	
	/**
	 * sets the Mousecursor of the MainFrame to a WaitCursor and Back
	 *
	 * @param on true=waitcursor false=normalcursor
	 */
	public void setWaitCursor(boolean on) {
		if (on) {
			this.getGlassPane().setVisible(true);
			this.getGlassPane().setCursor(
					Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));

		} else {
			this.getGlassPane().setCursor(
					Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
			this.getGlassPane().setVisible(false);
		}
	}
	
	/**
	 * just for testing
	 * @param args
	 */
	public static void main(String[] args) {
		DCMImportDialog dialog = new DCMImportDialog();
		dialog.setVisible(true);
		
	}
	
}
