/*
 *  Copyright (C) 2005, 2006 Mikhail A. Kryshen
 *
 *  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
 *
 *  $Id: Standard.java,v 1.5 2006/12/14 14:39:26 mikhail Exp $
 */

package kryshen.tema.functions;

import java.io.*;
import java.util.*;
import java.net.*;

import java.sql.SQLException;
import java.sql.PreparedStatement;

import kryshen.tema.*;

/**
 * Standard TEMA functions.
 */
public class Standard {
    public static final Function SET =
        new Function() {
            public int invoke(FunctionDataParser fdp, Writer out)
                throws IOException, TemplateException {

                String arg0 = fdp.getNextArg();
                String arg1 = fdp.getData();
                
                fdp.getTemplateParser().setValue(arg0, arg1);
                
		out.write(arg0);
                return 1;
            }
        };

    /* prepare:qury_name sql_statement */
    public static final Function PREPARE =
        new Function() {
            public int invoke(FunctionDataParser fdp, Writer out)
                throws IOException, TemplateException {

                String arg0 = fdp.getNextArg();
                String data = fdp.getData();

		PreparedStatement statement;

		try {
		    statement = Tema.getDbConnection()
                        .prepareStatement(data);
		} catch (SQLException e) {
		    throw new TemplateException
                        (e.getMessage(), e, fdp.getTemplateReader());
		}
                
                fdp.getTemplateParser().setValue(arg0, statement);   
             
		out.write(arg0);
                return 1;
            }
        };

    /* query:sql_query template arg1 arg2 ... argN */
    public static final Function QUERY =
        new Function() {
           public int invoke(FunctionDataParser fdp, Writer out)
                throws IOException, TemplateException {
                
                String arg0 = fdp.getNextArg();
                String arg1 = fdp.getNextArg();
		List<String> args = fdp.getArgs();				

		TemplateParser tp = fdp.getTemplateParser();

                TemplateReader tr = 
                    new TemplateReader(new StringReader(arg1),
                                       fdp.getTemplateReader());

		try {
		    return Tema.query
			(tr, (PreparedStatement)tp.getValue(arg0),
			 args, tp, out);
		} catch (SQLException e) {
		    throw new TemplateException
                        (e.getMessage(), e, fdp.getTemplateReader());
		}
            }
        };

    public static final Function REPLACE =
        new Function() {
	    public int invoke(FunctionDataParser fdp, Writer out)
		throws IOException, TemplateException {		

                String arg0 = fdp.getNextArg();
                String arg1 = fdp.getNextArg();

                ReplaceWriter rw = new ReplaceWriter(out, arg0, arg1);

                int ret = fdp.parseData(rw);
                rw.finish();
                return ret;
            }
        };

    public static final Function LOAD =

        new Function() {
            public int invoke(FunctionDataParser fdp, Writer out)
                throws IOException, TemplateException {
                
                final String name = fdp.getNextArg();
                final String className = fdp.getNextArg();

                List<URL> urls = new ArrayList<URL>(1);

                while (fdp.hasMoreData()) {
                    urls.add(new URL(fdp.getNextArg()));                    
                }
                
                ClassLoader loader = this.getClass().getClassLoader();

                if (urls.size() > 0) {
                    loader = new URLClassLoader
                        (urls.toArray(new URL[0]), loader);
                }                

                Class<Function> functionClass;
                Function function;

                try {
                    functionClass = loadFunctionClass(loader, className);
                } catch (ClassNotFoundException e) {
                    throw new TemplateException
                        ("Class not found", e, fdp.getTemplateReader());
                } catch (ClassCastException e) {
                    throw new TemplateException
                        ("Not a function class", e, fdp.getTemplateReader());
                }
                
                try {
                    function = functionClass.newInstance();
                } catch (InstantiationException e) {
                    throw new TemplateException
                        ("Could not load class", e, fdp.getTemplateReader());
                } catch (IllegalAccessException e) {
                    throw new TemplateException
                        ("Could not load class", e, fdp.getTemplateReader());
                }

                fdp.getTemplateParser().registerFunction(name, function);
                                

                out.write(name);
                return 1;
            }
        };

    public static final Function NULL_OUTPUT =
        new Function() {
	    public int invoke(FunctionDataParser fdp, Writer out)
		throws IOException, TemplateException {		

		/* Write nothing. */
		return fdp.parseData(new Writer() {
                    public void close() {}
                    public void flush() {}
                    public void write(char[] cbuf, int off, int len) {}
                });
            }
        };

    public static final Function XML_ESCAPE =
        new Function() {
	    public int invoke(FunctionDataParser fdp, Writer out)
		throws IOException, TemplateException {		

                final String[] chars = {"&", "<", ">", "`", "\""};
                final String[] escape = {"&", "<", ">", "'", """};

                ReplaceWriter rw = new ReplaceWriter(out, chars, escape);

                int ret = fdp.parseData(rw);
                rw.finish();
                return ret;
            }
        };

    public static final Function XML_CDATA =
        new Function() {
	    public int invoke(FunctionDataParser fdp, Writer out)
		throws IOException, TemplateException {		

                ReplaceWriter rw = new ReplaceWriter
                    (out, "]]>", "]]]><![CDATA[]>", "<![CDATA[", "]]>");

                int ret = fdp.parseData(rw);
                rw.finish();
                return ret;
            }
        };


    @SuppressWarnings("unchecked")
    private static Class<Function> loadFunctionClass
        (ClassLoader loader, String className)
        throws ClassNotFoundException {
        
        return (Class<Function>)loader.loadClass(className);
    }

    /**
     * Update file (copy if newer).
     *
     * @param src source file.
     * @param dest destination file.
     *
     * @trows IOException on copying error.
     */
    private static void copyFile(File src, File dest) throws IOException {
	System.err.print("Copying " + src + "... ");
	
	if (src.lastModified() < dest.lastModified()) {
	    System.err.println(dest + " is up to date.");
	    return;
	}

	File parent = dest.getParentFile();
	if (parent != null) parent.mkdirs();

        InputStream in = new FileInputStream(src);
        OutputStream out = new FileOutputStream(dest);
        
        byte[] buffer = new byte[1024];
        int len;
        while ((len = in.read(buffer)) > 0) {
            out.write(buffer, 0, len);
        }
        in.close();
        out.close();

	System.err.println("saved " + dest + ".");
    }
}