package relational;

import java.io.*;
import java.rmi.RemoteException;

/**
 * ClumpRelational
 *
 * Objects in this class have primary keys that encode their
 * database membership, saving four bytes per class member.
 */
public abstract class ClumpRelational
	extends Relational {

	static final int serialBits = 16;
	static final int serialMax = (1<<serialBits) - 1;
	static Database dbmap[] = new Database[1];
	static int highestSerial[] = new int[1];

	// synchronized because we don't want to be reading the maps
	// while they're being grown. (Could be done with a separate
	// object that is replaced atomically, maybe)
	public ClumpRelational(Database db) {
		synchronized(this) {
			int i;
			for (i=0; i<dbmap.length && dbmap[i]!=null; i++) {
				// TODO: talk to database to get a range of
				// primary keys to allocate. Else two apps hammering
				// on same database will clash, duh.
				if (dbmap[i]==db && highestSerial[i]<serialMax) {
					setPrimaryKey(new Integer(
						(i<<serialBits)|(++highestSerial[i])
					));
					return;
				}
			}
			// db not found
			if (i>=dbmap.length) {
				// map is full -- grow it to make room for index [i]
				growMap(dbmap.length*2);
			}
			dbmap[i]=db;
			highestSerial[i]=0;
			setPrimaryKey(new Integer(
				(i<<serialBits)|0
			));
		}
	}

	protected static void growMap(int pastHere) {
		int newSize = pastHere+1;
		Database[] newDbMap = new Database[newSize];
		int[] newHighestSerial = new int[newSize];
		System.arraycopy(dbmap, 0, newDbMap, 0, dbmap.length);
		System.arraycopy(highestSerial, 0, newHighestSerial, 0, dbmap.length);
		dbmap = newDbMap;
		highestSerial = newHighestSerial;
	}

	public Database getDatabase() {
		return dbmap[((Integer)primaryKey).intValue()>>serialBits];
	}

	private void writeObject(java.io.ObjectOutputStream out)
		throws IOException {
		out.writeObject(getDatabase());
	}

	private void readObject(java.io.ObjectInputStream in)
		throws IOException, ClassNotFoundException {
		Database db = (Database) in.readObject();
		int index = ((Integer)primaryKey).intValue()>>serialBits;
		if (index>=dbmap.length) {
			growMap(index);
		}
		dbmap[index] = db;
		// highestSerial doesn't matter here; we're not creating objects
		// on this JVM. Hmm. TODO.
	}
}
