-- ----------------------------------------------------------------------------
kObject   = "CampfireObject"
kLayout   = "Layout"
kGoal     = "Goal"
-- ----------------------------------------------------------------------------
kClipState_CompletelyInside = 0
kClipState_CompletelyOutside = 1
kClipState_ClipsParts = 2
kClipState_ClipsCompletely = 3
-- ----------------------------------------------------------------------------
kScreenWidth = 1024
kScreenHeight = 768
-- ----------------------------------------------------------------------------
kLeft = 0
kRight = 1
-- ----------------------------------------------------------------------------
SMALLNUMBER = 0.001
-- ----------------------------------------------------------------------------
vkSpace = 17
-- ----------------------------------------------------------------------------
-- Keep in sync with Label.h:
ALIGNH_LEFT = 0 
ALIGNH_CENTER = 1
ALIGNH_RIGHT = 2
ALIGNH_COUNT = 3
-- ----------------------------------------------------------------------------
-- Keep in sync with Anchorpoint.h:
ANCHOR_TOP_LEFT = 0
ANCHOR_TOP_CENTER = 1
ANCHOR_TOP_RIGHT = 2
ANCHOR_CENTER_LEFT = 3
ANCHOR_CENTER = 4
ANCHOR_CENTER_RIGHT = 5
ANCHOR_BOTTOM_LEFT = 6
ANCHOR_BOTTOM_CENTER = 7
ANCHOR_BOTTOM_RIGHT = 8
ANCHOR_CUSTOM = 9
-- ----------------------------------------------------------------------------
-- an ipairs iterator that iterates a table in reverse
function ripairs(t)
  local max = 1
  while t[max] ~= nil do
    max = max + 1
  end
  local function ripairs_it(t, i)
    i = i-1
    local v = t[i]
    if v ~= nil then
      return i,v
    else
      return nil
    end
  end
  return ripairs_it, t, max
end
-- ----------------------------------------------------------------------------
-- helper function to TitleCase a string
-- gotten from: http://lua-users.org/wiki/StringRecipes
local function tchelper(first, rest)
  return first:upper()..rest:lower()
end
-- ----------------------------------------------------------------------------
function constructPrefixedVariable(name, value, prefix)

	assert(type(name) == "string", "name is not a string")
	assert(type(prefix) == "string", "name is not a string")

	name = string.lower(name)

	if (prefix) then
		name = name:gsub("_", " ")						-- "PORCH_DOORKNOB" becomes "PORCH DOORKNOB"
		name = name:gsub("(%a)([%w_']*)", tchelper)		-- "PORCH DOORKNOB" becomes "Porch Doorknob"
		name = name:gsub(" ", "")						-- "Porch Doorknob" becomes "PorchDoorknob"
	else
		prefix = ""
	end

	local varName = prefix .. name
	
	return constructVariable(varName, value)
	
end
-- ----------------------------------------------------------------------------
function constructVariable(name, value)

	assert(type(name) == "string", "name is not a string")

	if (_G[name] ~= nil) then
	    print("WARNING: overwrite in constructVariable: " .. name)
	else
		print("constructed variable: " .. name)
	end
	
	_G[name] = value
	
	return value

end
-- ----------------------------------------------------------------------------
-- reads from a variable layoutNamesMap that is:
-- 	<variableName> => <layoutID>
-- 
-- it then builds two new maps
--  * layoutMap will map IDs to layout variables
function constructLayoutVariables()

	if (layoutNamesMap == nil) then
		print("ERROR: layoutNamesMap does not exist!")
		return
	end
	
	if (#layoutNamesMap == 0) then
		print("WARNING: layoutNamesMap has no elements!")
	end
	
	if (#layoutMap > 0) then
		print("ERROR: layoutMap already contains entries!")
		return
	end	
	
	for i, layoutEntry in ipairs(layoutNamesMap) do
	    local varName = layoutEntry[1]
	    local layoutID = layoutEntry[2]
	    assert(type(varName) == "string", "varName is not a string")
	    assert(type(layoutID) == "string", "layoutID is not a string")
	
		if (hasLayout(layoutID)) then
			layoutMap[layoutID] = constructVariable(varName, getLayout(layoutID))
			layoutMap[layoutID]:setName(varName)
			layoutMap[layoutID].type = kLayout
		else
			print("WARNING: invalid entry in layouts.lua: " .. varName .. " -> " .. layoutID)
		end
	end
	
end
-- ----------------------------------------------------------------------------
function layoutNewChildParticle(layout, var)

	assert(layout)
	assert(layout.type)
	assert(layout.type == kLayout)
	assert(layout.mParticles)
	
	local name = var["name"]
	if (name == nil) then
		print("ERROR: Particle effect without a name in: " .. layout:name())
		name = "Effect" .. math.random(1000)
	end
	
	if (layout.mParticles[name]) then
		print("ERROR: Particle effect already exists in: " .. layout:name() .. " (" .. name .. ")")
	end

	local effect = newChildParticle(layout, var)
	assert(effect)
	assert(effect.type == nil)
	
	layout.mParticles[name] = effect
	
	return effect

end
-- ----------------------------------------------------------------------------
-- 
function constructLayoutVariablesExt()

	for i, layoutEntry in ipairs(layoutNamesMap) do
	    local varName = layoutEntry[1]
	    local layoutID = layoutEntry[2]
	    local layout = _G[varName]
		if (layout) then
			-- all layouts have our animation wrappers
			layout.animate = doAnimation
			-- debug variable for decoda
			layout.dbgName = layout:name()
			-- all objects have a newChildParticle function
			layout.newChildParticle = layoutNewChildParticle			
		end
	end

end
-- ----------------------------------------------------------------------------
function constructObjectVariablesFirefly()

	for i, layoutEntry in ipairs(layoutNamesMap) do
	    local varName = layoutEntry[1]
	    local layoutID = layoutEntry[2]
	    local layout = _G[varName]
	   	if (layout) then
	   		layout.mFireflies = {}
	   		-- store in layout
			for i = layout:getObjectCount(),1,-1 do
				local object = layout:getObjectByIdx(i-1)
				if (object:isFirefly()) then
					-- all fireflies get stored in mFireflies				
					assert(layout.mFireflies[object] == nil)
					layout.mFireflies[object] = {}				
					
					-- store in objectNamesMap
					local objectID = object:getID()
					local number = objectID:sub(objectID:find("_")+1)
					local varName = "objectFirefly" .. number
					print(varName)
					assert(number)
					assert(objectNamesMap[varName] == nil)
					table.insert(objectNamesMap, {varName, objectID})
				end	
			end	
	   	end	    
	end

end
-- ----------------------------------------------------------------------------
-- see the comments on constructLayoutVariables
-- this function follows similar idea's
function constructObjectVariables()

	if (objectNamesMap == nil) then
		print("ERROR: objectNamesMap does not exist!")
		return
	end
	
	if (#objectNamesMap == 0) then
		print("WARNING: objectNamesMap has no elements!")
	end
	
	if (#objectMap > 0) then
		print("ERROR: objectMap already contains entries!")
		return
	end		

	for i, objectEntry in ipairs(objectNamesMap) do
	    constructObjectVariable(objectEntry[1], objectEntry[2])
	end
	
end
-- ----------------------------------------------------------------------------
function constructObjectVariable(varName, objectID)

    assert(type(varName) == "string", "varName is not a string")
    assert(type(objectID) == "string", "objectID is not a string")	    
    
    if (hasObject(objectID)) then
		objectMap[objectID] = constructVariable(varName, getObject(objectID))
		objectMap[objectID]:setName(varName)
		objectMap[objectID].type = kObject
	else
		print("WARNING: invalid entry in objects.lua: " .. varName .. " -> " .. objectID)			
	end

end
-- ----------------------------------------------------------------------------
-- 
function constructObjectVariablesExt()

	for i, objectEntry in ipairs(objectNamesMap) do
	    local varName = objectEntry[1]
	    local objectID = objectEntry[2]
	    local object = _G[varName]
	    if (object) then
			-- all objects have our animation wrappers
			object.animate = doAnimation
			
			-- debug variable for decoda
			object.dbgName = object:name()			
			
			-- all objects have an isFoundOrUsed function
			object.isFoundOrUsed = function()
				return object:isFound() or object:isUsed()			
			end
			-- all objects have additional cursor functions
			object.setCursor = function(self, cursor)
				assert(type(cursor) == "string")
				local layout = object:getLayout()
				if (not layout) then
					print("WARNING: attempting to set cursor on object that is not in a layout")
					return
				end
				if (not layout.mProps[object]) then
					print("WARNING: attempting to set cursor on object that does not have any properties")
					return
				end
				layout.mProps[object].cursor = cursor
			end
			object.setDragCursor = function(self, cursor)
				assert(type(cursor) == "string")
				local layout = object:getLayout()
				if (not layout) then
					print("WARNING: attempting to set cursor on object that is not in a layout")
					return
				end
				if (not layout.mProps[object]) then
					print("WARNING: attempting to set cursor on object that does not have any properties")
					return
				end
				layout.mProps[object].dragcursor = cursor
			end
			object.setCursors = function(self, cursor)
				assert(type(cursor) == "string")
				local layout = object:getLayout()
				if (not layout) then
					print("WARNING: attempting to set cursor on object that is not in a layout")
					return
				end
				if (not layout.mProps[object]) then
					print("WARNING: attempting to set cursor on object that does not have any properties")
					return
				end
				layout.mProps[object].cursors = cursor
				layout.mProps[object].cursor = nil
				layout.mProps[object].dragcursor = nil
			end
			-- all objects have a pocket variable, that can be nil
			-- a pocket 'blocks' an object
			local pocketVarName = nil
			pocketVarName = object:name()
			pocketVarName = pocketVarName .. "Pocket"
			object.pocket = _G[pocketVarName]
			-- all objects have an isInsidePocket function
			object.isInsidePocket = function(self, x, y)
				if (self.pocket) then
					assert(self.pocket.type == kObject)
					return self.pocket:isInsideStrict(x, y)
				end
			end
			-- all objects have a newChildParticle function
			object.newChildParticle = newChildParticle
		end
	end

end
-- ----------------------------------------------------------------------------
-- see the comments on constructLayoutVariables
-- this function follows similar idea's
function constructGoalVariables()

	if (goalNamesMap == nil) then
		print("ERROR: goalNamesMap does not exist!")
		return
	end
	
	if (#goalNamesMap == 0) then
		print("WARNING: goalNamesMap has no elements!")
	end
	
	if (#goalMap > 0) then
		print("ERROR: goalMap already contains entries!")
		return
	end		

	for i, goalEntry in ipairs(goalNamesMap) do
	    local varName = goalEntry[1]
	    local goalID = goalEntry[2]
	    assert(type(varName) == "string", "varName is not a string")
	    assert(type(goalID) == "string", "goalID is not a string")	    
	    
		goalMap[goalID] = constructVariable(varName, addGoal(goalID))
		goalMap[goalID]:setName(varName)
		goalMap[goalID].type = kGoal
	end
	
end
-- ----------------------------------------------------------------------------
-- makes sure math.random exists...
if (not math.random) then
	if (math.Random) then
		math.random = math.Random
	else
		error("no random function available!")
	end
end
-- ----------------------------------------------------------------------------
-- calculates the distance between two points
function math.distance(x1, y1, x2, y2)
	assert(type(x1) == "number")
	assert(type(y1) == "number")
	assert(type(x2) == "number")
	assert(type(y2) == "number")

	dx = x2 - x1
	dy = y2 - y1

	return math.sqrt(math.pow(dx,2)+math.pow(dy,2))
end
-- ----------------------------------------------------------------------------
-- gets the cursor selection
-- it returns nil if there is no selection, while cursor:getSelection()
-- returns null... which is different!
function getSelection()

	local s = nil
	
	assert(cursor)
	if (cursor:hasSelection()) then
		s = cursor:getSelection()
	end

	return s
	
end
-- ----------------------------------------------------------------------------
function getSelectionID()

	local s = getSelection()
	if (s) then
		return s:getID()
	end
	return ""
	
end
-- ----------------------------------------------------------------------------
function crossFadeImages(oldObject, newObject, animID)
	newObject:setAlpha(0)
	newObject:ss(kIdle)
	newObject:animate(animID,
		{ alpha=0									},
		{ alpha=1,		duration=kTimeFadeSwitch	}
	)
	
	oldObject:ss(kIdle)		
	oldObject:animate(
		{ alpha=0,		duration=kTimeFadeSwitch	}
	)
	
end