// Copyright 2007 Louis Jean­Richard (www.ljr.ch). All Rights Reserved.

//	========================================================
//						R A N G E
//	========================================================

//	the range in use here is open bounded at the upper limit:	[a , b [

function 												Range(min, max)
{
	if (arguments.length == 1)
		with(arguments[0]) {	// initialise from an object
			this.min = min
			this.max = max
		}
	else {
		this.min = min
		this.max = max
	}
}

//	---------------------------------------
function 												cloneRange()
{
	return new Range(this)
}
Range.prototype.clone= cloneRange

//	---------------------------------------
function 												toStringRange()
{
	with(this)
		return 'Range{' + (min) + ' .. ' + (max) + '}'
}
Range.prototype.toString = toStringRange

//	---------------------------------------
function 												isInsideRange(x)
{
	return (x >= this.min) && (x < this.max)
}
Range.prototype.isInside = isInsideRange

//	---------------------------------------
function 												isOutsideRange(x)
{
	return (x < this.min) || (x >= this.max)
}
Range.prototype.isOutside = isOutsideRange


//	========================================================
//					D I S P L A C E M E N T
//	========================================================

//	the displacement is initialised using polar coordinates
//	the angle omega is expressed as fraction of 2 * PI

function 												Displacement(ds, omega)
{
	if (arguments.length == 1)
		with(arguments[0]) {	// initialise from an object
			this.ds = ds
			this.omega = omega
		}
	else
	{
		this.ds = ds	// pixel
		this.omega = omega * Math.PI * 2	// radiant
	}

	this.reset()
}

//	---------------------------------------
function 												resetDisplacement()
{
	with (Math)
	{
		this.dx = round(this.ds * cos(this.omega))
		this.dy = round(this.ds * sin(this.omega))
	}
}
Displacement.prototype.reset = resetDisplacement

//	---------------------------------------
function 												toStringDisplacement()
{
	with(this)
		return 'Displacement{' + (dx) + ', ' + (dy) + '}'
}
Displacement.prototype.toString = toStringDisplacement

//	---------------------------------------
function 												cloneDisplacement()
{
	return new Displacement(this)
}
Displacement.prototype.clone = cloneDisplacement

//	---------------------------------------
function 										betweenDisplacement(aPointFrom, aPointTo)
{
	this.dx = aPointTo.x - aPointFrom.x
	this.dy = aPointTo.y - aPointFrom.y
/*
	this.ds = Math.sqrt(this.dx * this.dx + this.dy * this dy)
	this.omega = Math.atan2(this.dy, this.dx)
*/
}
Displacement.prototype.between = betweenDisplacement

//	---------------------------------------
function 												horizontalWallBounceDisplacement()
{
	this.dy *= -1
}
Displacement.prototype.horizontalWallBounce = horizontalWallBounceDisplacement

//	---------------------------------------
function 												moveDisplacement(aPoint)
{
	return new Point
		( aPoint.x + this.dx
		, aPoint.y + this.dy
		)
}
Displacement.prototype.move = moveDisplacement

//	---------------------------------------
function 												randomDisplacement(absValue)
{
	with (Math) this.omega = random() * PI * 2
	this.reset()
}
Displacement.prototype.random = randomDisplacement

//	---------------------------------------
function 												verticalWallBounceDisplacement()
{
	this.dx *= -1
}
Displacement.prototype.verticalWallBounce = verticalWallBounceDisplacement


//	========================================================
//						P O I N T
//	========================================================

function 												Point(x, y)
{
	if (arguments.length == 1)
		with(arguments[0]) {	// initialise from an object
			this.x = x
			this.y = y
		}
	else {
		this.x = x
		this.y = y
	}
}

//	---------------------------------------
function 												clonePoint()
{
	return new Point(this)
}
Point.prototype.clone= clonePoint

//	---------------------------------------
function 												toStringPoint()
{
	with(this)
		return 'Point{' + (x) + ', ' + (y) + '}'
}
Point.prototype.toString = toStringPoint


//	========================================================
//	                    R E C T A N G L E
//	========================================================

//	Rectangular zone with bounds  [ x1, x2 [  and [ y1, y2 [

function 												Rectangle(aPoint1, aPoint2)
{
	switch (arguments.length)
	{
	case 1:
		with(arguments[0]) {	// initialise from an object of this class
			this.x1 = x1
			this.y1 = y1
			this.x2 = x2
			this.y2 = y2
		}
		break
	case 2:		// two points
		if (aPoint1.x < aPoint2.x) {
			this.x1 = aPoint1.x
			this.x2 = aPoint2.x
		}
		else {
			this.x1 = aPoint2.x
			this.x2 = aPoint1.x
		}
		if (aPoint1.y < aPoint2.y) {
			this.y1 = aPoint1.y
			this.y2 = aPoint2.y
		}
		else {
			this.y1 = aPoint2.y
			this.y2 = aPoint1.y
		}
		break
	case 3:			// aPoint, width, Height
		this.x1 = aPoint1.x
		this.x2 = aPoint1.x + arguments[1]
		this.y1 = aPoint1.y
		this.y2 = aPoint1.y + arguments[2]
		break
	default:			// x1, y1, x2, y2
		if (arguments[0] < arguments[2]) {
			this.x1 = arguments[0]
			this.x2 = arguments[2]
		}
		else {
			this.x1 = arguments[2]
			this.x2 = arguments[0]
		}
		if (arguments[1] < arguments[3]) {
			this.y1 = arguments[1]
			this.y2 = arguments[3]
		}
		else {
			this.y1 = arguments[3]
			this.y2 = arguments[1]
		}
	}
}

//	---------------------------------------
function 									toStringRectangle()
{
	with(this)
		return ('Rectangle{' + (x1)
			+ ', ' + (y1)
			+ ', ' + (x2)
			+ ', ' + (y2)
			+ '}')
}
Rectangle.prototype.toString = toStringRectangle

//	---------------------------------------
function 									constrainedPointRectangle(aPoint)
{
	var cx = aPoint.x
	var cy = aPoint.y

	if (cx < this.x1)   cx = this.x1
	if (cx >= this.x2)  cx = this.x2
	if (cy < this.y1)   cy = this.y1
	if (cy >= this.y2)  cy = this.y2

	return new Point(cx, cy)
}
Rectangle.prototype.constrainedPoint = constrainedPointRectangle

//	---------------------------------------
function 									isInsideRectangle(aPoint)
{
	return ((aPoint.x >= this.x1) && (aPoint.x < this.x2)
		&& (aPoint.y >= this.y1) && (aPoint.y < this.y2))
}
Rectangle.prototype.isInside = isInsideRectangle

//	---------------------------------------
function 									isOutsideRectangle(aPoint)
{
	return ((aPoint.x < this.x1) || (aPoint.x >= this.x2)
		|| (aPoint.y < this.y1) || (aPoint.y >= this.y2))
}
Rectangle.prototype.isOutside = isOutsideRectangle

//	---------------------------------------
function 									isOutsideHorizontalRectangle(aPoint)
{
	return ((aPoint.x < this.x1) || (aPoint.x >= this.x2))
}
Rectangle.prototype.isOutsideHorizontal = isOutsideHorizontalRectangle

//	---------------------------------------
function 									isOutsideVerticalRectangle(aPoint)
{
	return ((aPoint.y < this.y1) || (aPoint.y >= this.y2))
}
Rectangle.prototype.isOutsideVertical = isOutsideVerticalRectangle


//	============================================================
//	                           B O X
//	============================================================
var box_count = 0

function 					Box(width, height, colour, displacement, position)
{
	this.id = 'box' + box_count
	this.index = box_count
	box_count++
	this.width = width
	this.height = height
	this.startColour = colour
	this.colour = colour

	this.displacement = displacement

	if (arguments.length == 5)
		this.startPosition = position	// initialise from a Point
	else
		if (arguments.length > 5)
			this.startPosition = new Point(arguments[4], arguments[5])
		else
			this.startPosition = new Point(0, 0)

	this.position = new Point(0, 0)
	this.rectZone = new Rectangle(this.startPosition, 1, 1)

	this.displayed = false

	this.reset()
}

//	-----------------------------------
function                                    resetBox()
{
	this.DIVstyle = undefined
	this.setPosition(this.startPosition.clone())
	this.displacement.reset()
	if (this.displayed)
		this.DIVstyle.backgroundColor = this.colour
}
Box.prototype.reset = resetBox

//	-----------------------------------
function                                    setZoneBox(x1, y1, x2, y2)
{
//	The upper left corner is allowed to move within the rectangular zone defined hereafter
	this.rectZone = new Rectangle(x1, y1, x2 - this.width, y2 - this.height)
}
Box.prototype.setZone = setZoneBox

//	-----------------------------------
function                                    htmlBox(parentName, x,y)
{
	if (y != undefined) {
		this.position.x = x
		this.position.y = y
	}
	var output = '<DIV id="' + this.id
	output += '" onmousedown="' + parentName +'.mouseDown(event, ' + this.index
	output += ')" style="position: absolute; left: ' + this.position.x
	output += 'px; top: ' + this.position.y
	output += 'px; width: ' + this.width
	output += 'px; height: ' + this.height
	if (this.index == 0)
		output += 'px; background-color: ' + this.colour	// the prey box
	else
		output += 'px; background-color: transparent'
	output += '"></DIV>'

	this.displayed = true

	return output
}
Box.prototype.html = htmlBox

//	-----------------------------------

function                                    horizontalRangeBox()
{
	return new Range(this.position.x, this.position.x + this.width)
}
Box.prototype.horizontalRange = horizontalRangeBox

//	-----------------------------------

function                                    verticalRangeBox()
{
	return new Range(this.position.y, this.position.y + this.height)
}
Box.prototype.verticalRange = verticalRangeBox

//	-----------------------------------
function                                    touchesBox(aBox)
{
	return ((aBox.horizontalRange().isInside(this.position.x)
			|| this.horizontalRange().isInside(aBox.position.x))
		&&
			(aBox.verticalRange().isInside(this.position.y)
			|| this.verticalRange().isInside(aBox.position.y))
		)
}
Box.prototype.touches = touchesBox

//	-----------------------------------
function                                    styleBox()
{
	if (this.DIVstyle == undefined)
		this.DIVstyle = document.getElementById(this.id).style

	return this.DIVstyle
}
Box.prototype.style = styleBox

//	-----------------------------------
function                                    hideBox()
{
	this.colour = 'transparent'
	if (this.displayed)
		this.style().backgroundColor = this.colour
}
Box.prototype.hide = hideBox

//	-----------------------------------
function                                    showBox()
{
	this.colour = this.startColour
	if (this.displayed)
		this.style().backgroundColor = this.colour
}
Box.prototype.show = showBox

//	-----------------------------------
function                                    setPositionBox(aPoint)
{
	if (arguments.length < 2)
		this.position = aPoint
	else
	{
		this.position.x = arguments[0]
		this.position.y = arguments[1]
	}

	if (this.displayed)
	{
		var st = this.style()

		st.left = this.position.x + 'px'
		st.top = this.position.y + 'px'
	}
}
Box.prototype.setPosition = setPositionBox

//	-----------------------------------
function                                    moveBox()
{
	var newPos = this.displacement.move(this.position)

	if (this.rectZone.isOutside(newPos))
	{
		if (this.rectZone.isOutsideHorizontal(newPos))
		{
			if (this.displacement.dx > 0)
				newPos.x = 2 * this.rectZone.x2 - newPos.x
			else
				newPos.x = 2 * this.rectZone.x1 - newPos.x

			this.displacement.verticalWallBounce()
		}
		if (this.rectZone.isOutsideVertical(newPos))
		{
			if (this.displacement.dy > 0)
				newPos.y = 2 * this.rectZone.y2 - newPos.y
			else
				newPos.y = 2 * this.rectZone.y1 - newPos.y

			this.displacement.horizontalWallBounce()
		}
	}
	this.setPosition(newPos)
}
Box.prototype.move = moveBox


//	============================================================
//	                    M O U S E   C U R S O R
//	============================================================

function 									MouseCursor()
{
	this.position = new Point(0, 0)
	this.previousPosition = new Point(0, 0)
	this.displacement = new Displacement(0,0)

	this.reset()
}

//	-----------------------------------
function                                    resetMouseCursor()
{
	this.isMouseDown = false
	this.isShiftPressed = false
}
MouseCursor.prototype.reset = resetMouseCursor

//	-----------------------------------
function                                    updateMouseCursor1()
{
	this.previousPosition.x = this.position.x
	this.previousPosition.y = this.position.y

	this.position.x = window.event.x	// IE
	this.position.y = window.event.y

	this.displacement.between(this.previousPosition, this.position)

	this.isShiftPressed = window.event.shiftKey
}
function                                    updateMouseCursor2(event)
{
	this.previousPosition.x = this.position.x
	this.previousPosition.y = this.position.y

	this.position.x = event.pageX	// others
	this.position.y = event.pageY

	this.displacement.between(this.previousPosition, this.position)

	this.isShiftPressed = event.shiftKey
}
if (browser.isIE)
	MouseCursor.prototype.update = updateMouseCursor1
else
	MouseCursor.prototype.update = updateMouseCursor2


//	============================================================
//	                        A N I M A T I O N
//	============================================================

function 			Animation(smallestWidth, initialSpeed, finalSpeed, timeOfFinalSpeed)
{
	this.maxDisplacement = smallestWidth

	if (initialSpeed == undefined)
		this.initialSpeed = 0	// pixel / second
	else
		this.initialSpeed = initialSpeed	// pixel / second

	if (finalSpeed == undefined)
		this.finalSpeed = 400	// pixel / second
	else
		this.finalSpeed = finalSpeed	// pixel / second

	if (timeOfFinalSpeed == undefined)
		this.timeOfFinalSpeed = 1000 * 30	// millisecond
	else
		this.timeOfFinalSpeed = 1000 * timeOfFinalSpeed	// millisecond

	this.acceleration = 1000 * (this.finalSpeed - this.initialSpeed) / this.timeOfFinalSpeed	// pixel / (second^2)

//	after the final speed is attained the acceleration drops to 0

	this.computationLoadFactor = 16

	this.cycleComputation = 2	// millisecond

	this.initParameters()

	this.reset()
}

//	-----------------------------------
function                                    resetAnimation()
{
	this.gameCycles = 0
	this.cycleTime = 0
	this.isCalibrating = false

	this.accTime2 = this.accTime * this.accTime
}
Animation.prototype.reset = resetAnimation

//	-----------------------------------
function                                    calibrateAnimation()
{
	if (!this.isCalibrating)
		return

	this.isCalibrating = false

	this.initParameters()
}
Animation.prototype.calibrate = calibrateAnimation

//	-----------------------------------
function                                    adjustLoadFactorAnimation()
{
	this.isCalibrating = true
	if (this.timeOfFinalSpeed > this.cycleTime)
	{
		this.computationLoadFactor *= this.timeOfFinalSpeed * 1.1 / (1 + this.cycleTime)
	}
}
Animation.prototype.adjustLoadFactor = adjustLoadFactorAnimation

//	-----------------------------------
function                                    adjustCycleComputationAnimation()
{
	this.isCalibrating = true
//	we compute the average cycle computation time
	if (this.gameCycles > 1)
	{
		var cycleComputation = this.cycleComputationSum / (this.gameCycles - 1)
		if (cycleComputation > this.cycleComputation)
			this.cycleComputation = cycleComputation
	}
}
Animation.prototype.adjustCycleComputation = adjustCycleComputationAnimation

//	-----------------------------------
function                                    startCalibrationAnimation()
{
	if (this.isCalibrating)
		return

	this.reset()
	this.isCalibrating = true
}
Animation.prototype.startCalibration = startCalibrationAnimation

//	-----------------------------------
function                                    initParametersAnimation()
{
	this.cycleDuration = this.computationLoadFactor * this.cycleComputation
	if (this.cycleDuration < 10)
		this.cycleDuration = 10

	this.minDisplacement = 1 + Math.floor(this.cycleComputation * this.finalSpeed / 1000)	// pixel

	this.ds = 1 + Math.floor(this.cycleDuration * this.finalSpeed / 1000)	// pixel
	if (this.ds < this.minDisplacement)
		this.ds = this.minDisplacement
	if (this.ds > this.maxDisplacement)
		this.ds = this.maxDisplacement

	this.dt = 1000 * this.ds / this.finalSpeed	// millisecond

	this.accTime = this.initialSpeed / this.acceleration
	this.cycleCoeff = 2 * this.ds / this.acceleration
}
Animation.prototype.initParameters = initParametersAnimation

//	-----------------------------------
function                                    timeAnimation()
{
	return systime() - this.startTime
}
Animation.prototype.time = timeAnimation

//	-----------------------------------
function                                    velocityAnimation()
{
	if (this.cycleTime >= this.timeOfFinalSpeed)
		return this.finalSpeed	// pixel / second
	else
		return this.initialSpeed + this.acceleration * this.cycleTime / 1000	// pixel / second
}
Animation.prototype.velocity = velocityAnimation

//	-----------------------------------
function                                    beginComputationAnimation()
{
	if (this.gameCycles == 0)
	{
		this.startTime = systime()	// millisecond
		this.cycleTime = 0
		this.cycleComputationSum = 0
	}
	else
	{
		this.cycleComputationSum = -(this.cycleTime + this.interval)
		this.cycleTime = this.time()
		this.cycleComputationSum += this.cycleTime
	}
	this.gameCycles++
}
Animation.prototype.beginComputation = beginComputationAnimation

//	-----------------------------------
function                                    endComputationAnimation()
{
	if (this.cycleTime < this.timeOfFinalSpeed)
	{
		this.accTime2 += this.cycleCoeff
		this.nextCycleComputationTime = 1000 * (Math.sqrt(this.accTime2) - this.accTime)
		this.interval = this.nextCycleComputationTime - this.time()
	}
	else
		if (this.isCalibrating)
			this.interval = 0
		else
		{
			this.nextCycleComputationTime += this.dt
			this.interval = this.nextCycleComputationTime - this.time()
		}
}
Animation.prototype.endComputation = endComputationAnimation


//	============================================================
//	                     H U N T I N G   Y A R D
//	============================================================

//	The "hunting yard" is represented by a rectangular space within which the boxes move.

function 								HuntingYard(name, width, height, enclosureThickness, aScore)
{
	this.name = name
	this.width = width
	this.height = height
	this.enclosureThickness = enclosureThickness

	this.animation = new Animation(Math.min(width, height),20,400,15)

	var boxSide = 40	// pixel
	this.thePrey = new Box(boxSide, boxSide, '#B90000'
		, new Displacement(0,0), new Point((width - boxSide) / 2, (height - boxSide) / 2))

	var ds = this.animation.ds
	this.mobileBox = new Array
		( this.thePrey
		, new Box( 60, 50, '#000099', new Displacement(ds,  0.2 ), new Point(220,  10))
		, new Box(100, 20, '#000099', new Displacement(ds,  0.31), new Point(245, 280))
		, new Box( 30, 60, '#000099', new Displacement(ds, -0.12), new Point( 20, 270))
		, new Box( 60, 60, '#000099', new Displacement(ds, -0.4 ), new Point( 20,  20))
		, new Box(  8,  8, '#000099', new Displacement(ds,  0.25), new Point(320, 170))
		)
	for (var i=0; i < this.mobileBox.length; i++)
		this.mobileBox[i].setZone(0, 0, width, height)

	this.hunters = this.mobileBox.length

	this.doNotTouchWalls = false

	this.cursor = new MouseCursor()

	this.movedBox = undefined

	this.timed_event = null

//	the container starts and stops the chronometer displayed in the score window
	this.score = aScore
//	the score needs to read status information from the container
	this.score.board = this

	this.displayed = false

	this.reset()
}

//	-----------------------------------
function                                    resetHuntingYard(mode)
{
	if (mode != undefined)
		this.hunters = mode + 1

	this.isFreezed = false

	this.animation.reset()

	this.cursor.reset()

	for (var i=0; i < this.mobileBox.length; i++)
		with (this.mobileBox[i])
		{
			reset()
			if (i < this.hunters)
				show()
			else
				hide()
		}

	if (this.displayed)
		this.score.reset('reset')
}
HuntingYard.prototype.reset = resetHuntingYard

//	-----------------------------------
function                                    animateHuntingYard()
{
//	this procedure is called at predetermined times via "setTimeout"
	this.animation.beginComputation()

	for (var i=1; i < this.mobileBox.length; i++)
		with (this.mobileBox[i])
		{
			move()

			if ((i < this.hunters) && touches(this.thePrey))
			{
				this.stopAnimation('box(' + i + ')')
				return
			}
		}

	this.animation.endComputation()

	if (this.animation.interval > 0)
		this.timed_event = setTimeout(this.name +".animate()", this.animation.interval)
	else
	{
		this.animation.adjustLoadFactor()
		this.animation.adjustCycleComputation()
		this.stopAnimation('speed limit')
	}
}
HuntingYard.prototype.animate = animateHuntingYard

//	-----------------------------------
function                                    calibrateAnimationHuntingYard()
{
	if (this.isFreezed)
		this.reset()
	else
		if (this.animation.isCalibrating)
			return

	this.cursor.isMouseDown = false
	this.thePrey.setPosition(this.width, this.height)

	this.animation.startCalibration()

	this.score.startChrono('calibration')

	this.animate()
}
HuntingYard.prototype.calibrateAnimation = calibrateAnimationHuntingYard

//	-----------------------------------
function                                    followCursorHuntingYard(event)
{
	this.cursor.update(event)

	if (this.isFreezed || (this.movedBox == undefined))
		return

	if (this.animation.gameCycles > 0)
		with (this.thePrey)
		{
			var newPos = this.cursor.displacement.move(position)

			if (!rectZone.isOutside(newPos))
				setPosition(newPos)
			else
				if (this.doNotTouchWalls)
					this.stopAnimation('wall')
				else
					setPosition(rectZone.constrainedPoint(newPos))
		}
	else
		if (this.cursor.isShiftPressed)
			with (this.movedBox)
			{
				var newPos = this.cursor.displacement.move(position)

				startPosition = rectZone.constrainedPoint(newPos)
				setPosition(startPosition)
			}
}
HuntingYard.prototype.followCursor = followCursorHuntingYard

//	-----------------------------------
function                                    htmlHuntingYard()
{
	var td_corner = '<TD width="' + this.enclosureThickness + '" height="' + this.enclosureThickness + '" bgColor=#000000></TD>'
	var td_horizontal = td_corner + '<TD bgColor=#000000></TD>' + td_corner
	var td_vertical = '<TD bgColor=#000000></TD>'
	var output = '<TABLE cellSpacing=0 cellPadding=0 border=0><TBODY><TR>'
	output += td_horizontal
	output += '</TR><TR>'
	output += td_vertical
	output += '<TD style="vertical-align: top" onmousemove="' + this.name + '.followCursor(event)" onmouseup="' + this.name + '.mouseUp(event)">'

	output += '<DIV id="container_space" style="position: relative; left: 0px; top: 0px; width: ' + this.width + 'px; height: ' + this.height + 'px; background-color: transparent">'

	for (var i=0; i < this.mobileBox.length; i++)
		output += this.mobileBox[i].html(this.name)

	output += '</TD>'
	output += td_vertical
	output += '</TR><TR>'
	output += td_horizontal
	output += '</TR></TBODY></TABLE>'

	this.displayed = true

	return output
}
HuntingYard.prototype.html = htmlHuntingYard

//	-----------------------------------
function                                    mouseDownHuntingYard(event, index)
{
	if (this.animation.isCalibrating)
		return

	this.cursor.isMouseDown = true

	if (this.movedBox != undefined)
		return

	this.movedBox = this.mobileBox[index]

	this.cursor.update(event)

	if ((index == 0) && (!this.cursor.isShiftPressed))
	{
		this.reset()
		this.score.startChrono('mouse down')
		this.animate()
	}
}
HuntingYard.prototype.mouseDown = mouseDownHuntingYard

//	-----------------------------------
function                                    mouseUpHuntingYard(event, index)
{
//	this.cursor.isMouseDown = false

	this.cursor.reset()

	this.movedBox = undefined

	if (this.animation.gameCycles > 0)
		this.stopAnimation('mouse up')

	this.reset()
}
HuntingYard.prototype.mouseUp = mouseUpHuntingYard

//	-----------------------------------
function                                    randomDisplacementHuntingYard()
{
//	set random displacements for all moving boxes
	for (var i=1; i < this.mobileBox.length; i++)
		this.mobileBox[i].displacement.random()
}
HuntingYard.prototype.randomDisplacement = randomDisplacementHuntingYard

//	-----------------------------------
function                                    stopAnimationHuntingYard()
{
	if (this.isFreezed)
		return

	this.isFreezed = true
	clearTimeout(this.timed_event)
	this.score.stopChrono(arguments[0])

	this.animation.calibrate()
	for (var i=1; i < this.mobileBox.length; i++)
		this.mobileBox[i].displacement.ds = this.animation.ds

//	this.animation.inspect()
}
HuntingYard.prototype.stopAnimation = stopAnimationHuntingYard


//	============================================================
//	                        S C O R E
//	============================================================

function 									Score(name)
{
	this.name = name
	this.chrono = new Chrono()
	this.board = undefined
	this.showAll = false

	this.displayed = false
}

//	-----------------------------------
function                                    resetScore(statusMsg)
{
	var sd = document.SCORE
	if (statusMsg != undefined)
		sd.status.value = statusMsg
	sd.best_time.value = this.control.playMode.selected.best_time_display
	sd.time.value = '0'
}
Score.prototype.reset = resetScore

//	-----------------------------------
function                                    htmlScore()
{
	output = '<form name="SCORE"><table>'
	output += '<tr><td>' + text5d
	output += '</td><td>' + text5e
	output += "</td></tr><tr><td>" + htmlDisplay('best_time', '')
	output += '</td><td>' + htmlDisplay('time', '')
	output += "</td></tr>"
	output += '<tr><td>' + text5f
	output += '</td><td>' + text5g
	output += '</td></tr><tr><td>' + htmlDisplay('status', '')
	output += '</td><td>' + htmlDisplay('speed', '')
	output += '</td></tr>'
	if (this.showAll)
	{
		output += '<tr><td>' + text5a
		output += '</td><td>' + text5b
		output += '</td></tr><tr><td>' + htmlDisplay('ds', '')
		output += '</td><td>' + htmlDisplay('dt', '')
		output += '</td></tr>'
		output += '<tr><td>' + text5c
		output += '</td><td>' + text5h
		output += '</td></tr><tr><td>' + htmlDisplay('minds', '')
		output += '</td><td>' + htmlDisplay('compdt', '')
		output += '</td></tr>'
	}
	output += "</table></form>"
	this.displayed = true

	return output
}
Score.prototype.html = htmlScore

//	-----------------------------------
function                                    startChronoScore(statusMsg)
 {
	var sd = document.SCORE
	sd.best_time.value = this.control.playMode.selected.best_time_display
	if (statusMsg == undefined)
		sd.status.value = 'started'
	else
		sd.status.value = statusMsg

	this.chrono.start(this.name +".updateTime()")

	this.updateTime()
}
Score.prototype.startChrono = startChronoScore

//	-----------------------------------
function                                    stopChronoScore(statusMsg)
{
	this.chrono.stop()
	this.updateTime()

	var playMode = this.control.playMode.selected
	var sd = document.SCORE

	if ((this.chrono.reading > playMode.best_time)
		&& !this.control.isCalibrating)
	{
		playMode.best_time = this.chrono.reading
		playMode.best_time_display = sd.time.value
	}

	if (statusMsg == undefined)
		sd.status.value = 'stopped'
	else
		sd.status.value = statusMsg
}
Score.prototype.stopChrono = stopChronoScore

//	-----------------------------------
function                                    toggleShowAllScore(event)
{
	if (event.ctrlKey)
	{
		this.showAll = (!this.showAll)
		document.SCORE.innerHTML = this.html()
		event.preventDefault()
		this.updateTime()
	}
}
Score.prototype.toggleShowAll = toggleShowAllScore

//	-----------------------------------
function                                    updateTimeScore()
{
	var sd = document.SCORE
	sd.time.value = this.chrono.elapsedMinutes()
	var ap = this.board.animation
	sd.speed.value = Math.round(ap.velocity())
	if (this.showAll)
	{
		sd.dt.value = Math.round(ap.interval)
		sd.compdt.value = Math.round(ap.cycleComputation)

		sd.minds.value = this.board.animation.minDisplacement
		sd.ds.value = this.board.animation.ds
	}
}
Score.prototype.updateTime = updateTimeScore


//	============================================================
//	                       P L A Y   M O D E
//	============================================================

function                                     PlayMode(mode, title)
{
	this.mode = mode
	this.optionText = mode.toString() + ' ' + title
	this.best_time = 0
	this.best_time_display = '0'
}


//	============================================================
//	                         C O N T R O L
//	============================================================

function                                     Control(name, aBoard, aScore)
{
	this.name = name
	this.board = aBoard
	this.score = aScore
	this.showAll = false

	this.score.control = this

	this.mode = aBoard.hunters - 1
	this.isCalibrating = false

	this.playMode = new Selector(name + '.playModeChanged', this.mode - 1
		, new PlayMode(1, text6a)
		, new PlayMode(2, text6b)
		, new PlayMode(3, text6b)
		, new PlayMode(4, text6b)
		, new PlayMode(5, text6b)
		)

	this.reset()
}

//	-----------------------------------
function                                    resetControl()
{
	this.score.playMode = this.playMode.selected
	this.isCalibrating = false
}
Control.prototype.reset = resetControl

//	-----------------------------------
function                                    calibrateAnimationControl()
{
	this.isCalibrating = true
	with (this.playMode) check(option.length - 1)
	this.playModeChanged()

	this.board.calibrateAnimation()
}
Control.prototype.calibrateAnimation = calibrateAnimationControl

//	-----------------------------------
function                                    htmlButtonControl(label, operation)
{
	return '<input class="button" type="button" value='
		+ sQuoted(label)
		+ " onclick='javascript:" + this.name + '.' + operation + "()' />"
}
Control.prototype.htmlButton = htmlButtonControl

//	-----------------------------------
function                                    htmlControl(formName)
{
	var newLine = "<br />\n"
	var output = '<form name=' + quoted(formName) + '>'
	output += this.htmlButton(text4,'randomSpeed')
//	output += this.sudoku.htmlList(formName)
	output += newLine
	output += "<fieldset>"
	output += "<legend>" + text7a + "</legend>"
	output += this.playMode.htmlRadio(formName)
	output += "</fieldset>"
	if (this.showAll)
	{
		output += newLine
		output += this.htmlButton(text3, 'calibrateAnimation')
		if (window.debug)
		{
			output += newLine
			output += this.htmlButton(text3a, 'showAnimation')
		}
//		output += newLine
//		output += htmlInput('loadFactor', text7b, 16)
	}
	output += "</form>"

	this.displayed = true

	return output
}
Control.prototype.html = htmlControl

//	-----------------------------------
function                                    playModeChangedControl()
{
	var old_mode = this.mode
	this.playMode.read()
	this.mode = this.playMode.selected.mode

	if (this.mode != old_mode)
	{
		this.score.playMode = this.playMode.selected
		this.board.reset(this.mode)
	}
}
Control.prototype.playModeChanged = playModeChangedControl

//	-----------------------------------
function                                    randomSpeedControl()
{
	this.board.randomDisplacement()
}
Control.prototype.randomSpeed = randomSpeedControl

//	-----------------------------------
function                                    showAnimationControl()
{
	this.board.inspect()
}
Control.prototype.showAnimation = showAnimationControl

//	-----------------------------------
function                                    toggleShowAllControl(event)
{
	if (event.ctrlKey)
	{
		this.showAll = (!this.showAll)
		document.CONTROL.innerHTML = game.html('CONTROL')
		event.preventDefault()
	}
}
Control.prototype.toggleShowAll = toggleShowAllControl


//	============================================================
//	                   I N T E R F A C E
//	============================================================

function                                    displayGame(language)
{
	initLanguage(language)

	score = new Score('score')
	board = new HuntingYard('board', 450, 350, 20, score)
	game = new Control('game', board, score)

	var output = '<table class="game"><tr><td class="score" onmousedown="game.score.toggleShowAll(event)">'
	output += score.html()
	output += '</td><td class="board" rowspan="2">'
	output += board.html()
	output += '</td></tr><tr><td class="control" onmousedown="game.toggleShowAll(event)">'
	output += game.html('CONTROL')
	output += '</td></tr></table>'

	document.open()
	document.write(output)
	document.close()

	game.playModeChanged()

	board.reset()
}

//	-----------------------------------------------

//	============================================================
var langCode = undefined
var text3 = 'Calibrate animation'
var text3a = 'Show animation'
var text4 = 'Set random speeds'
var text5a = 'ds (px)'
var text5b = 'dt (ms)'
var text5c = 'min ds (px)'
var text5h = 'comp dt (ms)'
var text5d = 'Best time'
var text5e = 'Time'
var text5f = 'Game status'
var text5g = 'speed (px/s)'
var text6a = 'hunter'
var text6b = 'hunters'
var text7a = 'Play mode'

//	-----------------------------------
function initLanguage(language)
{
	if (langCode != undefined)
		return

	langCode = language

	switch (langCode)
	{
	case 'de':
		text4 = 'Setze zuf&auml;llige Geschwindigkeiten'
		text5d = 'Beste Zeit'
		text5e = 'Zeit'
		text5f = 'Spielstatus'
		text5g = 'Geschwindigkeit (px/s)'
		text6a = 'J&auml;ger'
		text6b = 'J&auml;ger'
		text7a = 'Spielart'
		break
	case 'es':
		text4 = 'Poner velocidades al azar'
		text5d = 'Mejor tiempo'
		text5e = 'Tiempo'
		text5f = 'Estado del juego'
		text5g = 'Velocidad (px/s)'
		text6a = 'Cazador'
		text6b = 'Cazadores'
		text7a = 'Modalidad de juego'
		break
	case 'fr':
		text4 = 'Attribue vitesses au hasard'
		text5d = 'Meilleur temps'
		text5e = 'Temps'
		text5f = '&Eacute;tat du jeux'
		text5g = 'Vitesse (px/s)'
		text6a = 'Chasseur'
		text6b = 'Chasseurs'
		text7a = 'Mode de jeux'
		break
	case 'it':
		text4 = 'Velocit&agrave; casuali'
		text5d = 'Miglior tempo'
		text5e = 'Tempo'
		text5f = 'Stato del gioco'
		text5g = 'Velocit&agrave; (px/s)'
		text6a = 'Cacciatore'
		text6b = 'Cacciatori'
		text7a = 'Modalit&agrave; di gioco'
		break
	}
}
//	-----------------------------------

//	fine
