
package oracle.jaccelerator.server;

import java.io.*;
import java.util.Hashtable;
import java.util.Enumeration;
import java.util.Date;
import java.util.Stack;

import java.sql.*;

import oracle.jdbc.*;
import oracle.sql.CHAR;
import oracle.sql.BLOB;

import oracle.aurora.rdbms.Handle;
import oracle.aurora.rdbms.ClassHandle;
import oracle.aurora.rdbms.Schema;

public class MinimizeNcompListAndDumpTC extends TransitiveClosureDumper
{
  String libPrefix;
  String DLLExtension;
  public MinimizeNcompListAndDumpTC (BLOB classListBlob, String libPrefix, String DLLExtension) 
    throws SQLException, IOException
  {
    super(classListBlob);
    this.libPrefix = libPrefix;
    this.DLLExtension = DLLExtension;
  }

  public MinimizeNcompListAndDumpTC (String libPrefix, String DLLExtension) 
    throws IOException
  {
    super();
    this.libPrefix = libPrefix;
    this.DLLExtension = DLLExtension;
  }

  public void validate (String prefix, String[] roots, Schema schema) 
    throws SQLException, IOException
  {
    for (int i = 0; i < roots.length; i++) {
      String name = roots[i];
      if (prefix != null) name = prefix + name;
      ClassHandle h = Handle.lookupClass(name, schema);
      if (h == null) {
        classNotFound(name, schema);
      }
      else if (h.status() == Handle.INVALID) {
        // warning("class to translate is invalid: " + name + " in " + schema);
        try {
          h.resolve();
        } catch (Exception e) {
          warning("-- MinimizeNcompListAndDumpTC.validate: " + e);
        }
      }
    }
  }

  public static boolean classNotFoundMustThrowError = false;
  private static void classNotFound (String name, Schema schema) 
    throws SQLException
  {
    String message = "MinimizeNcompListAndDumpTC: class to translate not found: " + name + " in " + schema;
    warning(message);
    if (classNotFoundMustThrowError) throw new SQLException(message);
  }

  Hashtable probedDLLs = new Hashtable();
  String o_home = (String)System.getProperty("oracle.aurora.rdbms.oracle_home");
  String oracle_home = (o_home.endsWith(File.separator)) ? o_home :  o_home + File.separator;
  String adminPath = oracle_home + "javavm" + File.separator + "admin" + File.separator;

  public boolean DLLExists (ClassHandle h) {
    String libraryLogicalName = h.getNcompLibNameAsCHAR().toString();
    if (libraryLogicalName == null) return false;
    if (probedDLLs.get(libraryLogicalName) != null) return true;
    String so_name = libPrefix + "jtc" +  libraryLogicalName.replace('-','_') + "." + DLLExtension;      
    System.out.println("-- so_name: " + so_name);
    File file = new File(adminPath, so_name);
    boolean exists = file.exists();
    System.out.println("-- exists: " + exists);
    if (exists) probedDLLs.put(libraryLogicalName, libraryLogicalName);
    return exists;
  }

  private static String[] stackToStringArray (Stack stack) {
    if (stack == null) return null;
    String[] result = new String[stack.size()];
    stack.copyInto(result);
    return result;
  }

  Stack validClasses = new Stack();
  Stack invalidClasses = new Stack();

  Stack ncompedClasses = new Stack();
  Stack notNcompedClasses = new Stack();

  Stack notFoundClasses = new Stack();

  Stack notNcompedModules = new Stack();
  Stack ncompedModules = new Stack();

  public String[] getValidClasses () { return stackToStringArray(validClasses); }
  public String[] getInvalidClasses () { return stackToStringArray(invalidClasses); }
  public String[] getNcompedClasses () { return stackToStringArray(ncompedClasses); }
  public String[] getNotNcompedClasses () { return stackToStringArray(notNcompedClasses); }
  public String[] getNotFoundClasses () { return stackToStringArray(notFoundClasses); }
  public String[] getNotNcompedModules () { return stackToStringArray(notNcompedModules); }
  public String[] getNcompedModules () { return stackToStringArray(ncompedModules); }

  public boolean appearsNcomped (ClassHandle h)
    throws SQLException, IOException
  {
    return (h.getNcompIsEnabled() && h.getNcompIsAllowed() && DLLExists(h));
  }

  public void gatherClassesThatNeedNcomp (String module, String[] classes, Schema schema, boolean forceAll)
    throws SQLException, IOException
  {
    boolean everythingInThisModuleIsNcomped = (forceAll ? false : true);
    String[] validOrNull = new String[classes.length];
    for (int i = 0; i < classes.length; i++) {
      String name = module + classes[i];
      ClassHandle h = Handle.lookupClass(name, schema);
      if (h == null) {
        classNotFound(name, schema);
        notFoundClasses.push(name);
      }
      else if (h.status() == Handle.VALID) {
        validOrNull[i] = classes[i];
        boolean appearsNcomped = appearsNcomped(h);
        if (appearsNcomped) ncompedClasses.push(name);
        else notNcompedClasses.push(name);
        everythingInThisModuleIsNcomped = everythingInThisModuleIsNcomped && appearsNcomped;
      }
      else invalidClasses.push(name);
    }

    if (everythingInThisModuleIsNcomped) { 
      if (module.equals("") == false)
        ncompedModules.push(module);
    }
    else {
      if (module.equals("") == false)
        notNcompedModules.push(module);
      for (int i = 0; i < classes.length; i++) {
        String validClass = validOrNull[i];
        if (validClass != null) validClasses.push(module + validClass);
      }
    }
  }

  public String[] computeEffectiveClassList (String[] roots) {
    Hashtable bag = new Hashtable();
    Enumeration e = validClasses.elements();
    while (e.hasMoreElements()) {
      String elt = (String)e.nextElement();
      String pkg = classToPackageName(elt);
      for (int i = 0; i < roots.length; i++) {
        if (inPackage(roots[i], pkg))
          bag.put(elt, pkg);
      }
    }
    String[] result = new String[bag.size()];
    int idx = 0;
    e = bag.keys();
    while (e.hasMoreElements()) {
      result[idx] = (String)e.nextElement();
      idx++;
    }
    return result;
  }

  String classToPackageName (String className) {
    return className.substring(0, className.lastIndexOf('/'));
  }

  boolean inPackage (String className, String packageName) {
    if (className.length() < 2 + packageName.length()) return false;
    if (className.startsWith(packageName) == false) return false;
    className = className.substring(packageName.length());
    if (className.indexOf('/') != 0) return false;
    if (className.lastIndexOf('/') > 0) return false;
    return true;
  }

  public String listClassesAndDumpTC (String[][] modules, String[] moduleNames, String schemaName, boolean forceAll) 
    throws SQLException, IOException
  {
    schemaName = schemaName.toUpperCase();
    if (schemaName.equals("INTERNAL")) schemaName = "SYS";
    try {
      Schema schema = Schema.lookup(schemaName);
      System.out.println("-- schema: " + schema);
      for (int i = 0; i < modules.length; i++) validate(moduleNames[i], modules[i], schema);
      for (int i = 0; i < modules.length; i++) gatherClassesThatNeedNcomp(moduleNames[i], modules[i], schema, forceAll);
      String[] roots = new String[validClasses.size()];
      validClasses.copyInto(roots);
      return super.traverseReferences(roots, schemaName);
    }
    catch (Exception e) {
      System.out.println("-- MinimizeNcompListAndDumpTC.minimizeListAndDump(): " + e);
      e.printStackTrace(System.out);
      throw new SQLException("" +e);
    }
  }  

  private Messages messages;
  private Date stamp = new Date();

  private void emitInfo(String name, String status) 
    throws SQLException, IOException
  {
    // classAttributesWriter.println(name + " " + status + " _ _ _");
    classAttributesWriter.println("# " + status + " " + name);
    messages.makeStatusRecord(name, status, stamp, false);
  }

  private void emitInfo(Stack list, String status, String comment) 
    throws SQLException, IOException
  {
    if (list.size() > 0) {
      classAttributesWriter.println();
      classAttributesWriter.println("# " + comment);
      classAttributesWriter.println();
      Enumeration e = list.elements();
      while(e.hasMoreElements()) {
        String name = (String)e.nextElement();
        emitInfo(name, status);
      }
    }
  }

  protected void dumpClasses_before () 
    throws SQLException, IOException
  {
    try {
      OracleDriver driver = new OracleDriver();
      Connection connection = driver.defaultConnection();
      messages = new Messages(connection, "prepare status");
      messages.prepareInfoTables();
      messages.clearStatusRecords();

      String jtc_h_checksum = 
        Dumper.parseKeywordValuePair(oracle_home + "javavm" + File.separator + "jahome" + 
                                     File.separator + "jtc.h", 
                                     "JTC_CHECKSUM_NCOMP_H");
      if (jtc_h_checksum == null) jtc_h_checksum = "UNKNOWN_CHECKSUM";
      classAttributesWriter.println("# " + 
                                    System.getProperty("os.name") + " " + 
                                    System.getProperty("os.arch") + " " + 
                                    jtc_h_checksum);

      emitInfo(invalidClasses, "INVALID", "Classes detected as invalid");
      emitInfo(ncompedClasses, "ALREADY_NCOMPED", "Classes that are already ncomped");
      emitInfo(notNcompedClasses, "NEED_NCOMPING", "Classes that need ncomping");
      emitInfo(notFoundClasses, "NOT_FOUND_IN_SCHEMA", "Classes that are not found on the server");

      emitInfo(notNcompedModules, "NEED_NCOMPING", "Ncomp libraries that need ncomping");
      //emitInfo(ncompedModules, "ALREADY_NCOMPED", "Ncomp libraries are ncomped");

      messages.finishInfoTables();
    }
    catch (Exception e) {
      throw new SQLException("failed to produce status report because of: " + e);
    }
    finally {
    }
  }

}


