// Copyright 1996, Marimba Inc. All Rights Reserved.
// @(#)SameGamePlugin.java, 1.11, 11/08/96

package samegame;

import java.io.*;
import java.util.*;

import marimba.plugin.*;
import marimba.io.*;
import marimba.util.*;

/**
 * This class is the server plugin
 * to manage high scores for the Samegame channel. 
 *
 * @author      Carl W. Haynes III
 * @version     1.11, 11/08/96
 */

public class SameGamePlugin extends Plugin {

    Vector highScores = new Vector(200);
    static final String HIGHSCOREFILE = "highscores.txt";
    File highScoreFile = null;
    static final int NOT_IN_TOPTEN = -1;
    int lowestScore = 0;
    
    /** check some of last high score list generated */
    Checksum highScoreChecksum = null;

    /** initialize */
    public synchronized void init() {
//	try {
//	    PrintStream ps = new PrintStream(new FileOutputStream(File.separator + "tmp" + File.separator + "txout.txt"));
//	    System.err = System.out = ps;
//	} catch (IOException e) {
//	    System.err.println("Failed to open stdout and stderr replacements:");
//	    e.printStackTrace();
//	}

	highScoreFile = getDataFile(HIGHSCOREFILE);
	// initialize array
	for (int i = 0 ; i < HighScoreManager.MAXHIGHSCORES ; i++) {
	    HighScore hs = new HighScore();
	    highScores.addElement(hs);
	}
	readHighScores();

	highScoreChecksum = calculateChecksum(scoresToBytes());
    }
    

    /**
      * read the high scores
      */
    public void readHighScores() {
	if (highScoreFile.exists() == false) {
	    return;
	}
	try {
	    FastInputStream fis = new FastInputStream(highScoreFile);
	    String s;
	    while ((s = fis.readLine()) != null) {
		DebugStr("reading file: " + s);
		add(s);
	    }
	    fis.close();
	}
	catch(IOException e) {
	    e.printStackTrace();
	    return;
	}
    }

    /** parse and add a new high score */
    public void add(String s) {
	StringTokenizer st = new StringTokenizer(s, "\t");
	if (st.hasMoreElements() == false)
	    return;
	String name = (String)st.nextElement();
	//-----
	if (st.hasMoreElements() == false)
	    return;
	String email = (String)st.nextElement();
	//-----
	if (st.hasMoreElements() == false)
	    return;
	String tmp = (String)st.nextElement();
	int score = 0;
	try {
	    score = Integer.parseInt(tmp);
	}
	catch(NumberFormatException e) {
	    e.printStackTrace();
	    return;
	}
	//-----
	if (st.hasMoreElements() == false)
	    return;
	tmp = (String)st.nextElement();
	int time = 0;
	try {
	    time = Integer.parseInt(tmp);
	}
	catch(NumberFormatException e) {
	    e.printStackTrace();
	    return;
	}
	Date date = new Date(time);

	HighScore hs = new HighScore(score, name, email, date);
	if (isDuplicate(hs) == false)
	    insert(hs);
    }
    
    /** finds location in top ten and inserts */
    public void insert(HighScore hs) {
	int i = place(hs.getScore());
	if (i == NOT_IN_TOPTEN)
	    return;
	DebugStr("insert at: [" + i + "] / " + hs.toString());
//	Thread.dumpStack();
	insertAt(hs, i);
    }

    /** finds location in top ten but does not insert */
    public void insertAt(HighScore hs, int place) {
	highScores.insertElementAt(hs, place);
	while (highScores.size() > HighScoreManager.MAXHIGHSCORES)
	    highScores.removeElementAt(HighScoreManager.MAXHIGHSCORES);
	lowestScore = ((HighScore)highScores.elementAt(HighScoreManager.MAXHIGHSCORES-1)).getScore();
    }

    /** finds location in top ten but does not insert */
    public int place(int score) {
	if (score <= lowestScore)
	    return(NOT_IN_TOPTEN);
	
	int placement = HighScoreManager.MAXHIGHSCORES-1;
	Vector v = highScores;
	for (int i = HighScoreManager.MAXHIGHSCORES-1 ; i >= 0 ; i--) {
	    HighScore hs = (HighScore)v.elementAt(i);
	    if (score < hs.getScore())
		return(placement);
	    placement = i;
	}


	return(placement);
    }
    
    /**
     * Process a request.
     */
    public synchronized void processRequest(RequestContext context) throws IOException {
	
	Vector logs = getLogEntries(context);
	for (Enumeration e = logs.elements() ; e.hasMoreElements() ; ) {
	    LogEntry le = (LogEntry)e.nextElement();
	    String s = new String(le.data, 0);
	    DebugStr("log entry: " + s);
	    add(s);

	    highScoreChecksum = calculateChecksum(scoresToBytes());
	}

	byte b[] = scoresToBytes();
	context.addFile(HIGHSCOREFILE, b, highScoreChecksum, PERSISTENT);
	
	if (highScoreFile == null)
	    DebugStr("highScoreFile == null");
	try {
	    if (highScoreFile.exists() == false) {
		File dir = new File(highScoreFile.getParent());
		if (dir.exists() == false) {
		    if (dir.mkdirs()){
		    }
		}
	    }

	    FastOutputStream fos = new FastOutputStream(highScoreFile);
	    for (Enumeration e = highScores.elements() ; e.hasMoreElements() ; ) {
		HighScore hs = (HighScore)e.nextElement();
		fos.println(hs.toString());
	    }
	    fos.close();
	}
	catch(IOException e) {
	    e.printStackTrace();
	}

	super.processRequest(context);

    }


    /**
      * returns byte array ready for passing back to channel
      */
    public byte[] scoresToBytes() {
	String s = "";
	for (Enumeration e = highScores.elements() ; e.hasMoreElements() ; ) {
	    HighScore hs = (HighScore)e.nextElement();
	    s += hs.toString();
	    if (e.hasMoreElements())
		s += "\n";
	}
	int len = s.length();
	byte b[] = new byte[len];
	s.getBytes(0, len, b, 0);
	return(b);
    }

    /**
      * hack because I don't understand why a duplicate is being sent through
      *
      * this is really ugly and slow, I need to ask Jonathan about it
      */
    public boolean isDuplicate(HighScore in) { 
	for (Enumeration e = highScores.elements() ; e.hasMoreElements() ; ) {
	    HighScore hs = (HighScore)e.nextElement();
	    if (in.toString().equals(hs.toString())) {
		DebugStr("!! found duplicate: " + hs.toString());
		return(true);
	    }
	}
	return(false);
    }
    
    /** debugging */
    public void DebugStr(String s) {
//	System.out.println(s);
    }

}


