package baf.guide;
import baf.util.*;
import java.io.*;
import java.util.*;
import javax.servlet.*;

public class Index {
  long lastModified = 0;
  Hashtable games = new Hashtable(255);
  private SortedList categories = new SortedList();

  static Hashtable indices;
  String tag, none, name, path;
  Item noneItem = null;

  static String indexInit[] = {
    ":a", "Anonymous", "Index of Authors", "/authors/",
    ":d", null, "Index of Development Systems", "/devsys/",
    ":g", null, "Index of Genres", "/genres/",
    ":l", null, "Index of Languages", "/lingvoj/",
    ":p", null, "Index of Platforms", "/platforms/",
    ":r", "Not rated", "Index by Rating", "/ratings/",
    ":y", null, "Index by Date", "/years/",
    ":c", null, "Index by Date Reviewed", "/chron/"
  };

  static {
    indices = new Hashtable();
    for (int i=0; i<indexInit.length; i += 4) {
      Index idx =
	new Index(indexInit[i], indexInit[i+1], indexInit[i+2], indexInit[i+3]);
    }
  }

  public static Index forTag(String tag) {
    Index i = (Index)indices.get(tag);
    if (i == null)
      throw new IllegalArgumentException("No index for tag "+tag);
    return i;
  }

  public static Index forPath(String path) {
    return forTag(path);
  }

  public Index(String tag, String none, String name, String path) {
    this.tag = tag;
    this.none = none;
    this.name = name;
    this.path = path;
    indices.put(tag, this);
    indices.put(path, this);
    try {
      if (none != null) noneItem = Item.forPath(path+Item.nameToUrl(none));
    } catch (IOException e) {
      throw new IllegalArgumentException("Failed to create item for "+none);
    }
  }

  public final void classify(Game g) throws IOException {
    String s = g.attributes.getProperty(tag);
    if (s == null) s = none;
    if (s == null) return;
    StringTokenizer st = new StringTokenizer(s, "/");
    while (st.hasMoreElements()) {
      String key = st.nextToken();
      SortedList list = (SortedList)games.get(key);
      if (list == null) {
	list = new SortedList();
	games.put(key, list);
	// add to the category list, unless it's the "none" category
	Item newItem = Item.forPath(path+Item.nameToUrl(key));
	if (none != null && none.equals(key))
	  noneItem = newItem;
	else categories.add(newItem);
      }
      list.add(g);
    }
  }

  public synchronized final void update() throws IOException {
    long max = lastModified;
    Game.update();
    if (Game.globalLastModified > lastModified) {
      // take out the obsolete stuff
      if (Game.lastObsolete > lastModified) {
	for (Enumeration keys = games.keys(); keys.hasMoreElements(); ) {
	  String key = (String)keys.nextElement();
	  Vector obs = new Vector();
	  SortedList list = (SortedList)games.get(key);
	  for (Enumeration e = list.elements(); e.hasMoreElements();) {
	    Game g = (Game)e.nextElement();
	    if (g.obsolete) obs.addElement(g);
	  }
	  if (obs.size() > 0) {
	    for (Enumeration e = obs.elements(); e.hasMoreElements(); ) {
	      Game g = (Game)e.nextElement();
	      list.remove(g);
	    }
	    if (list.size() == 0) {
	      games.remove(key);
	      categories.remove(Item.forPath(path+Item.nameToUrl(key)));
	    }
	  }
	}
      }
      // add in the new stuff
      for (Enumeration e = Game.all.elements(); e.hasMoreElements(); ) {
	Game g = (Game)e.nextElement();
	if (g.lastModified > lastModified) classify(g);
	if (g.lastModified > max) max = g.lastModified;
      }
    }
    lastModified = max;
  }

  public final Game[] search(String value) throws IOException {
    update();
    if (value == null) value = none;
    if (value == null) throw new NoGamesException();
    SortedList list = (SortedList)games.get(value);
    if (list == null) throw new NoGamesException();
    Game arr[] = new Game[list.size()];
    list.copyInto(arr);
    return arr;
  }

  public final void header(ServletOutputStream out) throws IOException {
    Item.header(out, name, "Text Adventures: "+name);
  }

  void list(ServletOutputStream out, Item category) throws IOException {
      SortedList list = (SortedList)games.get(category.name);
      out.print("<li>");
      category.hotlink(out, category.rawname, category.getListingString());
      out.print(" (");
      out.print(list.size());
      out.print(")\n");
  }    

  public int indexOf(Item i) {
    return categories.indexOf(i);
  }

  public Item itemAt(int i) throws IOException {
    try {
      return (Item)categories.elementAt(i);
    } catch (ArrayIndexOutOfBoundsException e) {
      if (i == -1) return noneItem;
      else return null;
    }
  }

  public int size() {
    return categories.size();
  }

  public void page(ServletOutputStream out) throws IOException {
    update();

    header(out);
    out.print("<h2>");
    out.print(name);
    out.print("</h2>\n<ul>\n");

    if (none != null) list(out, noneItem);
    for (Enumeration e = categories.elements(); e.hasMoreElements(); )
      list(out, (Item)e.nextElement());
    out.print("</ul>");

    Item.footer(out);
  }

}
