/*
 *	EasyScout Boy Scouts of America tracking software.
 *	http://www.easyscout.org/
 *
 *	Copyright (C) 2000 Conan C. Albrecht
 *	conan@warp.cx
 *
 *	This program is released under the GNU license so tech-savvy
 *	Boy Scouts and others can enhance the program for the use
 *	of all.  Please e-mail changes to my address above so I can
 *	reintegrate into the main program at http://www.easyscout.org/.
 *	This way, all users mutually benefit one another in the spirit of
 *	scouting.
 *
 *	This program is free software; you can redistribute it and/or
 *	modify it under the terms of the GNU General Public License
 *	as published by the Free Software Foundation; either version 2
 *	of the License, or (at your option) any later version.
 *
 * 	Any and all changes to the program code must be documented with
 *  date, author name and e-mail, and a short description.
 *
 *	This program is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *	GNU General Public License for more details.
 *
 *	You should have received a copy of the GNU General Public License
 *	along with this program; if not, write to the Free Software
 *	Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

package cx.warp.scout.file;

import cx.warp.scout.*;
import Acme.Crypto.*;
import java.util.*;
import java.io.*;
import java.util.*;
import java.util.zip.*;

/** A simple cache for troops that need saving.  A cache is used so that save() can be called many times very quickly,
		and the class will only be saved once rather than many times */
public class SavingCache extends Thread {
	
	Hashtable troops = new Hashtable();
	boolean running = true;
	long wait = 1000 * 10; // 10 seconds
	private Des3Cipher cipher = null;
	
	/** Creates a new SavingCache.  The class automatically start()s itself. */
	public SavingCache() {
		start();
	}//constructor

	/** Retrieves a troop.  This is placed in SavingCache because the DES key is private to this class */
	public FileTroop getTroop(String name) throws Exception {
		File dir = new File(FileDataGate.DATA_DIR);
		File file = new File(dir, name); // we use this constructor so we don't have to worry about the ending slash being there or not
		ObjectInputStream in = null;
		
		// we've gone through three different file format versions
		// first was simple object saving; second was gzipping, third was encryption + gzipping
		// files are upgraded to encryption when they are opened and resaved.
		// therefore, inactive files might still be using a deprecated file version
		try { // try first with encryption + gzipping
			in = new ObjectInputStream(new GZIPInputStream(new EncryptedInputStream(cipher, new FileInputStream(file))));
		}catch (IOException ex) { 
			try {
				in = new ObjectInputStream(new GZIPInputStream(new FileInputStream(file)));
			}catch (IOException ex2) {
		  	in = new ObjectInputStream(new FileInputStream(file));
			}//try
		}//try
		FileTroop troop = (FileTroop)in.readObject();
		in.close();
		return troop;
	}//getTroop
	
	
	/** Adds a new troop to the cache */
	public synchronized void addTroop(Troop troop) throws DataException {
		if (troops.get(troop.getName()) == null) {
			troops.put(troop.getName(), troop);
		}//if
	}//addTroop
	
	/** Sets the DES Key */
	public synchronized void setKey(String key) {
		cipher = new Des3Cipher(key);
	}//setKEy
	
	/** Explicitly saves all troops in the cache */
	public synchronized void saveCache() {
		// save each troop in the cache
		for (Enumeration enum = troops.elements(); enum.hasMoreElements(); ) {
			Troop troop = (Troop)enum.nextElement();
			try {
				File dir = new File(FileDataGate.DATA_DIR);
				File file = new File(dir, troop.getName().toLowerCase());
				// compress the stream and also encrypt with triple-DES
				ObjectOutputStream out = new ObjectOutputStream(new GZIPOutputStream(new EncryptedOutputStream(cipher, new FileOutputStream(file))));
				out.writeObject(troop);
				out.close();
			}catch (Exception e) {
				System.err.println("Error: Could not save troop: " + troop);
				e.printStackTrace();
			}//try
		}//for
	  
		// clear out the cache
		troops.clear();
	}//saveCache
	
	/** Tells the SavingCache to die gracefully */
	public synchronized void kill() {
		// set the boolean value to false
		running = false;
	}//kill
	
	/** Sets the wait period between cache saves */
	public void setWait(long millis) {
		wait = millis;
	}//setWait
	
	/** Returns the current wait period, in milliseconds */
	public long getWait() {
		return wait;
	}//getWait
	
	/** Wakes every few seconds to save the cache */
	public void run() {
		while (running) {
			// wait the grace period
			try { Thread.sleep(wait); }
			catch (InterruptedException e) { }
			
			// save any objects in the cache
			saveCache();
		}//while
	}//run

}//SavingCache
