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

import java.awt.Cursor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;

import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.table.TableCellRenderer;

import lu.tudor.santec.gecamed.core.gui.GECAMedMessage;
import lu.tudor.santec.gecamed.core.gui.GECAMedMessageListener;
import lu.tudor.santec.gecamed.core.gui.IconFetcher;
import lu.tudor.santec.gecamed.core.gui.MainFrame;
import lu.tudor.santec.gecamed.core.gui.RegistrationDesk;
import lu.tudor.santec.gecamed.core.gui.listener.IEntryPrintRenderer;
import lu.tudor.santec.gecamed.core.gui.listener.IEntryTypeHandler;
import lu.tudor.santec.gecamed.core.gui.utils.CustomFileFilter;
import lu.tudor.santec.gecamed.core.gui.utils.SwingWorker;
import lu.tudor.santec.gecamed.core.gui.utils.WindowToolbox;
import lu.tudor.santec.gecamed.core.gui.widgets.ButtonPanel;
import lu.tudor.santec.gecamed.core.utils.Logger;
import lu.tudor.santec.gecamed.core.utils.ManagerFactory;
import lu.tudor.santec.gecamed.labo.ejb.entity.beans.Result;
import lu.tudor.santec.gecamed.labo.ejb.entity.beans.ResultStub;
import lu.tudor.santec.gecamed.labo.ejb.session.beans.ResultBean;
import lu.tudor.santec.gecamed.labo.ejb.session.interfaces.ResultInterface;
import lu.tudor.santec.gecamed.labo.gui.LaboModule;
import lu.tudor.santec.gecamed.labo.gui.event.result.ResultChangeEvent;
import lu.tudor.santec.gecamed.labo.gui.event.result.ResultListener;
import lu.tudor.santec.gecamed.labo.gui.history.HistoryPanel;
import lu.tudor.santec.gecamed.labo.gui.print.ResultPrintPreview;
import lu.tudor.santec.gecamed.labo.utils.ResultRenderer;
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.gui.PatientManagerModule;
import lu.tudor.santec.gecamed.patient.gui.PatientViewTab;
import lu.tudor.santec.i18n.Translatrix;

import org.apache.log4j.Level;

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

public class LaboPanel extends PatientViewTab implements GECAMedMessageListener,
														 ResultListener,
														 ActionListener,
														 IEntryTypeHandler
	{
/**
 * 
 */
	private JSplitPane		m_SplitPane;

	private ButtonPanel		m_ButtonPanel;
	private JButton			m_ExportAsPDFButton;
	private JButton			m_ExportAsXMLButton;
	private JButton			m_PrintResultButton;	
	private JScrollPane		m_ResultScroller;
	private ResultPanel		m_Results;
	private ResultStub		m_OpenedResult;
	
	private JScrollPane		m_HTMLScroller;
	private ResultViewer	m_ResultViewer;
	
	private SwingWorker		m_RenderWorker;
	private int				m_RenderFormat;
	private Result			m_RenderResult;
	private File			m_ExportFile;
	
	private PatientHistoryRenderer	m_HistoryRenderer;
	
	private ResultInterface	m_ResultInterface;	
		
	private Logger	m_Logger = new Logger (LaboPanel.class);	

	private static final Collection <Integer> m_Dependencies = new ArrayList <Integer> ();
    
	static 	{
    		m_Dependencies.add(ResultInterface.c_LaboratoryDependency);
    		m_Dependencies.add(ResultInterface.c_PrescriberDependency);
    		m_Dependencies.add(ResultInterface.c_PatientDependency);
    		m_Dependencies.add(ResultInterface.c_ContactsDependency);
    		m_Dependencies.add(ResultInterface.c_AnalysesDependency);
    		m_Dependencies.add(ResultInterface.c_AntibiogramsDependency);
	    	}

	private static CustomFileFilter c_XMLFilter = new CustomFileFilter ("xml","XML Files"); 
	private static CustomFileFilter c_PDFFilter = new CustomFileFilter ("pdf","PDF Files"); 

	
//---------------------------------------------------------------------------
//***************************************************************************
//* Constants	                                                            *
//***************************************************************************
//---------------------------------------------------------------------------

	private static final long serialVersionUID = 1L;
	
	private static final int  c_BufferSize  = 4096;

	private final static String c_Columns= "fill:50dlu:grow";		
	
	private final static String c_Rows=    "fill:50dlu:grow";

	private final static String c_ResultColumns	= "3dlu,fill:pref:grow,3dlu";		
	
	private final static String c_ResultRows	= "3dlu,fill:max(34px;pref)," +
										   		  "3dlu,fill:pref:grow:3dlu";
	
//---------------------------------------------------------------------------
//***************************************************************************
//* Constructor	                                                            *
//***************************************************************************
//---------------------------------------------------------------------------

public LaboPanel ()
	{
	Translatrix.addBundle ("lu.tudor.santec.gecamed.labo.gui.resources.WidgetResources");
	
	CellConstraints	l_Constraints;
	FormLayout		l_Layout;
 	
	l_Constraints  	= new CellConstraints();
	l_Layout		= new FormLayout(c_Columns, c_Rows);
		
	this.setLayout (l_Layout); 
	this.setOpaque(false);

	this.setIcon(IconFetcher.getIcon (LaboModule.class, LaboModule.c_i32_Labo));
	this.setTitle(Translatrix.getTranslationString("Labotitle"));
		
	m_ResultViewer = new ResultViewer ();
	
    m_HTMLScroller = new JScrollPane ();
    m_HTMLScroller.setViewportView (m_ResultViewer); 
    
    m_SplitPane = new JSplitPane (JSplitPane.HORIZONTAL_SPLIT,this.buildResultPanel(),m_HTMLScroller);
	m_SplitPane.setOpaque(false);
	m_SplitPane.setOneTouchExpandable(true);

	this.add (m_SplitPane,l_Constraints.xywh(1, 1, 1, 1));
	
	m_OpenedResult = null;
	
	RegistrationDesk.addGECAMedMessageListener (this);
	RegistrationDesk.addEntryTypeHandler (Result.c_LaboResultIncident, this);
	}
	
//---------------------------------------------------------------------------
//***************************************************************************
//* Primitives	                                                            *
//***************************************************************************

private JPanel buildResultPanel ()
	{
	CellConstraints	l_Constraints;
	FormLayout		l_Layout;
	JPanel			l_Panel;
	
	l_Constraints  	= new CellConstraints();
	l_Layout		= new FormLayout (c_ResultColumns, c_ResultRows);
	l_Panel 		= new JPanel ();
		
	l_Panel.setLayout (l_Layout); 
	l_Panel.setOpaque (false);
	
	m_ButtonPanel = new ButtonPanel (LaboModule.c_ModuleColor);

	m_ExportAsPDFButton = new JButton ();
	m_ExportAsPDFButton.setIcon(IconFetcher.getIcon (LaboModule.class, "result_export_pdf_action.png"));
	m_ExportAsPDFButton.addActionListener (this);
	
	m_ExportAsXMLButton = new JButton ();
	m_ExportAsXMLButton.setIcon(IconFetcher.getIcon (LaboModule.class, "result_export_xml_action.png"));
	m_ExportAsXMLButton.addActionListener (this);
	
	m_PrintResultButton = new JButton ();
	m_PrintResultButton.setIcon(IconFetcher.getIcon (LaboModule.class, "result_print_action.png"));
	m_PrintResultButton.addActionListener (this);
//	m_PrintResultButton.setEnabled(false);
	
	m_ButtonPanel.addButton(m_ExportAsPDFButton);
	m_ButtonPanel.addButton(m_ExportAsXMLButton);
	m_ButtonPanel.addButton(m_PrintResultButton);

	m_Results = new ResultPanel ();
    m_Results.addResultListener(this);
	m_ResultScroller = new JScrollPane ();
    m_ResultScroller.setViewportView (m_Results); 
		
	l_Panel.add (m_ButtonPanel,   l_Constraints.xywh(2, 2, 1, 1));
	l_Panel.add (m_ResultScroller,l_Constraints.xywh(2, 4, 1, 1));
		
	return l_Panel;	
	}

//---------------------------------------------------------------------------
/**
 * The private getKeyChainInterface returns an instance of the KeyChainBean
 * session bean. On the first call, the KeyChainBean will actualy be looked up
 * via JNDI. Once it has been found, the reference to the bean will be stored
 * in a private data member. Doing so avoids JNDI lookups on later calls.
 * @return an instance of the KeyChainBean session bean.
 */
//---------------------------------------------------------------------------

private ResultInterface getResultInterface ()
	{
	if (m_ResultInterface != null) return m_ResultInterface;

	try {
		m_ResultInterface = (ResultInterface) ManagerFactory.getRemote(ResultBean.class);
		} 
	catch (Exception p_Exception) 
		{
		m_Logger.log(Level.FATAL, "Failed to lookup ResultInterface!",p_Exception);
		}

	return m_ResultInterface;
	}

//---------------------------------------------------------------------------

private Collection <ResultStub> getResultStubsForPatient (Patient p_Patient)
	{
	ResultInterface			l_Interface;
	Collection <ResultStub>	l_ResultStubs = null;
	
	if (p_Patient == null) return null;
	
	l_Interface = this.getResultInterface();
	if (l_Interface == null) return null;
	
	try	{
		l_ResultStubs = l_Interface.getResultStubsForPatient(p_Patient);
		}
	catch (Exception p_Exception)
		{
		m_Logger.log(Level.FATAL, "Failed to get ResultStubs for patient " + p_Patient.toString() + "!",p_Exception);	
		}
	
	return l_ResultStubs;
	}

//---------------------------------------------------------------------------

private Result	getResultById (Integer p_ResultId)
	{
	ResultInterface	l_Interface;
	Result			l_Result = null;
	
	if (p_ResultId == null) return null;
	
	l_Interface = this.getResultInterface();
	if (l_Interface == null) return null;
	
	try	{
		l_Result = l_Interface.getResultById(p_ResultId);
		l_Result = l_Interface.fetchLazyDependencies(l_Result, m_Dependencies);
		}
	catch (Exception p_Exception)
		{
		m_Logger.log(Level.FATAL, "Failed to get Result with Id " + p_ResultId.toString() + "!",p_Exception);	
		}
	
	return l_Result;	
	}

//---------------------------------------------------------------------------

private Result	getResultByIncidentEntryId (Integer p_IncidentEntryId)
	{
	ResultInterface	l_Interface;
	Result			l_Result = null;
	
	if (p_IncidentEntryId == null) return null;
	
	l_Interface = this.getResultInterface();
	if (l_Interface == null) return null;
	
	try	{
		l_Result = l_Interface.getResultByIncidentEntryId(p_IncidentEntryId);
		l_Result = l_Interface.fetchLazyDependencies(l_Result, m_Dependencies);
		}
	catch (Exception p_Exception)
		{
		m_Logger.log(Level.FATAL, "Failed to get Result for Incident Entry with ID " + p_IncidentEntryId.toString() + "!",p_Exception);	
		}
	
	return l_Result;	
	}

//---------------------------------------------------------------------------

private Result	getSelectedResult ()
	{
	ResultStub	l_SelectedStub;
	Result		l_Result = null;
	
	l_SelectedStub = m_Results.getSelectedResult();
	if (l_SelectedStub != null)
		{
		l_Result = this.getResultById (l_SelectedStub.getId());		
		}
	return l_Result;
	}

//---------------------------------------------------------------------------

private void saveAsXMLFile ()
	{
	JFileChooser	l_FileChooser;
	
	l_FileChooser = MainFrame.getFileChooser();
	try {
		l_FileChooser.setSelectedFile(new File("LaboResult_" + m_RenderResult.getReference() + "_" + m_RenderResult.getResultStatus() + ".xml"));		
	} catch (Exception e) {}
	l_FileChooser.setFileFilter (c_XMLFilter);
	
	if (l_FileChooser.showSaveDialog(WindowToolbox.getOwnerFrame(this)) == JFileChooser.APPROVE_OPTION)
		{
		m_RenderResult = this.getSelectedResult();
		m_ExportFile = l_FileChooser.getSelectedFile();
		this.doRender(ResultRenderer.c_XMLFormat);
		}
	}	

//---------------------------------------------------------------------------

private void saveAsPDFFile ()
	{
	JFileChooser	l_FileChooser;
	
	l_FileChooser = MainFrame.getFileChooser();
	try {
		l_FileChooser.setSelectedFile(new File("LaboResult_" + m_RenderResult.getReference() + "_" + m_RenderResult.getResultStatus() + ".pdf"));		
	} catch (Exception e) {}
	l_FileChooser.setFileFilter (c_PDFFilter);
	
	if (l_FileChooser.showSaveDialog(WindowToolbox.getOwnerFrame(this)) == JFileChooser.APPROVE_OPTION)
		{
		m_RenderResult = this.getSelectedResult();
		m_ExportFile = l_FileChooser.getSelectedFile();
		this.doRender(ResultRenderer.c_PDFFormat);
		}
	}	

//---------------------------------------------------------------------------

private void printResult ()
	{
	m_RenderResult = this.getSelectedResult();

	this.doRender(ResultRenderer.c_ImageFormat);	
	}

//---------------------------------------------------------------------------

private void writeToFile (byte[] p_Data, File p_TargetFile)
	{
	InputStream			l_DataStream;
	FileOutputStream	l_FileStream;
	byte[]				l_Buffer;
	int					l_BytesRead;
					
	l_Buffer = new byte [c_BufferSize];

	try	{
		l_DataStream = new ByteArrayInputStream (p_Data);
		l_FileStream = new FileOutputStream (p_TargetFile.getPath());
		
		do	{
			l_BytesRead = l_DataStream.read(l_Buffer, 0, c_BufferSize);
			if (l_BytesRead > 0) l_FileStream.write(l_Buffer, 0, l_BytesRead);
			}
		while (l_BytesRead > 0);
		
		l_DataStream.close();
		l_FileStream.close();
		}
	catch (Exception p_Exception)
		{
		m_Logger.log(Level.FATAL, "Failed to write file " + p_TargetFile.getPath(),p_Exception);
		}		
	}

//---------------------------------------------------------------------------

private void showPrintPreview (ResultRenderer p_Renderer, BufferedImage[]	p_PagePreviews)
	{
	ResultPrintPreview	l_PreviewDialog;	
		
	l_PreviewDialog = new ResultPrintPreview ("Test");
	l_PreviewDialog.setPreviewPages(p_PagePreviews);
	l_PreviewDialog.setRenderer(p_Renderer);
	l_PreviewDialog.pack();
		
	MainFrame.showDialogCentered(l_PreviewDialog);
	}

//---------------------------------------------------------------------------
//***************************************************************************
//* Swing Worker Thread	                                                    *
//***************************************************************************

//---------------------------------------------------------------------------

Object RenderWorker ()
	{
	this.renderResult(m_RenderFormat);
	return "";
	}

//---------------------------------------------------------------------------

private synchronized void renderResult (int p_RenderFormat)
	{
	ResultRenderer	l_ResultRenderer;
	byte[]			l_ExportData;
	BufferedImage[]	l_PrintData;
	
	l_ResultRenderer = new ResultRenderer (m_RenderResult);
	l_ResultRenderer.setTemplate (p_RenderFormat);
	l_ResultRenderer.setOffice (MainFrame.getCurrentOffice());
		
	switch (p_RenderFormat)
		{
		case ResultRenderer.c_XMLFormat:
			
			l_ExportData = l_ResultRenderer.renderAsXML();
			this.writeToFile(l_ExportData, m_ExportFile);
			break;
		
		case ResultRenderer.c_PDFFormat:
			
			l_ExportData = l_ResultRenderer.renderAsPDF();
			this.writeToFile(l_ExportData, m_ExportFile);
			break;
		
		case ResultRenderer.c_ImageFormat:
			
			l_PrintData = l_ResultRenderer.renderAsImage();
			this.showPrintPreview (l_ResultRenderer,l_PrintData);
			break;
		}
	}

//---------------------------------------------------------------------------

public void doRender (int p_RenderFormat)
	{
	m_RenderFormat = p_RenderFormat;
	m_RenderWorker = new SwingWorker() 
	{
	public Object construct() 
		{
		return RenderWorker ();
		}
	public void start ()
		{
		MainFrame.getInstance().showMessage(Translatrix.getTranslationString("LaboPanel.ExportingResult"));
		MainFrame.getInstance().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
		super.start();
		}
	public void finished ()
		{
		MainFrame.getInstance().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
		m_RenderFormat = ResultRenderer.c_None;
		}
	public void interrupt ()
		{
		super.interrupt();
		MainFrame.getInstance().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
		m_RenderFormat = ResultRenderer.c_None;
		}
	};

	m_RenderWorker.start ();  	
	}

//---------------------------------------------------------------------------
//***************************************************************************
//* Class Body	                                                            *
//***************************************************************************
//---------------------------------------------------------------------------

@Override
public String getName()
	{
	return "LABO_PANEL";
	}

//---------------------------------------------------------------------------

public void preparetoShowup ()
	{
	}

//---------------------------------------------------------------------------

public void setPatient (Patient p_Patient)
	{
	Collection <ResultStub>	l_PatientResults;
		
	l_PatientResults = this.getResultStubsForPatient(p_Patient);
	m_Results.setResultStubs (l_PatientResults);
	
	if (m_OpenedResult != null)
		{
		m_Results.selectResult(m_OpenedResult);				
		m_OpenedResult = null;
		}
	else
		{
		m_ResultViewer.clear();	
		}	
	}

//---------------------------------------------------------------------------

public void resultChanged (ResultChangeEvent p_Event)
	{
	Result	l_Result;
		
		if (p_Event.getType() == ResultChangeEvent.c_ResultOpened)
		{
		if (p_Event.getResult() instanceof Result) 
			 l_Result = p_Event.getResult();
		else l_Result = this.getResultById(p_Event.getResultId());	
			
		if (l_Result != null) 
			{
			m_ResultViewer.renderResult(l_Result);
		    m_HTMLScroller.setViewportView (m_ResultViewer); 
			}
		}		
	}

//---------------------------------------------------------------------------

public void handleGECAMedMessage (GECAMedMessage p_Message)
	{
	ResultStub	l_Stub;
	Result		l_Result;
		
	if (   p_Message.getModul().equals(LaboModule.getInstance()) 
		&& p_Message.getMessage().equals(HistoryPanel.c_OpenLaboResultMessage))
		{
		l_Stub = (ResultStub) p_Message.getNewValue();	
		l_Result = this.getResultById(l_Stub.getId());	
			
		if (l_Result != null) 
			{
			MainFrame.getInstance().selectModule(PatientManagerModule.MODULE_NAME);
			PatientManagerModule.setCurrentPatient(l_Result.getPatient());
			PatientManagerModule.getInstance().getPatientPanel().showPanel(this.getName());
			
			m_OpenedResult = l_Stub;
			m_ResultViewer.renderResult(l_Result);
			m_HTMLScroller.setViewportView (m_ResultViewer); 		
			}
		}
	}

//---------------------------------------------------------------------------

public Collection<Action> getActions(IncidentEntry entry)
	{
	return null;
	}


public JPopupMenu getPopup(IncidentEntry entry)
	{
	return null;
	}

//---------------------------------------------------------------------------

public TableCellRenderer getRenderer(String p_EntryType)
	{
	if (m_HistoryRenderer == null) m_HistoryRenderer = new PatientHistoryRenderer ();
	return m_HistoryRenderer;
	}

//---------------------------------------------------------------------------

public HashMap<Integer, Object> getSpecialEntries (String entryType, Integer patientId)
	{
	return null;
	}

//---------------------------------------------------------------------------

public boolean openEntry (IncidentEntry p_Entry)
	{
	Result		l_Result;
		
	if ((p_Entry != null) && (p_Entry.isPersistent()))
		{
		l_Result = this.getResultByIncidentEntryId(p_Entry.getId());
			
		if (l_Result != null) 
			{
			PatientManagerModule.getInstance().getPatientPanel().showPanel(this.getName());
			
			m_OpenedResult = new ResultStub ();
			m_OpenedResult.setId (l_Result.getId());
			m_Results.selectResult(m_OpenedResult);	
			m_OpenedResult = null;
			
			m_ResultViewer.renderResult(l_Result);
			m_HTMLScroller.setViewportView (m_ResultViewer); 		
			return true;
			}
		}
		
	return false;
	}

//---------------------------------------------------------------------------

public void actionPerformed (ActionEvent p_Action)
	{	
	if (p_Action.getSource().equals(m_ExportAsXMLButton))
		{
		this.saveAsXMLFile();	
		}
	else if (p_Action.getSource().equals(m_ExportAsPDFButton))
		{
		this.saveAsPDFFile();	
		}
	else if (p_Action.getSource().equals(m_PrintResultButton))
		{
		this.printResult();
		}
	}

public IEntryPrintRenderer getPrintRenderer(String entryType) {
	/* ====================================================== */
	// TODO Auto-generated method stub
	return null;
	/* ====================================================== */
}

//---------------------------------------------------------------------------
//***************************************************************************
//* End of Class                                                            *
//***************************************************************************
//---------------------------------------------------------------------------
	}
