/*******************************************************************************
 * 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.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.Date;

import javax.imageio.ImageIO;

import lu.tudor.santec.dicom.gui.header.DicomHeader;
import lu.tudor.santec.gecamed.core.utils.FileUtils;
import lu.tudor.santec.gecamed.core.utils.ManagerFactory;
import lu.tudor.santec.gecamed.dicom.ejb.entity.beans.DCMEntry;
import lu.tudor.santec.gecamed.dicom.ejb.entity.beans.DCMImage;
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.dicom.ejb.session.beans.DCMManagerBean;
import lu.tudor.santec.gecamed.dicom.ejb.session.interfaces.DCMManagerInterface;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.Incident;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.IncidentEntry;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.Patient;
import lu.tudor.santec.gecamed.patient.ejb.session.beans.IncidentManagerBean;
import lu.tudor.santec.gecamed.patient.ejb.session.beans.PatientAdminBean;
import lu.tudor.santec.gecamed.patient.ejb.session.interfaces.IncidentManager;
import lu.tudor.santec.gecamed.patient.ejb.session.interfaces.PatientAdminInterface;
import lu.tudor.santec.gecamed.patient.gui.PatientManagerModule;

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

/**
 * 
 * 
 *
 * @author Johannes Hermen johannes.hermen(at)tudor.lu
 *
 * @version
 * <br>$Log: DCMImporter.java,v $
 * <br>Revision 1.13  2013-12-27 18:09:23  donak
 * <br>Cleanup of imports
 * <br>
 * <br>Revision 1.12  2013-07-15 06:18:35  ferring
 * <br>logging changed
 * <br>
 * <br>Revision 1.11  2013-07-02 09:21:14  ferring
 * <br>removing invalid symbols from DICOM header tags
 * <br>
 * <br>Revision 1.10  2013-06-27 13:40:23  ferring
 * <br>dicom viewer updated
 * <br>
 * <br>Revision 1.9  2010-05-26 16:33:34  troth
 * <br>bugfix - change the way to get a new and create a incident. rename the method getRecentIncident() in class PatientPanel to getCurrentIncident()
 * <br>
 * <br>Revision 1.8  2009-02-25 13:26:09  hermen
 * <br>added siteID to incidents and invoices
 * <br>
 * <br>Revision 1.7  2008-09-25 09:43:07  heinemann
 * <br>fixed copyrights
 * <br>
 * <br>Revision 1.6  2008-07-03 11:58:47  heinemann
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.5  2008-04-08 13:15:32  hermen
 * <br>added dicom entry to medical history
 * <br>
 * <br>Revision 1.4  2008-04-07 12:05:13  hermen
 * <br>translations...
 * <br>
 * <br>Revision 1.3  2008-03-28 14:12:20  hermen
 * <br>changed file path handling to new ServerConfiguration and FileUtils classes
 * <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 DCMImporter {

//	 general DCM Tags
//	public static final String DCM_PATIENT_NAME = "0010,0010";
//	
//	// DCM Tags for the Study
//	public static final String STUDY_INSTANCE_UID = "0020,000D";
//	public static final String STUDY_ID = "0020,0010";
//	public static final String STUDY_DESC = "0008,1030";
//	public static final String STUDY_INSTITUTION = "0008,0080";
//	public static final String STUDY_DATE = "0008,0020";
//	public static final String STUDY_TIME = "0008,0030";
//	
//	// DCM Tags for the Series
//	public static final String SERIES_INSTANCE_UID = "0020,000E";
//	public static final String SERIES_NR = "0020,0011";
//	public static final String SERIES_DESC = "0008,103E";
//	public static final String SERIES_PROTOCOL = "0018,1030";
//	public static final String SERIES_PHYCICIAN = "0008,0090";
//	public static final String SERIES_MODALITY = "0008,0060";
//	public static final String SERIES_DATE = "0008,0021";
//	public static final String SERIES_TIME = "0008,0031";
//	
//	//	 DCM Tags for the Image
//	public static final String IMAGE_INSTANCE_UID = "0008,0018";
//	public static final String IMAGE_NR = "0020,0013";
//	public static final String IMAGE_COMMENTS = "0020,4000";
//	public static final String IMAGE_MODALITY = "0008,0060";
//	public static final String IMAGE_TYPE = "0008,0008";
//	public static final String IMAGE_DATE = "0008,0023";
//	public static final String IMAGE_TIME = "0008,0033";

	
	/**
	 * static logger for this class
	 */
	private static Logger logger = Logger.getLogger(PatientDicomPanel.class
			.getName());
	
	private DCMManagerInterface manager;

	private PatientAdminInterface patientAdmin;

	private IncidentManager incidentManager;

	public boolean stopImport = false;


	public DCMImporter() {
    	// get reference to the session bean
 		manager = (DCMManagerInterface) ManagerFactory.getRemote(DCMManagerBean.class);
 		patientAdmin = (PatientAdminInterface) ManagerFactory.getRemote(PatientAdminBean.class);
 		incidentManager = (IncidentManager) ManagerFactory.getRemote(IncidentManagerBean.class);
	}
	

	/**
	 * checks for an existing Study, if not found, it creates a new one.
	 * @param dh the dicom header of the file
	 * @return the found/created study
	 * @throws Exception 
	 */
	public DCMStudy findOrMakeStudy(DicomHeader dh, Patient patient) throws Exception {
		stopImport = false;
		DCMStudy study = null;
		String studyInstanceUID = dh.getHeaderStringValue(Tag.StudyInstanceUID);
		try {
			study = manager.getStudy(studyInstanceUID);			
		} catch (Exception e) {
			e.printStackTrace();
		}
		if (study == null) {
			logger.info("No Study for ID: "+ studyInstanceUID + " found; Creating NEW........");
			study = new DCMStudy();
			study.setPatientID(patient.getId());
			study.setInstanceUID(cleanValue(dh.getHeaderStringValue(Tag.StudyInstanceUID)));
//			try {
				study.setNumber(dh.getHeaderIntegerValue(Tag.StudyID));
//			} catch (DicomHeaderParseException e1) {
//				logger.info("no valid study id");
//			}
			study.setDescription(cleanValue(dh.getHeaderStringValue(Tag.StudyDescription)));
			study.setInstitution(cleanValue(dh.getHeaderStringValue(Tag.InstitutionName)));
			study.setDate(dcm2Date(
					dh.getHeaderDateValue(Tag.StudyDate),
					dh.getHeaderDateValue(Tag.StudyTime)
					)
				);
			try {
			    	study = manager.saveStudy(study);
				logger.info("STUDY for ID: "+ studyInstanceUID + " created");
				return study;
			} catch (Exception e) {
				logger.log(Level.WARN,"Error creating STUDY for ID: "+ studyInstanceUID, e);
				throw e;
			}
		} else {
			return study;
		}
	}
	


	/**
	 * checks for an existing Series, if not found, it creates a new one.
	 * @param studyID id of the parent study
	 * @param dh the dicom header of the file
	 * @return the found/created series
	 * @throws Exception 
	 */
	public DCMSeries findOrMakeSeries(DCMStudy study, DicomHeader dh) throws Exception {
		DCMSeries series = null;
		String seriesInstanceUID = cleanValue(dh.getHeaderStringValue(Tag.SeriesInstanceUID));
		try {
			series = manager.getSeries(seriesInstanceUID);			
		} catch (Exception e) {
			e.printStackTrace();
		}
		if (series == null) {
			logger.info("No Series for ID: "+ seriesInstanceUID + " found; Creating NEW........");
			series = new DCMSeries();
			series.setStudyID(study.getId());
			series.setInstanceUID(cleanValue(dh.getHeaderStringValue(Tag.SeriesInstanceUID)));
//			try {
				series.setNumber(dh.getHeaderIntegerValue(Tag.SeriesNumber));
//			} catch (DicomHeaderParseException e1) {
//				logger.warn("Series has valid number");
//			}
			series.setProtocol(cleanValue(dh.getHeaderStringValue(Tag.ProtocolName)));
			series.setPhysician(cleanValue(dh.getHeaderStringValue(Tag.ReferringPhysicianName)));
			series.setDescription(cleanValue(dh.getHeaderStringValue(Tag.SeriesDescription)));
			series.setModality(dh.getHeaderStringValue(Tag.Modality));
			try {
				series.setDate(dcm2Date(
						dh.getHeaderDateValue(Tag.SeriesDate),
						dh.getHeaderDateValue(Tag.SeriesTime)
				)
				);
			} catch (Exception e) {
				logger.warn("Series has no Date/Time");
			}
			try {
			    series = manager.saveSeries(series); 
			    IncidentEntry ie = createIncidentEntry(study, series);
			    series.setIncidentEntryID(ie.getId());
			    series = manager.saveSeries(series);
			    logger.info("SERIES for ID: "+ seriesInstanceUID + " created");
			    return series;
			} catch (Exception e) {
				logger.log(Level.WARN,"Error creating SERIES for ID: "+ seriesInstanceUID, e);
				throw e;
			}
		} else {
			return series;
		}
	}
	
	/**
	 * checks for an existing Image, if not found, it creates a new one.
	 * @param seriesID id of the parent series
	 * @param dh the dicom header of the file
	 * @param ip the imagePlus created from the file
	 * @return the found/created image
	 * @throws Exception 
	 */
	public DCMImage findOrMakeImage(Patient patient, Integer seriesID, DicomHeader dh, ImagePlus ip, File f) throws Exception {
		DCMImage image = null;
		String imageInstanceUID = cleanValue(dh.getHeaderStringValue(Tag.SOPInstanceUID));
		try {
			image = manager.getImage(imageInstanceUID);
		} catch (Exception e) {
			e.printStackTrace();
		}
		if (image == null) {
			logger.info("No Image for ID: "+ imageInstanceUID + " found; Creating NEW........");
			image = new DCMImage();
			image.setPatientID(patient.getId());
			image.setSeriesID(seriesID);
			image.setInstanceUID(cleanValue(dh.getHeaderStringValue(Tag.SOPInstanceUID)));
//			try {
				image.setNumber(dh.getHeaderIntegerValue(Tag.InstanceNumber));
//			} catch (DicomHeaderParseException e1) {
//				logger.info("no valid image number");
//			}
			image.setDescription(cleanValue(dh.getHeaderStringValue(Tag.ImageComments)));
			image.setModality(cleanValue(dh.getHeaderStringValue(Tag.Modality)));
			image.setType(cleanValue(dh.getHeaderStringValue(Tag.ImageType)));
			try {
				image.setDate(dcm2Date(
						dh.getHeaderDateValue(Tag.InstanceCreationDate),
						dh.getHeaderDateValue(Tag.InstanceCreationTime)
				)
				);
			} catch (Exception e) {
				logger.warn("Series has no Date/Time");
			}
			
			image.setCols(ip.getWidth());
			image.setRows(ip.getHeight());
			image.setSize(f.length());
			image.setPreview(createThumb(ip));
			
			try {
				logger.info("IMAGE for ID: "+ imageInstanceUID + " created");
				return manager.saveImage(image, FileUtils.readFile(f));
			} catch (Exception e) {
//				ErrorDialog.showErrorDialog(
//						MainFrame.getInstance(), 
//						Translatrix.getTranslationString("dcm.imageError"), 
//						Translatrix.getTranslationString("dcm.imageErrorText") + "\"" +  
//						PatientDicomPanel.dicomAdminsettings.getValue(DCMAdminSettingsPlugin.IMAGE_PATH) + "\"", 
//						"");
				stopImport  = true;
				logger.log(Level.WARN, "Error creating IMAGE for ID: "+ imageInstanceUID, e);
				throw e;
			}
		} else {
			return image;
		}
	}
	
	
	/**
	 * creates a thunbnail image from the given imageplus
	 * in jpg format
	 * @param ip the ImagePlus
	 * @return the jpg thumb as byte[]
	 */
	private byte[] createThumb(ImagePlus ip) {

		int thumbWidth = 100;
		int thumbHeight = 100;
		double thumbRatio = (double)thumbWidth / (double)thumbHeight;
		int imageWidth = ip.getWidth();
		int imageHeight = ip.getHeight();
		double imageRatio = (double)imageWidth / (double)imageHeight;
		if (thumbRatio < imageRatio) {
			thumbHeight = (int)(thumbWidth / imageRatio);
		} else {
			thumbWidth = (int)(thumbHeight * imageRatio);
		}

		Image scaledImage = ip.getImage().getScaledInstance(thumbWidth, thumbHeight,
				Image.SCALE_SMOOTH);

		BufferedImage outImg = new BufferedImage(thumbWidth, thumbHeight,
				BufferedImage.TYPE_INT_RGB);
		Graphics g = outImg.getGraphics();
		g.drawImage(scaledImage, 0, 0, null);
		g.dispose();

		try {
			ByteArrayOutputStream bOut = new ByteArrayOutputStream();
			ImageIO.write(outImg, "jpeg", bOut);
			return bOut.toByteArray();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	
	/**
	 * converts a dicom formated date and time to a Java Date Object
	 * @param dateTag dicom formated date yyyymmdd
	 * @param timeTag dicom formated time HHmmss
	 * @return
	 */
	private Date dcm2Date (Date dateTag, Date timeTag)
	{
		if (dateTag == null)
			return null;
		else
			return new Date(dateTag.getTime() + (timeTag != null ? timeTag.getTime() : 0));
		
//		try
//		{
//			int year = Integer.parseInt(dateTag.substring(0, 4));
//			int month = Integer.parseInt(dateTag.substring(4, 6));
//			int day = Integer.parseInt(dateTag.substring(6, 8));
//			int hour = Integer.parseInt(timeTag.substring(0, 2));
//			int minute = Integer.parseInt(timeTag.substring(2, 4));
//			int second = Integer.parseInt(timeTag.substring(4, 6));
//			Calendar c = new GregorianCalendar(
//					year,
//					month - 1,
//					day
//					);
//			c.set(Calendar.HOUR_OF_DAY, hour);
//			c.set(Calendar.MINUTE, minute);
//			c.set(Calendar.SECOND, second);
//			c.set(Calendar.MILLISECOND, 0);
//			return c.getTime();
//		}
//		catch (NumberFormatException e)
//		{
//			System.out.println(dateTag + " / " + timeTag);
//			return null;
//		}
	}
	
   public String getPatientName(Integer pID) {
	   return patientAdmin.getPatientName(pID);
   }
	
	private IncidentEntry createIncidentEntry(DCMStudy study, DCMSeries series) {
	    IncidentEntry ie = new IncidentEntry();
	    // get latest incident
	    Incident inc = PatientManagerModule.getInstance()
	    	.getPatientPanel().getCurrentIncident();
	   
	    ie.setIncident(inc);
	    
	    // set Text
	    StringBuffer desc = new StringBuffer("<html><b>" + series.getModality() + "</b>  ");
	    desc.append(((study.getInstitution() != null)?study.getInstitution()+":":"") + " ");
	    desc.append(((series.getDescription() != null)?series.getDescription():"") + " ");
	    desc.append(((series.getProtocol() != null)?series.getProtocol():"") + " ");
	    ie.setTextContent(desc.toString());
	    return incidentManager.saveEntry(ie, DCMEntry.DCM_ENTRY_TYPE);
	}
	
	
	public static String cleanValue (String value)
	{
		if (value == null)
			return "";
		
		// replace nullbytes as they kill Postgres....
		value = value.replace('\0', ' ');
		// replace control characters etc as they kill Excel....
		value = value.replaceAll("\\p{Cntrl}", " ");
		return value;
	}
}
