/*
 * Created on 2004-3-14
 *
 * To change the template for this generated file go to
 * Window - Preferences - Java - Code Generation - Code and Comments
 */
package workflow.script.compiler;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import workflow.script.Options;
import workflow.script.ScriptCompilationContext;
import workflow.script.ScriptException;
import workflow.script.interpretor.ScriptGenerator;
import workflow.script.interpretor.ScriptWriter;

/**
 * @author zosatapo
 *
 * To change the template for this generated type comment go to
 * Window - Preferences - Java - Code Generation - Code and Comments
 */
public class Compiler
{
	private static Log log = LogFactory.getLog(Compiler.class);

	// ----------------------------------------------------- Instance Variables
	protected ScriptCompilationContext ctxt;
	
	protected Options options;

	// ------------------------------------------------------------ Constructor
	public Compiler(ScriptCompilationContext ctxt)
	{
		this.ctxt = ctxt;
		this.options = ctxt.getOptions();
	}

	// --------------------------------------------------------- Public Methods

	/** 
	 * Compile the jsp file into equivalent servlet in .java file
	 */
	private void generateJava() throws Exception
	{
		long t1 = System.currentTimeMillis();

		String javaFileName = ctxt.getServletJavaFileName();

		// Setup the WfsScriptWriter
		String javaEncoding = ctxt.getOptions().getJavaEncoding();
		OutputStreamWriter osw = null;
		try
		{
			osw = new OutputStreamWriter(new FileOutputStream(javaFileName), javaEncoding);
		}
		catch (UnsupportedEncodingException ex)
		{
			if (log.isErrorEnabled())
			{
				log.error("jsp.error.needAlternateJavaEncoding" + javaEncoding);
			}
		}

		ScriptWriter writer = new ScriptWriter(new PrintWriter(osw));
		ctxt.setWriter(writer);

		// Parse the file
		long t2 = System.currentTimeMillis();
		// generate  .java file for the wfs file
		ScriptGenerator.generate(writer, this);
		try
		{
			writer.close();
		}
		catch (IOException e)
		{
		}

		long t3 = System.currentTimeMillis();
		if (t3 - t1 > 500)
		{
			log.debug("Generated " + javaFileName + " total=" + (t3 - t1) + " generate=" + (t3 - t2));
		}
	}

	/** 
	 * Compile the servlet from .java file to .class file
	 */
	private void generateClass() throws FileNotFoundException, ScriptException, Exception
	{
		long t1 = System.currentTimeMillis();
		String javaFileName = ctxt.getServletJavaFileName();
		String classpath = ctxt.getClassPath();

		StringBuffer info = new StringBuffer();
		info.append("Compile: javaFileName=" + javaFileName + "\n");
		info.append("    classpath=" + classpath + "\n");

		// Start capturing the System.err output for this thread
		//SystemLogHandler.startCapture();

		log.debug("Using classpath: " + classpath);

		// Initializing sourcepath
		File srcPath = options.getScratchDir();
		info.append("    work dir=" + options.getScratchDir() + "\n");

		ScriptCompiler compiler = null;
		String compilerClassName=options.getCompiler();
		if(compilerClassName!=null)
		{
			try
			{
				Class clazz=ctxt.getClassLoader().loadClass(compilerClassName);
				compiler=(ScriptCompiler)clazz.newInstance();
				
				info.append("    compiler=" + compilerClassName + "\n");
			}
			catch(Exception ex)
			{
				compiler=null;
			}
		}
		
		if(compiler==null)
		{
			compiler=new JavaCompiler();
			info.append("    compiler=" + compiler.getClass().getName() + "\n");
		}
		compiler.setClassDebugInfo(options.getClassDebugInfo());
		compiler.setClassPath(classpath);
		compiler.setJavaFilePath(javaFileName);
		compiler.setJavaEncoding(options.getJavaEncoding());
		compiler.setOutputDir(srcPath.getAbsolutePath());
		compiler.setMsgOutput(new PrintWriter(System.err));
		
		try
		{
			compiler.compile();
		}
		catch (Exception ex)
		{
			log.error("Javac exception ", ex);
			log.error("Env: " + info.toString());
		}
		// Stop capturing the System.err output for this thread
		//String errorCapture = SystemLogHandler.stopCapture();

		if (!ctxt.keepGenerated())
		{
			File javaFile = new File(javaFileName);
			javaFile.delete();
		}

		long t2 = System.currentTimeMillis();
		if (t2 - t1 > 500)
		{
			log.debug("Compiled " + javaFileName + " " + (t2 - t1));
		}
	}

	/** 
	 * Compile the jsp file from the current engine context
	 */
	public void compile() throws ScriptException,FileNotFoundException, Exception 
	{
		compile(true);
	}

	/**
	 * Compile the jsp file from the current engine context.  As an side-
	 * effect, tag files that are referenced by this page are also compiled.
	 * @param compileClass If true, generate both .java and .class file
	 *                     If false, generate only .java file
	 */
	public void compile(boolean compileClass) throws ScriptException,FileNotFoundException, Exception 
	{

		try
		{
			generateJava();
			if (compileClass)
			{
				generateClass();
			}
		}
		finally
		{
			if (ctxt.getWriter() != null)
			{
				ctxt.getWriter().close();
				ctxt.setWriter(null);
			}
		}
	}

	/**
	 * This is a protected method intended to be overridden by 
	 * subclasses of Compiler. This is used by the compile method
	 * to do all the compilation. 
	 */
	public boolean isOutDated()
	{
		return isOutDated(true);
	}

	/**
	 * Determine if a compilation is necessary by checking the time stamp
	 * of the JSP page with that of the corresponding .class or .java file.
	 * 
	 * This method can by overidden by a subclasses of Compiler.
	 * @param checkClass If true, check against .class file,
	 *                   if false, check against .java file.
	 */
	public boolean isOutDated(boolean checkClass)
	{		
		String wfsFilePath = ctxt.getScriptFilePath();
		long wfsRealLastModified = 0;

		try
		{
			File scriptFile=new File(ctxt.getRealPath(wfsFilePath));
			if (!scriptFile.exists())
			{
				return true;
			}

			wfsRealLastModified = scriptFile.lastModified();
		}
		catch (Exception e)
		{
			
			return true;
		}

		long targetLastModified = 0;
		File targetFile = null;

		if (checkClass)
		{
			targetFile = new File(ctxt.getServletClassFileName());
		}
		else
		{
			targetFile = new File(ctxt.getServletJavaFileName());
		}
		
		if (!targetFile.exists())
		{
			log.info("Compiler: outdated: " + targetFile + " " + targetLastModified);
			return true;
		}

		targetLastModified = targetFile.lastModified();
		
		if (targetLastModified < wfsRealLastModified)
		{
			log.info("Compiler: outdated: " + targetFile + " " + targetLastModified);
			return true;
		}
		
		return false;

	}

	public ScriptCompilationContext getCompilationContext()
	{
		return ctxt;
	}
	/**
	 * Remove generated files
	 */
	public void removeGeneratedFiles()
	{
		try
		{
			String classFileName = ctxt.getServletClassFileName();
			if (classFileName != null)
			{
				File classFile = new File(classFileName);
				if (log.isDebugEnabled())
				{
					log.debug("Deleting " + classFile);
				}
				classFile.delete();
			}
		}
		catch (Exception e)
		{
			// Remove as much as possible, ignore possible exceptions
		}
		try
		{
			String javaFileName = ctxt.getServletJavaFileName();
			if (javaFileName != null)
			{
				File javaFile = new File(javaFileName);
				log.debug("Deleting " + javaFile);
				javaFile.delete();
			}
		}
		catch (Exception e)
		{
			// Remove as much as possible, ignore possible exceptions
		}
	}

	public void removeGeneratedClassFiles()
	{
		try
		{
			String classFileName = ctxt.getServletClassFileName();
			if (classFileName != null)
			{
				File classFile = new File(classFileName);
				log.debug("Deleting " + classFile);
				classFile.delete();
			}
		}
		catch (Exception e)
		{
			// Remove as much as possible, ignore possible exceptions
		}
	}
}
