package tk.eclipse.plugin.jspeditor.compiler;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * This compiler converts the JSP source code to the Java code
 * to make code completion in the scriptlet / expression.
 * <p>
 * <strong>Note:</strong> <code>compile()</code> is not thread safe.
 * 
 * @author Naoki Takezoe
 * @since 2.0.3
 */
public class JSPCompiler {
	
	private static final int NONE    = 0;
	private static final int LT      = 1;
	private static final int PARCENT = 2;
	private static final int SCRIPT  = 3;
	private static final int END     = 4;
	
	private static final Pattern IMPORT_PATTERN 
		= Pattern.compile("<%@\\s*page.+?import\\s*=\\s*\"(.+?)\"");
	
	private static final Pattern USEBEAN_PATTERN 
		= Pattern.compile("<jsp:useBean.+?>", Pattern.DOTALL);
	
	/**
	 * Do compile. This method is not thread safe.
	 * 
	 * @param source the raw JSP source code
	 * @return the compile result
	 */
	public static CompileResult compile(String source){
		
		StringBuffer header = new StringBuffer();
		Matcher matcher = IMPORT_PATTERN.matcher(source);
		while(matcher.find()){
			String[] imports = matcher.group(1).split(",");
			for(int i=0;i<imports.length;i++){
				String trimedImport = imports[i].trim();
				if(trimedImport.length()!=0){
					header.append("import ").append(trimedImport).append(";\n");
				}
			}
		}
		header.append("public class _xxx {\n");
		header.append("public static void doService() throws Exception {\n");
		// implicit objects
		header.append("javax.servlet.http.HttpServletRequest request = null;\n");
		header.append("javax.servlet.http.HttpServletResponse response = null;\n");
		header.append("javax.servlet.http.HttpServletSession session = null;\n");
		header.append("javax.servlet.jsp.JspWriter out = null;\n");
		header.append("javax.servlet.ServletContext application = null;\n");
		header.append("javax.servlet.ServletConfig config = null;\n");
		header.append("javax.servlet.jsp.HttpJspPage page = null;\n");
		header.append("javax.servlet.jsp.PageContext pageContext = null;\n");
		header.append("java.lang.Exception exception = null;\n");
		// jsp:useBean
		matcher = USEBEAN_PATTERN.matcher(source);
		while(matcher.find()){
			String useBean = matcher.group();
			String id   = getAttibuteValue(useBean, "id");
			String type = getAttibuteValue(useBean, "class");
			if(id!=null && type!=null){
				header.append(type + " " + id + " = null;\n");
			}
		}
		
		StringBuffer sb = new StringBuffer();
		int flag = NONE;
		for(int i=0;i<source.length();i++){
			char c = source.charAt(i);
			if(flag==NONE && c=='<'){
				flag = LT;
				sb.append(' ');
			} else if(flag==LT){
				if(c=='%'){
					flag = PARCENT;
				} else {
					flag = NONE;
				}
				sb.append(' ');
			} else if(flag==PARCENT){
				if(c=='@'){
					flag = NONE;
					sb.append(' ');
				} else if(c=='='){
					flag = SCRIPT;
					sb.append(' ');
				} else {
					flag = SCRIPT;
					sb.append(c);
				}
			} else if(flag==SCRIPT){
				if(c=='%'){
					flag = END;
				} else {
					sb.append(c);
				}
			} else if(flag==END){
				if(c=='>'){
					flag = NONE;
					sb.append("  ");
				} else {
					flag = SCRIPT;
					sb.append('%');
					sb.append(c);
				}
			} else if(c=='\n' || c=='\r' || c=='\t'){
				sb.append(c);
			} else {
				sb.append(' ');
			}
		}
		
		return new CompileResult(header.toString(), sb.toString(), "}}");
	}
	
	private static String getAttibuteValue(String source, String name){
		Pattern pattern = Pattern.compile(
				"\\s+" + name + "\\s*?=\\s*?\"(.+?)\"", Pattern.DOTALL);
		Matcher matcher = pattern.matcher(source);
		if(matcher.find()){
			return matcher.group(1);
		}
		return null;
	}
	
}
