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 = "sessioncatalog.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() {
		System.out.println("Start CatalogManger ctor.");
		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();
		}
		System.out.println("End CatalogManger ctor.");
	}

	// 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"};
    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);

    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);

    db.createRoot("Catalog", catalogRoot);

		// 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();
		}
	}
}
