/*
** Luxor - XML User Interface Language (XUL) Toolkit
** Copyright (c) 2001, 2002 by Gerald Bauer
**
** This program is free software.
**
** You may redistribute it and/or modify it under the terms of the GNU
** General Public License as published by the Free Software Foundation.
** Version 2 of the license should be included with this distribution in
** the file LICENSE, as well as License.html. If the license is not
** included with this distribution, you may find a copy at the FSF web
** site at 'www.gnu.org' or 'www.fsf.org', or you may write to the
** Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139 USA.
**
** THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND,
** NOT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR
** OF THIS SOFTWARE, ASSUMES _NO_ RESPONSIBILITY FOR ANY
** CONSEQUENCE RESULTING FROM THE USE, MODIFICATION, OR
** REDISTRIBUTION OF THIS SOFTWARE.
**
*/

package luxor.template;

import java.io.*;
import java.util.*;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.context.AbstractContext;
import org.apache.velocity.context.Context;
import org.apache.velocity.exception.MethodInvocationException;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.ResourceNotFoundException;
import luxor.core.*;
import luxor.status.*;
import luxor.util.*;

public class TemplateManager
{
   static Logger T = Logger.getLogger( TemplateManager.class );

   static TemplateManager _manager;

   VelocityEngine _builtInEngine;

   LuxorDefaultVelocityContext _defaultContext;
   VelocityEngine _userEngine;


   private TemplateManager()
   {
      // init velocity template engines

      try
      {
         _builtInEngine = new VelocityEngine();

         Properties builtInProps = new Properties();
         builtInProps.setProperty( "resource.loader", "builtin" );
         builtInProps.setProperty( "builtin.resource.loader.description", "Built-In Velocity Template Resource Loader" );
         builtInProps.setProperty( "builtin.resource.loader.class", "luxor.template.BuiltInTemplateResourceLoader" );
         builtInProps.setProperty( "builtin.resource.loader.path", "" );
         builtInProps.setProperty( "builtin.resource.loader.cache", "true" );
         builtInProps.setProperty( "builtin.resource.loader.modificationCheckInterval", "0" );

         // use our own logger
         builtInProps.setProperty( "runtime.log.logsystem.class", "luxor.template.LuxorVelocityLogger" );

         _builtInEngine.init( builtInProps );

         _userEngine = new VelocityEngine();

         Properties userProps = new Properties();
         userProps.setProperty( "resource.loader", "xul" );
         userProps.setProperty( "xul.resource.loader.description", "Xul Velocity User Template Resource Loader" );
         userProps.setProperty( "xul.resource.loader.class", "luxor.template.XulTemplateResourceLoader" );
         userProps.setProperty( "xul.resource.loader.path", "" );
//     userProps.setProperty( "xul.resource.loader.cache", "true" );
//     userProps.setProperty( "xul.resource.loader.modificationCheckInterval", "0" );

         // fix: just for debuggin don't cache
         // todo: use property in future to avoid recompilation
         userProps.setProperty( "xul.resource.loader.cache", "false" );

         // use our own logger
         userProps.setProperty( "runtime.log.logsystem.class", "luxor.template.LuxorVelocityLogger" );

         _userEngine.init( userProps );
      }
      catch( Exception ex )
      {
         Xul.error( "failed to initialize velocity library: " + ex.toString() );
      }
   }

   public static TemplateManager getTemplateManager()
   {
      if( _manager == null )
         _manager = new TemplateManager();

      return _manager;
   }

   public String getBuiltInTemplate( String templateName )
   {
      return getBuiltInTemplate( Collections.EMPTY_MAP, templateName );
   }

   public String getBuiltInTemplate( Map data, String templateName )
   {
      try
      {
         Template template = _builtInEngine.getTemplate( templateName );
         VelocityContext context = new VelocityContext();

         // add user supplied data

         for( Iterator it = data.entrySet().iterator(); it.hasNext();  )
         {
            Map.Entry entry = ( Map.Entry ) it.next();
            context.put( ( String ) entry.getKey(), entry.getValue() );
         }

         StringWriter out = new StringWriter();

         template.merge( context, out );

         return out.toString();
      }
      catch( ResourceNotFoundException rex )
      {
         Xul.error( "template '" + templateName + "' not found: " + rex.toString() );
         return "";
      }
      catch( ParseErrorException pex )
      {
         Xul.error( "failed to parse template '" + templateName + "': " + pex.toString() );
         return "";
      }
      catch( Exception ex )
      {
         Xul.error( "failed to retrieve template '" + templateName + "': " + ex.toString() );
         return "";
      }
   }

   public String getTemplate( String templateName )
   {
      // aka getUserTemplate
      return getTemplate( Collections.EMPTY_MAP, templateName );
   }

   public String getTemplate( Map data, String templateName )
   {
      // aka getUserTemplate
      try
      {
         Template template = _userEngine.getTemplate( templateName );

         if( _defaultContext == null )
         {
            // fix: create dynamic VelocityContext for profile properties
            //   as they may change and we don't want to
            //   create a new context every time
            _defaultContext = new LuxorDefaultVelocityContext();
         }

         // note: context is chained to default context
         VelocityContext context = new VelocityContext( _defaultContext );

         // fix: today should be a dynamic property of default context
         context.put( "today", new Date() );

         // add user supplied data
         // note: we can just create a MapVelocityContext wrapper
         //   since the VelocityContext uses
         //   a sophisticated Reflection engine we don't
         //   want to reimplement

         Iterator it = data.entrySet().iterator();
         while( it.hasNext() )
         {
            Map.Entry entry = ( Map.Entry ) it.next();
            context.put( ( String ) entry.getKey(), entry.getValue() );
         }

         StringWriter out = new StringWriter();

         template.merge( context, out );

         return out.toString();
      }
      catch( ResourceNotFoundException rex )
      {
         Xul.error( "template '" + templateName + "' not found: " + rex.toString() );
         return "";
      }
      catch( ParseErrorException pex )
      {
         Xul.error( "failed to parse template '" + templateName + "': " + pex.toString() );
         return "";
      }
      catch( Exception ex )
      {
         Xul.error( "failed to retrieve template '" + templateName + "': " + ex.toString() );
         return "";
      }
   }

   public String getTemplateFromFile( Map data, File templateFile )
   {
      if( _defaultContext == null )
      {
         // fix: create dynamic VelocityContext for profile properties
         //   as they may change and we don't want to
         //   create a new context every time
         _defaultContext = new LuxorDefaultVelocityContext();
      }

      // note: context is chained to default context
      VelocityContext context = new VelocityContext( _defaultContext );

      // fix: today should be a dynamic property of default context
      context.put( "today", new Date() );

      // add user supplied data
      // note: we can just create a MapVelocityContext wrapper
      //   since the VelocityContext uses
      //   a sophisticated Reflection engine we don't
      //   want to reimplement

      Iterator it = data.entrySet().iterator();
      while( it.hasNext() )
      {
         Map.Entry entry = ( Map.Entry ) it.next();
         context.put( ( String ) entry.getKey(), entry.getValue() );
      }

      try
      {
         // todo: as an alternative add temporarily
         //  a FileResourceLoader
         //  this lets users include templates in templates and more

         String template = FileUtils.getFileAsString( templateFile );

         StringWriter out = new StringWriter();
         _userEngine.evaluate( context, out, "dynamic-template", template );

         return out.toString();
      }
      catch( ParseErrorException pex )
      {
         Xul.error( "failed to parse template '" + templateFile.getAbsolutePath() + "': " + pex.toString() );
         return "";
      }
      catch( Exception ex )
      {
         Xul.error( "failed to retrieve template '" + templateFile.getAbsolutePath() + "': " + ex.toString() );
         return "";
      }
   }

}