//the script for handling a drag and drop (with images)

// global variables
var sensitivity = 15
var offsetX = 0
var offsetY = 0
var selectedObj
var draggables = new Array()
var draggablesIndexList = new Array()
var dropAreasIndexList = new Array()
var dropAreas = new Array()
var dropWhere = new Array()
var lastFeedback

// object constructer for the concept of a "draggable"
// the x and y here is relative to the background image
// and represent the x and y of the target area.
function draggable(code, origX, origY, width, height) {
	this.code = code
	this.origX = origX
	this.origY = origY
	this.width = width
	this.height = height
	this.midX = (0.5*width)+origX
	this.midY = (0.5*height)+origY
	this.done = false
	assignEvents(this)
	draggablesIndexList[draggablesIndexList.length] = code
}

// object constructer for the concept of a target area
function dropArea(code, x, y, width, height) {
	this.code = code
	this.x = x
	this.y = y
	this.width = width
	this.height = height
	this.midX = (0.5*width)+x
	this.midY = (0.5*height)+y
	this.x2 = x + width
	this.y2 = y + height
	this.occupied = false
	dropAreasIndexList[dropAreasIndexList.length] = code
}

// assigns event handlers to each map layer
function assignEvents(layer) {
	var obj
	if (document.layers) {
		obj = document.layers[layer.code + "layer"]
		obj.captureEvents(Event.MOUSEDOWN | Event.MOUSEMOVE | Event.MOUSEUP)
	} else if (document.all) {
		obj = document.all(layer.code + "layer")
	} else if (document.getElementById) {
		obj = document.getElementById(layer.code + "layer")
	}
	if (obj) {
		obj.onmousedown = engage
		obj.onmousemove = dragIt
		obj.onmouseup = release
	}
}

// set a global reference to the object being dragged
// this also ensures that the object is an image - necessary to drag?
function setSelectedMap(evt) {
	var target = (evt.target) ? evt.target : evt.srcElement
	var code = (target.name && target.src) ? target.name.slice(0,-5) : ""
	if (code) {
		if (document.layers) {
			selectedObj = document.layers[code + "layer"]
		} else if (document.all) {
			selectedObj = document.all(code + "layer")
		} else if (document.getElementById) {
			selectedObj = document.getElementById(code + "layer")
		}
		setZIndex(selectedObj, 100)
		return
	}
	selectedObj = null
	return
}

// this function reacts to one of the draggable items being selected
// it sorts out a bunch of stuff to do with offsets, taking into account scrolling
function engage(evt) {
	evt = (evt) ? evt : event
	setSelectedMap(evt)
	if (lastFeedback) { 
		hide(lastFeedback)
	}

	if (selectedObj) {
		if (evt.pageX) {
			offsetX = evt.pageX - ((selectedObj.offsetLeft) ? 
							selectedObj.offsetLeft : selectedObj.left)
			offsetY = evt.pageY - ((selectedObj.offsetTop) ? 
							selectedObj.offsetTop : selectedObj.top)
		} else if (evt.offsetX || evt.offsetY) {
			offsetX = evt.offsetX - ((evt.offsetX < -2) ?
							0 : document.body.scrollLeft)
			offsetY = evt.offsetY - ((evt.offsetY < -2) ?
							0 : document.body.scrollTop)
		}
		return false
	}
}
		
// actually move the object when the mouse moves
function dragIt(evt) {
	evt = (evt) ? evt : event
	if (selectedObj) {
		if (evt.pageX) {
			shiftTo(selectedObj, (evt.pageX - offsetX), (evt.pageY - offsetY))
		} else if (evt.clientX || evt.clientY) {
			shiftTo(selectedObj, (evt.clientX - offsetX), (evt.clientY - offsetY))
		}
		evt.cancelBubble = true
		return false
	}
}

// what happens when the user "drops" the object?
// calls function to check if we are in a legitimate drop bay and is it the right one?
function release(evt) {
	evt = (evt) ? evt : event
	var target = (evt.target) ? evt.target : evt.srcElement
	var code = (target.name && target.src) ? target.name.slice(0,-5) : ""
	if (code && selectedObj) {
		if (onTarget(evt)) {
			draggables[code].done = true
			if (isDone()) {
				show("congrats")
			}
		} else {
			draggables[code].done = false
			hide("congrats")
		}
		setZIndex(selectedObj, 0)
	} 
	selectedObj = null
}

// is the draggable object over a legitimate target?
function onTarget(evt) {
	evt = (evt) ? evt : event
	var target = (evt.target) ? evt.target : evt.srcElement
	var dragCode = (target.name && target.src) ? target.name.slice(0,-5) : ""
	var dropCode
	var dropped = false
	var thisFeedback
	var thisFeedbackLayer


	if (dragCode && selectedObj) {
		// need to loop through all possible targets for this draggable 
		// then establish whether it is legit or not - then provide feedback
		var objX, objY
		if (selectedObj.pageX) {
			objX = selectedObj.pageX
			objY = selectedObj.pageY
		} else if (selectedObj.style) {
			objX = parseInt(selectedObj.style.left)
			objY = parseInt(selectedObj.style.top)
		}
		var objMidX = (0.5*draggables[dragCode].width) + objX
		var objMidY = (0.5*draggables[dragCode].height) + objY
		for (i=0; i < dropAreasIndexList.length; i++) {
			dropCode = dropAreasIndexList[i]
			var x = dropAreas[dropCode].x
			var y = dropAreas[dropCode].y
			var midX = dropAreas[dropCode].midX
			var midY = dropAreas[dropCode].midY
			var x2 = dropAreas[dropCode].x2
			var y2 = dropAreas[dropCode].y2

			// middles of draggable and dropArea need to be within
			// sensitivity pixels of each other (both x & y)
			//if ((objMidX >= (midX - sensitivity) && objMidX <= (midX + sensitivity)) 
			//	&& (objMidY >= (midY - sensitivity) && objMidY <= (midY + sensitivity))) {
			
			// middle of draggable needs to be within the dropArea
			if ((objMidX >= (x - sensitivity) && objMidX <= (x2 + sensitivity)) 
				&& (objMidY >= (y - sensitivity) && objMidY <= (y2 + sensitivity))) {
			
				thisFeedback = dragCode + dropCode
				// need to convert thisFeedback into an object we can check exists
				if (thisFeedback) {
					if (document.layers) {
						thisFeedbackLayer = document.layers[thisFeedback]
					} else if (document.all) {
						thisFeedbackLayer = document.all(thisFeedback)
					} else if (document.getElementById) {
						thisFeedbackLayer = document.getElementById(thisFeedback)
					}
				}
				// need to identify target  - if it is legit then snap to middle
				// of dropArea
				for (j=0; j<dropWhere[dragCode].length; j++) {
					if (dropWhere[dragCode][j]==dropCode) {
						var newObjX = parseInt(x + 0.5*(dropAreas[dropCode].width - draggables[dragCode].width))
						var newObjY = parseInt(y + 0.5*(dropAreas[dropCode].height - draggables[dragCode].height))
						shiftTo(selectedObj, newObjX, newObjY)
						dropAreas[dropCode].occupied = true
						if (!thisFeedbackLayer) {
							thisFeedback = "correctfeedback"
						}
						setBGColor(thisFeedback, "rgb(102,255,153)")
						show(thisFeedback)
						lastFeedback = thisFeedback
						dropped = true
						return true
					}
				}
				// if it wasn't a correct dropArea then it was a wrong one - act accordingly
				if (!dropped) {
					if (!thisFeedbackLayer) {
						thisFeedback = "incorrectfeedback"
					}
					setBGColor(thisFeedback, "rgb(255,153,153)")
					show(thisFeedback)
					shiftTo(selectedObj, draggables[dragCode].origX, draggables[dragCode].origY)
					lastFeedback = thisFeedback
					dropped = true
					return false
				}
			} 
		}
		if (!dropped) {
			// snaps back to original position if not a legit dropArea
			shiftTo(selectedObj, draggables[dragCode].origX, draggables[dragCode].origY)
			thisFeedback = "missedfeedback"
			setBGColor(thisFeedback, "rgb(255,153,153)")
			show(thisFeedback)
			lastFeedback = thisFeedback
			return false
		}
	}
	return false
}


// called when all the pieces are in place
function isDone() {
	for (var i=0; i < draggablesIndexList.length; i++) {
		if (!draggables[draggablesIndexList[i]].done) {
			return false
		}
	}
	return true
}

// called when page loads - initialises the application
function init() {
	initArray()
	document.onmousemove = dragIt
}
