package COM.odi.demo.treeCatalog;

// Import the java related classes used in the example.
import java.io.*;
import java.util.*;

import COM.odi.*;
import COM.odi.util.*;
import COM.odi.util.query.*;

public class CatalogManager {
  private static String dbName = "treecatalog.odb";
  /* The database that this CatalogManager is operation against. */
  private static Database db = null;

  // Class storage for the HOLLOW roots.
  private static OSTreeSet catalogRoot = null;
  private static OSVectorList booksRoot = null;
  private static OSVectorList moviesRoot = null;

  private static String CATALOG_ROOT_NAME = "Catalog";
  private static String BOOKS_ROOT_NAME = "Books";
  private static String MOVIES_ROOT_NAME = "Movies";

  public CatalogManager() {
    try {
      // The following line starts a nonglobal session and joins
      // this thread to the new session.  This allows the thread
      // to use PSE Pro.
      Session.create(null, null).join();
      String threadName = Thread.currentThread().getName();
      String sessionName = Session.getCurrent().getName();
      System.out.println("threadName = " + threadName);
      System.out.println("sessionName = " + sessionName);
      createDatabase(dbName);
    } finally {
      // Insure shutdown is performed even if exception is caught.
      db.close(); 
      Session.getCurrent().terminate();
    }
  }

  // This method will create and open the database that is specified
  //   on the command line when the Java application is invoked.
  public static void createDatabase(String dbName) {
    System.out.println("Starting createDatabase("+dbName+")");

    // Attempt to open and destroy the database specified on the
    // command line.  This ensures that that program creates a
    // new database each time the application is called.
    try {
      Database.open(dbName, ObjectStore.OPEN_UPDATE).destroy();
    } catch (DatabaseNotFoundException e) {
    }

    // Create a new database.
    db = Database.create(dbName,
       ObjectStore.ALL_READ | ObjectStore.ALL_WRITE);

    // Build the data and store it in the OSTreeSet before
    //  a transaction is started.
    // Create books.
    String authors[] = {"James Goodwill", "Steven D. Wilkinson"};
    Book servletBook = new Book("Developing Java Servlets", 
      "Cool book on Servlets", "Sams Publishing", authors);
    String authors2[] = {"Michael Morrison", "et. all"};
    Book javaBook = new Book("Java Unleashed, Second Edition", 
      "Cool book on Java", "Sams Publishing", authors2);

    // Create movies.
    String talent[] = {"Will Smith", "Tommy Lee Jones", 
      "Linda Fiorentino"};
    Movie menInBlack = new Movie("Men in Black", 
      "Funny alien invasion move", "Sci-Fi/Horror", 
      "VHS", "PG-13", talent);
    String talent2[] = {"Harrison Ford", "Gary Oldman"};
    Movie airForceOne = new Movie("Air Force One",
      "Thriller about hijacking of Air Force One", "Action/Adventure",
      "VHS", "PG-13", talent2);

    // Create instances catalog items.
    CatalogItem book1 = new CatalogItem(servletBook, 2);
    CatalogItem book2 = new CatalogItem(javaBook, 1);
    CatalogItem movie1 = new CatalogItem(menInBlack, 1);
    CatalogItem movie2 = new CatalogItem(airForceOne, 3);

    System.out.println("book1.getName()=" + book1.getName());
    System.out.println("book2.getName()=" + book2.getName());
    System.out.println("movie1.getName()=" + movie1.getName());
    System.out.println("movie2.getName()=" + movie2.getName());

    // Start an update transaction.
    Transaction tr = Transaction.begin(ObjectStore.UPDATE);

    catalogRoot = new OSTreeSet(db);
    catalogRoot.add(book1);
    catalogRoot.add(book2);
    catalogRoot.add(movie1);
    catalogRoot.add(movie2);
    db.createRoot("Catalog", catalogRoot);

    booksRoot = new OSVectorList(2);
    booksRoot.addElement(book1);
    booksRoot.addElement(book2);
    db.createRoot("Books", booksRoot);

    moviesRoot = new OSVectorList(2);
    moviesRoot.addElement(movie1);
    moviesRoot.addElement(movie2);
    db.createRoot("Movies", moviesRoot);

    // all database references will be STALE.
    tr.commit();
    System.out.println("Ending createDatabase("+dbName+")");
  }

  // Populate the HOLLOW Roots.
  public static void getRoots() {
    // Start a read-only transaction:
    Transaction tr = Transaction.begin(ObjectStore.READONLY);

    catalogRoot = (OSTreeSet)db.getRoot(CATALOG_ROOT_NAME);
    booksRoot = (OSVectorList)db.getRoot(BOOKS_ROOT_NAME);
    moviesRoot = (OSVectorList)db.getRoot(MOVIES_ROOT_NAME);

    // End the read-only transaction.  This makes the roots
    //  HOLLOW so later when used in a transaction, they don't
    //  have to be read from the database.
    tr.commit(ObjectStore.RETAIN_HOLLOW);
  }

  // This method reads the catalogItems out of the database.
  public static void readDatabase(Database db) {
    System.out.println("Starting readDatabase("+dbName+")");

    // Start a read-only transaction:
    Transaction tr = Transaction.begin(ObjectStore.READONLY);

    // Use the HOLLOW root object.  The object will be refreshed 
    //  in the transaction with this access.
    // Currently in COM.odi.util.* package, but will be in JDK1.2 
    Iterator iterator = catalogRoot.iterator();

    CatalogItem item = null;
    int itemCount = 0;
    while(iterator.hasNext()) {
      item = (CatalogItem) iterator.next();
      itemCount++;
      System.out.println("item[" + itemCount + "]="+ item.toString());
    }

    // End the read-only transaction. This ends the
    // accessibility of the persistent objects and abandons
    // the transient objects.
    tr.commit(ObjectStore.RETAIN_HOLLOW);
    System.out.println("Ending readDatabase("+dbName+")");
  }

  public static void runProductQuery() {
    // Construct a query on all Book classes to find out how
    //  many books there are in the CatalogItems.
    Query productQuery = new Query(CatalogItem.class, "getProductType() == \"Book\"");

    long start = System.currentTimeMillis();
    // Start transaction.
    Transaction tr = Transaction.begin(ObjectStore.READONLY);

    Collection result = productQuery.select(catalogRoot);
    System.out.println("Found " + result.size() + " Book.");

    long stop = System.currentTimeMillis(); 
    System.out.println("Time: " + (stop - start) + " milliseconds");

    Iterator iterator = result.iterator();
    while(iterator.hasNext()) {
      System.out.println("value="+iterator.next());
    }
    tr.commit(ObjectStore.RETAIN_HOLLOW);
  }

  public static void addIndexToCatalogItems() {

    // Start transaction.
    Transaction tr = Transaction.begin(ObjectStore.UPDATE);

    catalogRoot.addIndex(CatalogItem.class, "getProductType()");

    tr.commit(ObjectStore.RETAIN_HOLLOW);
    //db.close();
  }

  //Main
  public static void main(String argv[]) {
    CatalogManager catalogManager = new CatalogManager();

    try {
      Session.create(null, null).join();
      String threadName = Thread.currentThread().getName();
      String sessionName = Session.getCurrent().getName();
      System.out.println("threadName = " + threadName);
      System.out.println("sessionName = " + sessionName);
      try {
        db = Database.open(dbName, ObjectStore.OPEN_UPDATE);
      } catch (DatabaseNotFoundException e) {
        System.out.println("database("+dbName+") not found.");
      }

      getRoots();
      readDatabase(db);
      // Note a db.close make all persistent objects stale.
      System.out.println("Product Query before Index.");
      runProductQuery();
      addIndexToCatalogItems();
      System.out.println("Product Query after Index.");
      runProductQuery();
    } finally {
      // Insure shutdown is performed even if exception is caught.
      db.close(); 
      Session.getCurrent().terminate();
    }
  }
}
