package net.ihe.common.report;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.render.Renderer;
import javax.persistence.EntityManager;
import javax.servlet.ServletContext;
import net.ihe.gazelle.common.application.action.ApplicationPreferenceManager;
import net.ihe.gazelle.common.log.ExceptionLogging;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRExporterParameter;
import net.sf.jasperreports.engine.JasperCompileManager;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.design.JasperDesign;
import net.sf.jasperreports.engine.export.JRHtmlExporter;
import net.sf.jasperreports.engine.export.JRHtmlExporterParameter;
import net.sf.jasperreports.engine.query.JRJpaQueryExecuterFactory;
import net.sf.jasperreports.engine.util.JRLoader;
import net.sf.jasperreports.engine.util.JRSaver;
import net.sf.jasperreports.engine.xml.JRXmlLoader;
import org.hibernate.Session;
import org.jboss.seam.Component;
import org.jboss.seam.log.Logging;
import org.jboss.seam.log.Log;
 

public class ReportRenderer extends Renderer
{
   private static final Log log=Logging.getLog(ReportRenderer.class);

   @Override
   public void encodeEnd(FacesContext context,UIComponent component) throws IOException {
      super.encodeEnd(context,component);
      ResponseWriter writer=context.getResponseWriter();
      try {

         ServletContext servlet = (ServletContext) context.getExternalContext().getContext() ;
         String realPath = servlet.getRealPath("/img/reports" ) ;
         String applicationBaseName = ApplicationPreferenceManager.getApplicationUrlBaseName((EntityManager)Component.getInstance("entityManager") ) ;         
          
          
         ReportExporterManager.getListOfJRXMLSubReportsAndCompileThem() ;
         
         ReportComponent reportComponent=(ReportComponent)component; 
         String reportName=reportComponent.getReport();
         String reportPath  = null ;
         Map<String, Object> parameters=reportComponent.getParameters();
         
         
         String reportDir= ApplicationPreferenceManager.getGazelleReportsPath() ;
          
         int index=reportDir.lastIndexOf( File.pathSeparatorChar );
         reportPath = reportDir ;
         if(index==-1)
            { 
            reportPath  +=  File.separatorChar ;
            }
         reportPath+=reportName ;
         
         parameters=new HashMap<String, Object>(parameters); // TODO do we really need to copy this?
         parameters.put("SUBREPORT_DIR",reportDir);
         
         log.info("Report Path : " + reportPath ) ; 
         JasperReport jasperReport=getJasperReport(reportPath);
      
         HashMap<String, Object> hashMap = new HashMap<String, Object>() ;
         
         //Get current connection :
         EntityManager em = (EntityManager)Component.getInstance("entityManager") ;
         Session s = (Session)em.getDelegate() ;
          
         JasperPrint jasperPrint=JasperFillManager.fillReport(jasperReport, parameters,   s.connection() );
         JRHtmlExporter exporter=new JRHtmlExporter();

      //  servlet.setAttribute(BaseHttpServlet.DEFAULT_JASPER_PRINT_SESSION_ATTRIBUTE, jasperPrint);

        
         exporter.setParameter(JRExporterParameter.JASPER_PRINT,jasperPrint);
         exporter.setParameter(JRExporterParameter.OUTPUT_WRITER,writer);
         exporter.setParameter(JRHtmlExporterParameter.IS_USING_IMAGES_TO_ALIGN , Boolean.FALSE );
         exporter.setParameter(JRHtmlExporterParameter.IS_WHITE_PAGE_BACKGROUND , Boolean.TRUE  ) ;
      //  exporter.setParameter(JRHtmlExporterParameter.IS_WHITE_PAGE_BACKGROUND , Boolean.TRUE  ) ;
      //   exporter.setParameter(JRHtmlExporterParameter.SIZE_UNIT , "%"  ) ;
         exporter.setParameter(JRHtmlExporterParameter.IMAGES_URI,"/"+ applicationBaseName +"/img/reports/"+ reportName );
      //   exporter.setParameter(JRHtmlExporterParameter.IMAGES_DIR_NAME,"/opt/gazelle_data/reports/");
         exporter.setParameter(JRHtmlExporterParameter.IMAGES_MAP , hashMap ) ;
         exporter.exportReport()  ;              
        
         Set<String> setOfPictureName  =  hashMap.keySet() ;
         Iterator<String> itOfPictName = setOfPictureName.iterator() ;
         while( reportName != null && itOfPictName.hasNext()  )
         {
            
            String currentKey = itOfPictName.next() ;
            String newFilePicName = realPath + File.separatorChar+reportName + currentKey ;
            log.info(currentKey ) ;
            
            File newFilePic = new File(newFilePicName) ;
            if ( !newFilePic.exists() )
            {
               log.info( "try to create " + newFilePic ) ;
               if ( newFilePic.createNewFile() )
               {
                  FileOutputStream fis = new FileOutputStream(newFilePic) ;
                  fis.write( (byte[])hashMap.get(currentKey)   ) ;
               }
            }
         }
         
         log.info("stop ID control ! :)") ;

      }
      catch(JRException e) {
         ExceptionLogging.logException(e, log);
         log.error("could not generate report",e);
         PrintWriter out=new PrintWriter(writer);
         out.write("<span class=\"bnew\">JasperReports encountered this error :</span>\n");
         out.write("<pre>\n");
         e.printStackTrace(out);
         out.write("</pre>\n");
      }
   }

   /**
    * Adds the Seam managed EntityManager to the given parameters if we have Seam on the classpath.
    * 
    * @param parameters
    *            the parameters Map passed to JasperReports when filling the report.
    */
   private static void setSeamManagedEntityManager(Map<String, Object> parameters) {
      try { // perform org.jboss.seam.Component.getInstance("entityManager") to get the EntityManager
         Class<?> clazz=Class.forName("org.jboss.seam.Component");
         Method getInstance=clazz.getMethod("getInstance",String.class);
         Object entityManager=getInstance.invoke(null,"entityManager");
         parameters.put(JRJpaQueryExecuterFactory.PARAMETER_JPA_ENTITY_MANAGER,entityManager);
         log.info("setting EntityManager "+entityManager);
      }
      catch(Exception e) {
         ExceptionLogging.logException(e, log);
         log.info("Seam not in the classpath or other problem, EntityManager not set");
         // silently ignore exception, probably "Seamless" environment
      }
   }

   /**
    * Returns a JasperReport by its file name. If the report's jasper file is found and its date is before the jrxml file then it is loaded and returned.
    * Otherwise, we look for an jrxml file and compile it to the jasper file first.
    * 
    * @param reportName
    *            the name of the JasperReport to return.
    * @return the corresponding JasperReport ready to be filled.
    * @throws JRException
    *             if something went wrong.
    * @throws FileNotFoundException 
    */
   public static JasperReport getJasperReport(String reportName) throws JRException, FileNotFoundException {
      String jasperFilename= reportName+".jasper";
      String jrxmlFilename = reportName+".jrxml";
      
      //if(jasperFilename.startsWith("/")) jasperFilename=jasperFilename.substring(1); // strip off leading slash
      File jasperFile=new File(jasperFilename);
      File jrxmlFile =new File(jrxmlFilename);
      
      if(jasperFile.exists()) {
        if ( !jrxmlFile.exists() ) 
        {
           log.info("Unable to find " + jrxmlFile.getAbsolutePath() ) ;
           log.info("Loading " + jasperFilename ) ;
           
           JasperReport jasperReport=(JasperReport)JRLoader.loadObject(jasperFilename);
           return jasperReport;
        }
        else
        {
           log.info("Found " + jasperFilename ) ;
           log.info("and  " + jrxmlFilename ) ;
           
        Date dateJasperFile =  new Date(jasperFile.lastModified()) ;
        Date dateJrxmlFile  =  new Date(jrxmlFile.lastModified()) ;
         if ( dateJrxmlFile.before(dateJasperFile) )
         {
            log.info(" dateJrxmlFile before dateJasperFile") ;
            log.info("loading jasper file from "+ jasperFile.getPath());
            JasperReport jasperReport=(JasperReport)JRLoader.loadObject(jasperFilename);
            return jasperReport;
         }
        }
      }
      if(  !jrxmlFile.exists() )
      {
         
         throw new FileNotFoundException(" File " + jrxmlFile.getAbsolutePath() + " not found" ) ;
      }
      
      InputStream is= new FileInputStream( jrxmlFile )  ;
      if(is==null) throw new JRException("could not open "+ jrxmlFile.getAbsolutePath()  );
      log.info("loading jrxml file from "+jrxmlFilename+" and compiling it to "+ jasperFile.getPath() ) ;
      JasperDesign design=JRXmlLoader.load(is);
      JasperReport jasperReport=JasperCompileManager.compileReport(design);
      JRSaver.saveObject(jasperReport,jasperFile);
      return jasperReport;
   }
}
