include("scripts/helperfunctions.lua")
include("scripts/interface.lua")
include("scripts/persons/npc.lua")
include("scripts/persons/cleaner.lua")
include("scripts/persons/fakecustomer.lua")
include("scripts/persons/luacustomer.lua")
include("scripts/persons/storycharacter.lua")
include("scripts/persons/elevatorcustomer.lua")

-- global variables and behavior we might need accross multiple restaurants
dayEnded = false
dayEndTick = false

-- object manager
om = nil

-- trophy manager
tm = nil

-- upgrade manager
um = getUpgradeManager()

-- people manager
pm = nil

-- product manager
prm = nil

-- hint manager
hm = nil

-- emily
emily = nil

-- cleaner
-- if no cleaner, this will stay nil. so check for it!
cleaner = nil

-- entertainer
-- if no entertainer, this will stay nil. so check for it!
entertainer = nil

-- player
player = getPlayer()

-- level
level = nil

-- input
input = nil

-- scene
scene = nil

-- tray
tray = nil

-- event gui
eventBar = nil

-- register
register = nil

-- telephone
telephone = nil

-- handlers
event = interface()
restaurant = interface()
day = interface()

-- anchor points
TOPLEFT 		= 0
TOPCENTER 		= 1
TOPRIGHT 		= 2
LEFT 			= 3
CENTER 			= 4
RIGHT 			= 5
BOTTOMLEFT 		= 6
BOTTOMCENTER 	= 7
BOTTOMRIGHT 	= 8

--music defines
MAIN 			= 0
TENDER 			= 1
NORMAL 			= 2
EXCITING 		= 3

-- CustomerGroupState
GROUP_STATE_ENTERED 						= 0
GROUP_STATE_COUNTER_WALKING_TO_QUEUE 		= 1
GROUP_STATE_COUNTER_WAITING_IN_QUEUE 		= 2
GROUP_STATE_COUNTER_THINKING 				= 3
GROUP_STATE_COUNTER_ORDERING 				= 4
GROUP_STATE_COUNTER_WALKING_TO_EXIT 		= 5
GROUP_STATE_TABLE_WALKING_TO_TABLE 			= 6
GROUP_STATE_TABLE_THINKING 					= 7
GROUP_STATE_TABLE_ORDERING 					= 8
GROUP_STATE_TABLE_EATING 					= 9
GROUP_STATE_TABLE_LOOKING_FOR_QUEUE_TO_PAY 	= 10
GROUP_STATE_TABLE_WALKING_TO_QUEUE_TO_PAY 	= 11
GROUP_STATE_TABLE_WALKING_TO_TABLE_PAY_FULL = 12
GROUP_STATE_TABLE_WAITING_IN_QUEUE_TO_PAY 	= 13
GROUP_STATE_TABLE_PAYING 					= 14
GROUP_STATE_TABLE_WALKING_TO_EXIT 			= 15

-- eBlendMode
BLENDMODE_BLEND								= 0
BLENDMODE_ADDITIVE							= 1
BLENDMODE_ADDITIVE_IGNOREALPHA				= 2
BLENDMODE_MULTIPLY							= 3
BLENDMODE_COPY								= 4
BLENDMODE_COPY_COLOR						= 5
BLENDMODE_MAXALPHA							= 6
BLENDMODE_COPYALPHA							= 7

-- table states
PLATES_FULL			= 0
PLATES_EMPTY		= 1
PLATES_CLEANING		= 2
PLATES_INVISIBLE	= 3

-- grid
GRID_SPACING_X = 16
GRID_SPACING_Y = 16
GRID_SIZE_X = 50
GRID_SIZE_Y = 35

-- direction
DIRECTION_UP = 0
DIRECTION_DOWN = 1
DIRECTION_LEFT = 2
DIRECTION_RIGHT = 3
DIRECTION_COUNT = 4

-- level layers
LAYER_BACKGROUND = 0
LAYER_GRID_ELEMENTS = 1
LAYER_FEATURES = 2
LAYER_FLOATERS = 3
LAYER_ENGINE = 4
LAYER_INPUT = 5
LAYER_COUNT = 6

hintMap = {
	-- General
	"HINT_MOOD_DROP",
	"HINT_LEAVING",
	"HINT_REMOVE_TRAY_ITEM",
	"HINT_SHOP",
	-- Hotel restaurant #1
	"HINT_NEWSPAPERS_REFILLED",
	"HINT_TURKEY",
	"HINT_KEBAB_FINISHED",
	"HINT_STIR_PEARS",
	"HINT_KEY_DROPED",
	"HINT_SPIDER_WEB_CLEANING",
	"HINT_ATTENTION",
	-- Winter fair restaurant #2
	"HINT_APPLAUSE",
	"HINT_STIR_SOUP",
	-- Hotel restaurant #3
	"HINT_MORE_LOGS",
	"HINT_FOOTSTEPS_CLEANING",
	-- Skihut restaurant #4
	"HINT_FONDUE_REFILL",
	"HINT_FONDUE_TIP",
	-- Farm restaurant #5
	"HINT_COOKIE_CUTTER",
	"HINT_COOKIE_DONE",
	"HINT_SHED_HEAT",
	"HINT_SLIPPERY_ICE",
	"HINT_BREAK_ICE",
}

arrowMap = {
	-- General
	"ARROW_HINT_REGISTER",
	"ARROW_HINT_ENTERTAINER",
	"ARROW_HINT_TABLE_TO_BE_ENTERTAINED",
	
	-- Hotel restaurant #1
	"ARROW_HINT_NEWSPAPERS_DROPED",
	"ARROW_HINT_RAW_TURKEY",
	"ARROW_HINT_TURKEY_BASTER_1",
	"ARROW_HINT_TURKEY_BASTER_2",
	"ARROW_HINT_TURKEY_FINISHED",
	"ARROW_HINT_POSTMAN",
	"ARROW_HINT_KEY_RETURNED",
	"ARROW_HINT_ROOM_REQUESTED",
	"ARROW_HINT_KEY_ORDERED",
	"ARROW_HINT_MAIL_QUERY",
	"ARROW_HINT_MAIL_PICKUP",
	"ARROW_HINT_KEBAB",
	"ARROW_HINT_SAUSAGE",
	"ARROW_HINT_INVITATION_MADE",
	"ARROW_HINT_INVITATION_PLACE",
	"ARROW_HINT_DANCE",
	
	-- Hotel restaurant #3
	"ARROW_HINT_SPIES_1",
	"ARROW_HINT_SPIES_2",
	"ARROW_HINT_SPIES_3",
	"ARROW_HINT_SPIES_READY",
	"ARROW_ENTER_ELEVATOR",
	"ARROW_HIDDEN_OBJECT",
	"ARROW_CHASE",
	
	-- Skihut restaurant #4
	"ARROW_FONDUE_REFILL",
	"ARROW_FONDUE_CHOP",
	
	-- Farm restaurant #5
	"ARROW_MILK_PAN",
	"ARROW_MILK_TOPPING",
	"ARROW_COOKIE_DOUGH",
	"ARROW_COOKIE_CUTTER",
	"ARROW_COOKIE_DONE",
	"ARROW_FALLEN_SKATER",
	"ARROW_MISTLETOE_STOCK",
	"ARROW_SHED_HEAT",
	"ARROW_SLIPPERY_ICE",	
}

function getHintIndex(hintID)

	local nrHints = table.getn(hintMap)
	
	for index, hint in ipairs(hintMap) do
		if (hint == hintID) then
			return (index - 1)
		end
	end
	return -1
	
end

function getArrowIndex(arrowID)

	local nrHints = table.getn(hintMap)
	
	for index, arrow in ipairs(arrowMap) do
		if (arrow == arrowID) then
			return (index + nrHints - 1)
		end
	end
	return -1
	
end

-- customer
CUSTOMER_READING_MIN_INTERVAL = 1200
CUSTOMER_READING_RND_INTERVAL = 1200
CUSTOMER_ORDERING_MIN_INTERVAL = 1600
CUSTOMER_ORDERING_RND_INTERVAL = 1600
CUSTOMER_EATING_MIN_INTERVAL = 1000
CUSTOMER_EATING_RND_INTERVAL = 1500

-- score
scoreTableFloaterOffsetY = -75
scorePersonFloaterOffsetY = -75
scoreBonusFloaterDelay = 1500

-- configuration
autoStartGame = true
autoAllPeopleWalkIn = true
autoAllPeopleWalkOut = true
makeProgressBarsSmall = false

function onLevelGlobals()

	om = getObjectManager()
	tm = getDeliciousTrophyManager()
	um = getUpgradeManager()
	pm = getPeopleManager()
	prm = getProductManager()
	hm = getHintManager()
	emily = pm:getEmily()
	cleaner = pm:getCleaner()
	entertainer = pm:getEntertainer()
	grid = getGrid()
	level = getLevel()
	input = getInput()
	scene = getScene()
	tray = getTray()
	eventBar = getEventBar()
	register = om:getRegister()
	telephone = om:getTelephone()
	
	if (entryX ~= nil and entryY ~= nil) then
		entryNode = grid:getNode(entryX, entryY)
	end
	if (exitX ~= nil and exitY ~= nil) then
		exitNode = grid:getNode(exitX, exitY)
	end
	if (movieX ~= nil and movieY ~= nil) then
		movieNode = grid:getNode(movieX, movieY)	
	end
	
	event:onLevelGlobals()
	day:onLevelGlobals()
	restaurant:onLevelGlobals()

end

function onLevelInited()

	event:onLevelInited()
	day:onLevelInited()
	restaurant:onLevelInited()

	if (autoStartGame == true) then
		scene:startGame()
		
	end
	
end

function onDayBegin()

	event:onDayBegin()
	day:onDayBegin()	
	restaurant:onDayBegin()

	if (autoAllPeopleWalkIn == true) then
		pm:allPeopleWalkIn()
	end
	
end

function onDayEnd()

	cout("&&&&&&&&& global onDayEnd &&&&&&&&&&\n")
	event:onDayEnd()
	day:onDayEnd()
	restaurant:onDayEnd()
	
	if (autoAllPeopleWalkOut == true) then
		pm:allPeopleWalkOut()
	else
		if (cleaner) then
			cleaner:setReturnHome(false)
		end
		
		if (entertainer) then
			entertainer:setReturnHome(false)
		end	
	end
	
    dayEnded = true

end

function onAllTasksFinished()
	
	if (event.onAllTasksFinished) then
		return event:onAllTasksFinished()
	elseif (day.onAllTasksFinished) then
		return day:onAllTasksFinished()
	elseif (restaurant.onAllTasksFinished) then
		return restaurant:onAllTasksFinished()
	end

	return true
end

function onTick(time)

    if ((not dayEnded) or dayEndTick) then
    	event:onTick(time)
	    day:onTick(time)
	    restaurant:onTick(time)
	    
    end
	
end


function onDeactivate()

    if (event.onDeactivate) then
    	event:onDeactivate()
    end
    
    if (day.onDeactivate) then
	    day:onDeactivate()	
    end
    
    if (restaurant.onDeactivate) then
	    restaurant:onDeactivate()
	end

end

function onPause(pause)

    if (event.onPause) then
	    event:onPause(pause)
	end
	
    if (day.onPause) then
    	day:onPause(pause)	
    end
    
    if (restaurant.onPause) then
    	restaurant:onPause(pause)
    end
	
end

function onRemoveProductFromTray(product)

	if (event.onRemoveProductFromTray) then
        event:onRemoveProductFromTray(product)
     end
	
	if (day.onRemoveProductFromTray) then
	    day:onRemoveProductFromTray(product)
	end

	if (restaurant.onRemoveProductFromTray) then
	    restaurant:onRemoveProductFromTray(product)	    
    end

end

function onClick(x, y)
  
   local handled = false
   if (not dayEnded) then
    	
    	if (event.onClick) then
            handled = event:onClick(x, y)
         end
    	
    	if (day.onClick and not handled) then
    	    handled = day:onClick(x, y)
    	end

    	if (restaurant.onClick and not handled) then
    	    handled = restaurant:onClick(x, y)	    
	    end
    end
    
    return handled
end

function onClickProduct(product)

	if (event:onClickProduct(product) == true) then
		return true
	end

	if (day:onClickProduct(product) == true) then
		return true
	end
	
	if (restaurant:onClickProduct(product) == true) then
		return true
	end	
	
	return false

end


function onClickRegister(register)

	if (event:onClickRegister(register) == true) then
		return true
	end

	if (day:onClickRegister(register) == true) then
		return true
	end
	
	if (restaurant:onClickRegister(register) == true) then
		return true
	end		
	
	return false

end


function onClickPerson(person)
	
	if (event:onClickPerson(person) == true) then
		return true
	end

	if (day:onClickPerson(person) == true) then
		return true
	end
	
	if (restaurant:onClickPerson(person) == true) then
		return true
	end		
	
	return false	
	
end

function onClickObject(object)

	if (event:onClickObject(object) == true) then
		return true
	end

	if (day:onClickObject(object) == true) then
		return true
	end
	
	if (restaurant:onClickObject(object) == true) then
		return true
	end		
	
	return false

end


function onClickTable(table)

	if (event:onClickTable(table) == true) then
		return true
	end

	if (day:onClickTable(table) == true) then
		return true
	end
	
	if (restaurant:onClickTable(table) == true) then
		return true
	end			
	
	return false

end

function onClickMessage(text, result)

	event:onClickMessage(text, result)
	day:onClickMessage(text, result)
	restaurant:onClickMessage(text, result)

end

function onClickButton(button)

	event:onClickButton(button)
	day:onClickButton(button)
	restaurant:onClickButton(button)

end

function onLockToNode(actor, node)

	event:onLockToNode(actor, node)
	day:onLockToNode(actor, node)
	restaurant:onLockToNode(actor, node)
	
end

function onTaskHook(actorType, taskType, p0, p1)

	if (event:onTaskHook(actorType, taskType, p0, p1) == true) then
		return true
	end

	if (day:onTaskHook(actorType, taskType, p0, p1) == true) then
		return true
	end
	
	if (restaurant:onTaskHook(actorType, taskType, p0, p1) == true) then
		return true
	end	
	
	return false

end

function onTaskStart(actorType, taskType, p0, p1)

	event:onTaskStart(actorType, taskType, p0, p1)
	day:onTaskStart(actorType, taskType, p0, p1)
	restaurant:onTaskStart(actorType, taskType, p0, p1)
	
end

function onTaskComplete(actorType, taskType, p0, p1)

	event:onTaskComplete(actorType, taskType, p0, p1)
	day:onTaskComplete(actorType, taskType, p0, p1)
	restaurant:onTaskComplete(actorType, taskType, p0, p1)

end

function onTaskCancel(actorType, taskType, p0, p1)

	event:onTaskCancel(actorType, taskType, p0, p1)
	day:onTaskCancel(actorType, taskType, p0, p1)
	restaurant:onTaskCancel(actorType, taskType, p0, p1)
	
end

function onCustomerGroupStateChange(customerGroup, prevState, endState)

	if (event.onCustomerGroupStateChange) then
		event:onCustomerGroupStateChange(customerGroup, prevState, endStateprevState, endState)
	end
	
	if (day.onCustomerGroupStateChange) then
		day:onCustomerGroupStateChange(customerGroup, prevState, endState)
	end

	if (restaurant.onCustomerGroupStateChange) then
		restaurant:onCustomerGroupStateChange(customerGroup, prevState, endState)
	end
	
end

function onNotify(notification)

	event:onNotify(notification)
	day:onNotify(notification)
	restaurant:onNotify(notification)

end

function createD2Sprite()
    return D2Sprite()
end

function createPerson(class)

	p = event:createPerson(class)
	if (p ~= nil) then
		return p
	end  

	p = day:createPerson(class)
	if (p ~= nil) then
		return p
	end
	
	p = restaurant:createPerson(class)
	if (p ~= nil) then
		return p
	end  	
	
	if (class == "francois") then
		f = npc("INGAME_PERSON_FRANCOIS")
		f:setType("francois")
		f:setButtX(20)
		f:animate("IDLE", 0)
		return f
		
	elseif (class == "fakecustomer") then
		fc = fakecustomer("INGAME_CUSTOMER_MALE_WINTER_REGULAR")
		fc:setType("fakecustomer")
		fc:animate("IDLE", 0)
		return fc
		
	elseif (class == "luacustomer") then
		fc = luacustomer("INGAME_CUSTOMER_MALE_REGULAR")
		fc:setType(class)
		fc:animate("IDLE", 0)
		return fc		
		
	elseif (class == "npc") then
		n = npc("")
		n:setType("npc")
		return n

	elseif (class == "story_character") then
		m = storyCharacter("")
		m:setType(class)
		return m
		
	elseif (class == "elevatorCustomer") then
		m = elevatorCustomer("INGAME_CUSTOMER_MALE_REGULAR")
		m:setType(class)
		m:animate("IDLE", 0)
		return m

	end
  
	return nil
  
end

function createObject(desc)

	o = event:createObject(desc)
	if (o ~= nil) then
		return o
	end  

	o = day:createObject(desc)
	if (o ~= nil) then
		return o
	end  	
	
	o = restaurant:createObject(desc)
	if (o ~= nil) then
		return o
	end  	
	
  	return nil

end

function createProduct(base)

	p = event:createProduct(base)
	if (p ~= nil) then
		return p
	end  

	p = day:createProduct(base)
	if (p ~= nil) then
		return p
	end  	
	
	p = restaurant:createProduct(base)
	if (p ~= nil) then
		return p
	end  	
	
  	return nil

end

function math.contains(rectangle, x, y)

	local left = rectangle[1]
	local top = rectangle[2]
	local right = rectangle[3]
	local bottom = rectangle[4]
	local width = right - left
	local height = bottom - top
	
	local x_ = x - left
	local y_ = y - top
	
	if (x_ < 0) then
		return false
	end
	
	if (y_ < 0) then
		return false
	end
	
	if (x_ >= width) then
		return false
	end
	
	if (y_ >= height) then
		return false
	end
	
	return true

end