package org.plenix.jsp;

import com.ibm.xml.parser.*;
import com.kav.xsl.*;
import java.io.*;
import java.lang.reflect.*;
import java.util.*;
import org.w3c.dom.*;

import org.plenix.jsp.language.*;

class LanguageLoader
{
  private static Hashtable languages;

  static
  {
    try
    {
      languages = new Hashtable();
      InputStream input = ResourceManager.getResourceStream("languages.xml");

      Document document = new Parser("languages.xml").readStream(input);

      Element root = document.getDocumentElement();
      NodeList languageList = root.getElementsByTagName("language");

      for (int i = 0; i < languageList.getLength(); i++)
      {
        Element language = (Element) languageList.item(i);
        loadLanguage(language);
      }
    }
    catch (Exception e)
    {
      System.err.println("Error loading language definitions: " + e.getMessage());
      System.err.println("Exception: " + e.getClass().getName());
    }
  }

  protected static LanguageProcessor getLanguage(String name)
  {
    return (LanguageProcessor) languages.get(name);
  }

  protected static Enumeration languages()
  {
    return languages.elements();
  }

  private static void loadLanguage(Element element)
    throws Exception
  {
    LanguageProcessor language = null;
    String processor = element.getAttribute("processor");

    if (processor != null) // Supporting Java class
    {
      language = loadLanguageProcessor(processor);
    }
    else // Scripting used to support language
    {
      ScriptedLanguage scriptedLanguage = new ScriptedLanguage();

      scriptedLanguage.setInitScript(getText(element, "init-script"));
      scriptedLanguage.setCompileScript(getText(element, "compile-script"));
      scriptedLanguage.setLoadScript(getText(element, "load-script"));
      scriptedLanguage.setUnloadScript(getText(element, "unload-script"));
      scriptedLanguage.setExecuteScript(getText(element, "execute-script"));

      language = scriptedLanguage;
    }

    String sourceExtension = element.getAttribute("source-extension"); // *** Validate uniqueness
    String objectExtension = element.getAttribute("object-extension"); // *** Validate uniqueness

    language.setSourceExtension(sourceExtension);
    language.setObjectExtension(objectExtension);

    NodeList versions = element.getElementsByTagName("version");

    for (int i = 0; i < versions.getLength(); i++)
    {
      Element version = (Element) versions.item(i);

      String versionName = version.getAttribute("name");
      String stylesheetName = version.getAttribute("stylesheet");

      InputStream input = ResourceManager.getResourceStream(stylesheetName);

      XSLReader xslReader = new XSLReader();
      XSLStylesheet stylesheet = xslReader.readStylesheet(input, stylesheetName);

      language.addStylesheet(versionName, stylesheet);
    }

    NodeList parameters = element.getElementsByTagName("param");

    for (int i = 0; i < parameters.getLength(); i++)
    {
      Element parameter = (Element) parameters.item(i);

      String parameterName = parameter.getAttribute("name");
      String parameterValue = parameter.getAttribute("value");

      language.addParameter(parameterName, parameterValue);
    }

    String languageNames = element.getAttribute("name");
    StringTokenizer st = new StringTokenizer(languageNames, "|");

    while (st.hasMoreTokens())
    {
      String name = st.nextToken();
      language.addName(name);

      languages.put(name, language);
    }

    // Language load complete: invoke init() method
    language.init(ResourceManager.getStageDirectory());
  }

  private static LanguageProcessor loadLanguageProcessor(String className)
    throws Exception
  {
    Object[] initArgs = { };
    Class[] parameterTypes = { };

    Class languageClass = java.lang.Class.forName(className);
    Constructor constructor = languageClass.getConstructor(parameterTypes);

    return (LanguageProcessor) constructor.newInstance(initArgs);
  }

  // *** FIXME!!!
  private static String getText(Element element, String name)
  {
    NodeList nodeList = element.getElementsByTagName(name);

    if (nodeList.getLength() == 0)
    {
      return null;
    }

    element = (Element) nodeList.item(0);
    Node node = element.getFirstChild();

    return node.getNodeValue();
  }
}
