/*
 * JSPTem - use jsp's for all your templating!
 * Copyright (c) 2006, Paul Tuckey
 * All rights reserved.
 *
 * JSPTem is based on GNUJSP (Copyright (C) 1999, Yaroslav Faybishenko <yaroslav@cs.berkeley.edu>)
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
package org.tuckey.jsptem;

import org.tuckey.jsptem.servlet.JspTemServletContext;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.StringTokenizer;


public class JspToJavaCompiler {

    URL[] deps;
    boolean debug = true;
    boolean usePackages = true;
    private File destDir;
    private File srcDir;
    private String emitter;

    public JspToJavaCompiler() {
        this.destDir = new File(".");
    }

    public JspToJavaCompiler(File destDir, File srcDir) {
        this(destDir, srcDir, null);
    }

    public JspToJavaCompiler(File destDir, File srcDir, String emitter) {
        this.emitter = emitter;
        this.destDir = destDir;
        this.srcDir = srcDir;
    }

    public void exec() throws IOException {
        URL jspURL = JspToJavaCompiler.class.getResource("../spgen/DbConnTemplate.sql.jsp");
        String jspURI = jspURL.getFile();
        compileFile(jspURL, jspURI);

    }

    public void compileFileAnt(File f) throws IOException {
        log("file " + f + " " + srcDir);
        compileFile(f.toURL(), f.toURI().toString());
    }

    public void compileFile(URL jspURL, String jspURI) throws IOException {

        JspTemServletContext context = new JspTemServletContext();
        boolean pathDebug = true;

        JspParser parser = new JspParser(context);
        // for use of pagebase for some engines
        parser.setPageBase("");
        parser.setPathHints(true);

        StackedReader sr = new StackedReader(new InputStreamReader(jspURL.openStream()), jspURI, jspURL);
        // Alternative: pass context info in JspParser
        // by parameter ...
        JspParserContext jpctx = new JspParserContext(jspURI, jspURL);
        if (pathDebug)
            jpctx.setPathDebug(true);

        sr.setContextInfo(jpctx);
        JspNode body = null;

        try {
            body = parser.parse(sr);
        } catch (ParseException e) {
            throw e;
        } finally {
            sr.close();
        }

        // StackedReader knows what files were included.
        deps = sr.getAllIncludesAsResources();

        if (debug) {
            for (int i = 0; i < deps.length; i++) {
                URL dep = deps[i];
                log("Depends on " + dep);
            }
        }
        body.setPageAttributeObject("gnujspDeps", deps);
        body.setPageAttributeObject("gnujspCompilerVersion", new Integer(1));
        //body.setAttribute("gnujspCompilerVersion", String.valueOf(getCompilerVersion()));
        body.setAttribute("gnujspTimestamp", String.valueOf(getMaxTimestamp()));
        String className = getClassName(jspURI);
        String classPart = className;
        String packageName = null;
        int lastDot = className.lastIndexOf('.');
        if (lastDot != -1) {
            packageName = className.substring(0, lastDot);
            classPart = className.substring(lastDot + 1);
        }

        if (debug) {
            log("package " + packageName);
            log("class " + className);
            log("class part " + classPart);
        }

        // This is a kludge for JavaEmitter.
        body.setAttribute("package", packageName);
        body.setAttribute("class", classPart);

        File sourceFile = getFileName(destDir, className, ".java", true);
        if (debug) log("Emitting source to " + sourceFile);
        BufferedWriter w = new BufferedWriter(new FileWriter(sourceFile));
        if ("stringbuffer".equalsIgnoreCase(emitter)) {
            JspEmitterStringBuffer.emit(w, body);
        } else if ("servlet".equalsIgnoreCase(emitter)) {
            JspEmitterServlet.emit(w, body);
        } else {
            JspEmitterWriter.emit(w, body);
        }
        w.close();


    }


    public static File getFileName(File scratchdir, String classname, String ext, boolean mkdir) {
        File resp = new File(scratchdir, classname.replace('.', File.separatorChar) + ext);
        if (mkdir)
            (new File(resp.getParent())).mkdirs();
        return resp;
    }

    void log(String msg) {
        System.out.println(msg);
    }


    String getClassName(String jspURI) {
        String token;
        char c;
        int i;
        String srcDirUri = srcDir.toURI().toString();
        if (jspURI.startsWith(srcDirUri)) {
            jspURI = jspURI.substring(srcDirUri.length());
        }
        if (jspURI.contains(".")) {
            jspURI = jspURI.substring(0, jspURI.indexOf("."));
        }
        StringTokenizer st = new StringTokenizer(jspURI, "/");
        StringBuffer buf = new StringBuffer(jspURI.length() + 32);
        //buf.append("jsp");
        // String dirSep = usePackages ? "._" : "__";
        String dirSep = ".";
        int j = 0;
        int num = st.countTokens();
        while (st.hasMoreTokens()) {
            token = st.nextToken();
            if (j > 0) buf.append(dirSep);
            if (num - 1 == j) {
                buf.append("Generated");
            }
            for (i = 0; i < token.length(); i++) {
                c = token.charAt(i);
                if (Character.isJavaIdentifierPart(c))
                    buf.append(c);
                else
                    buf.append('_').append(Integer.toHexString(c));
            }
            if (num - 2 == j) {
                buf.append(".generated");
            }
            j++;
        }
        return buf.toString();
    }


    /**
     * Look at all dependencies (including source JSP) and find the
     * maximum value.  This will be set as the timestamp attribute of
     * the generated servlet.  System.currentTimeMillis() is not used
     * because the JSP files may exist on a separate server than the
     * servlet container.
     */
    long getMaxTimestamp() throws MalformedURLException, IOException {
        long maxstamp = 0;
        long l;
        for (int i = 0; i < deps.length; i++) {
            URL dep = deps[i];
            // Ugly kludge for local files.  Shame on Sun for not
            // providing getLastModified() for FileURLConnections.
            if (dep.toString().startsWith("file:")) {
                l = new File(dep.toString().substring(5)).lastModified();
            } else {
                l = dep.openConnection().getLastModified();
            }
            if (debug) log(dep + " timestamp " + l);
            if (l > maxstamp) maxstamp = l;
        }
        return maxstamp;
    }

}
