/*
	modernca.js - Javascript support for Modern Cellular Automata

	Written by George Maydwell
	Copyright (c) 2001 - 2007 by Software Collidoscope Foundry.

	May be freely reproduced and modified.

Updates:
06/12/03 - makePatternRule now supports 'a' and 'd' counting codes. Add hexagonal support.
08/24/02 - Added makePatternRule function.
06/21/02 - Fixed bogus "private" declaration which caused Netscrape problems.
05/27/02 - Added support for synchronized experiments.
05/17/01 - Added stringRepeat function for making formations.
*/


/*								General Note

	A writerObject provides write(string) and writeln(string) methods.
	A document object is thus a writerObject. Writer objects are passed
	into Ca write methods, to give the Ca a place to write html strings. 
*/


// Helper function which writes an applet parameter to a writer object:
function Ca_writeParam(writerObject, paramNameString, paramValueString) {
	if (paramValueString) {
		writerObject.writeln(
			'<param name="' + paramNameString + '" value="' + paramValueString + '">')
	}
}


// Implement a Ca object's write method:
function Ca_write(writerObject, defaultCa) {

	// Don't worry about defaultCa == null:
	if (defaultCa == null) {
		defaultCa = this
	}

	var appletWidth = 256
	var appletHeight = 256

	var appletSize = this.appletSize ? this.appletSize : defaultCa.appletSize
	var zoom = this.zoom ? this.zoom : (defaultCa.zoom ? defaultCa.zoom : 2)
	var universeSize = this.universeSize ? this.universeSize
										 : (defaultCa.universeSize ? defaultCa.universeSize : '128,128')
	var dimensions
	if (appletSize == null) {
		dimensions = universeSize.split(',')
		if (dimensions.length > 1) {
			appletWidth = zoom * dimensions[0]
			appletHeight = zoom * dimensions[1]
		}
	} else {
		dimensions = appletSize.split(',')
		if (dimensions.length > 1) {
			appletWidth = dimensions[0]
			appletHeight = dimensions[1]
		}
	}

	if (this.caption) {
		// Use a table so that things line up correctly
		writerObject.writeln('<TABLE><TR ALIGN="CENTER"><TD ALIGN="CENTER">')
	}

	if (this.hexagonal) {
		writerObject.writeln(
			'<APPLET ' +
				(this.name ? this.name : ' ') +
				' CODEBASE = "." ARCHIVE = "modernca.jar" CODE = "modernca.HexCa.class" ' +
				'WIDTH = "' + appletWidth + '" HEIGHT = "' + appletHeight + '" ALIGN = "middle">')
	}
	else {
		writerObject.writeln(
			'<APPLET ' +
				(this.name ? this.name : ' ') +
				' CODEBASE = "." ARCHIVE = "modernca.jar" CODE = "modernca.Ca.class" ' +
				'WIDTH = "' + appletWidth + '" HEIGHT = "' + appletHeight + '" ALIGN = "middle">')
	}
		
	Ca_writeParam(writerObject, "Maximum Rate", this.maximumRate ? this.maximumRate : defaultCa.maximumRate)
	Ca_writeParam(writerObject, "Reset Bounds", this.resetBounds ? this.resetBounds : defaultCa.resetBounds)
	Ca_writeParam(writerObject, "Reset Formation", this.resetFormation ? this.resetFormation : defaultCa.resetFormation)
	Ca_writeParam(writerObject, "Reset Generation", this.resetGeneration ? this.resetGeneration : defaultCa.resetGeneration)
	Ca_writeParam(writerObject, "Reset Interval", this.resetInterval ? this.resetInterval : defaultCa.resetInterval)
	Ca_writeParam(writerObject, "Rule", this.rule ? this.rule : defaultCa.rule)
	Ca_writeParam(writerObject, "Rule Mutation Rate", this.ruleMutationRate ? this.ruleMutationRate : defaultCa.ruleMutationRate)
	Ca_writeParam(writerObject, "Universe Size", this.universeSize ? this.universeSize : defaultCa.universeSize)
	Ca_writeParam(writerObject, "Zoom", zoom != 2 ? zoom : null)

	writerObject.writeln('</APPLET>')

	if (this.caption) {
		writerObject.writeln(
			'</TD></TR><TR ALIGN="CENTER"><TD ALIGN="CENTER">' + this.caption +
			'</TD></TR></TABLE>')
	}
}

// function Ca_synchronize(caArray, firstSlave, lastSlave)
//						  (caArray, masterIndex)


// function mcaSynchronize(caArray, masterIndex)
// function mcaMakeMutationExperiment(caArray, controlIndex, mutationRate)
// function mcaWriteTable(writerObject, caArray, columns)

function Ca_synchronize(slave1, slave2, slave3, etc) {
	for (i = 0; i < arguments.length; i++) {
		var slaveName = "slave" + i
		arguments[i].name = slaveName
		arguments[i].slave = true
		if (this.ca) {
			this.ca += " "
		}
		this.ca += slaveName
	}
}

function Ca_mutationExperiment(targetObject) {
}

/*
	A Ca object provides the means to create an applet with an
	associated caption
*/
function Ca() {
// public:
	// ca.write(writerObject, defaultCA) generates HTML for the CA applet
	this.write = Ca_write
//	this.synchronize = Ca_synchronize
//	this.mutationExperiment = Ca_mutationExperiment

	this.appletSize = null
	this.maximumRate = null
	this.resetBounds = null
	this.resetFormation = null
	this.resetGeneration = null
	this.resetInterval = null
	this.rule = null
	this.ruleMutationRate = null
	this.universeSize = null
	this.caption = null
	this.zoom = null
	this.hexagonal = false
// private:
	this.name = null
	this.slave = null
	this.ca = null
}





function stringRepeat(str, count) {
	var result = ''
	for (var i = 0; i < count; i++) {
		result += str
	}
	return result
}

function stringReverse(str)
{
	var result = ''
	for (var i = str.length - 1; i >= 0; i--) {
		result += str.charAt(i)
	}
	return result
}

function vonNeumannRulePhrase(countCode, r0, r1, r2, r3, r4)
{
//	return 'n1'
	return countCode + stringRepeat(r0, 5) + stringRepeat(r1, 5) + stringRepeat(r2, 5) +
					   stringRepeat(r3, 5) + stringRepeat(r4, 5)
}

function vonNeumannCornerRulePhrase(countCode, r0, r1, r2, r3, r4)
{
	return countCode + stringRepeat('' + r0 + r1 + r2 + r3 + r4, 5)
}

function mooreRulePhrase(countCode, resultCodeString)
{
	var temp = resultCodeString + '000000000'
	var resultStates = new Array(9)
	for (var i = 0; i < 9; i++) {
		resultStates[i] = resultCodeString.charAt(i)
	}
	var result = countCode
	for (var side = 0; side < 5; side++) {
		for (var corner = 0; corner < 5; corner++) {
			result += resultStates[corner + side]
		}
	}
	return result
}

function writeSelect(writer, name, onChangeFcnString, itemsArray, selection)
{
	writer.write('<SELECT NAME="' + name + '" OnChange="' + onChangeFcnString + '(this.selectedIndex)">')
	for (var i = 0; i < itemsArray.length; i++) {
		writer.write('<option' + (i == selection ? ' selected>' : '>') + itemsArray[i])
	}
	writer.write('</SELECT>')
}


function writeSelectRow(writer, label, onChangeFcnString, itemsArray, selection)
{
	writer.write('<TR><TD>' + label + ':</TD><TD>')
	writeSelect(writer, label, onChangeFcnString, itemsArray, selection)
	writer.writeln('</TD></TR>')
}


function makePatternRule(input) {
// Converts a canonical step specification into an equivalent pattern generation canonical step
	var seen = new Array(0,0,0,0,0,0,0,0)
	var s00 = -1
	var s0end = -1
	var i
	for (i = 1; i < input.length; i++) {
		var ch = input.charAt(i)
		if (ch >= '0' && ch <= '8') {
			seen[ch - '0'] = 1
			if (s00 < 0) {
				s00 = i
			}
		}
		if (ch == 'n' || ch == 'c' || ch == 'a' || ch == 'd') {
			if (s0end < 0) {
				s0end = i
			}
		}
	}
	var nStates = 0
	for (i = 0; i < 8; i++) {
		if (seen[i]) {
			nStates = i + 1
		}
	}
	var result = input.substr(0, s00 + 1)
	for (i = s00 + 1; i < s0end; i++) {
		if (input.charAt(i) == '0') {
			result += '' + nStates
		}
		else {
			result += input.charAt(i)
		}
	}
	result += input.substr(s0end)
	for (i = nStates; i < 9; i++) {
		result += input.substr(0, s00)
		result += i
		for (var j = s00 + 1; j < s0end; j++) {
			if (input.charAt(j) == '0') {
				result += '' + (i + 1) % 9
			}
			else {
				result += input.charAt(j)
			}
		}
	}
	return result
}


		









