-- -----------------------------------------------------------------------------
-- SimNetwork
-- simulation.lua
-- Simulation Engine
-- -----------------------------------------------------------------------------

SimNetwork_SIMULATION = true

if not SimNetwork_DATA		then dofile("data.lua")	end
if not SimNetwork_BALANCE	then dofile("balance.lua")	end

START_DELTA		= 128		-- Used by Ford-Fulkerson for faster
					-- augmenting path finding
LEVEL = {
	{
		startBalance = 2000,
		customerCount = 20 
	},
	
	{
		startBalance = 1500,
		customerCount = 30
	},
	
	{
		startBalance = 1000, 
		customerCount = 40
	},
}

MAX_CUSTOMERS = 40

Sim = {}
SimMt = {__index = Sim}

function Sim:init(diffLevel)
	--print("in Sim:init()")
	local newSim = {
		balance = LEVEL[diffLevel].startBalance,
		customersLeft = LEVEL[diffLevel].customerCount - 2,	
						-- we start with two customers

						-- player's balance
		overallSat = 100,		-- overall customer satisfaction
		
		
		customers = nil,		-- pointers to gObjects of
		routers = nil,			-- game elements. Actual elements
		cables = nil,			-- are stored in Scene.map, these
						-- are just pointers to them
						
		stateChanged = false,		-- indicates whether anything
						-- changed since the last time
						-- the simulation algorithm was called
						
		lastDoTimer = getTime(),	-- last time Sim:do_timer() was called
		level = diffLevel,
		
		lastCustomer = 0.0,
		
		waiting = false,	-- used to take a nap; we do so to wait for answer when asking the
					-- scene for collision testing
		newCusType = nil,	-- type of new customer
		newCusX = nil,
		newCusZ = nil,
	}
	
	setmetatable(newSim, SimMt)
	
	return newSim
end

function Sim:modified()
	--print("in Sim:modified()")
	self.stateChanged = true
end

function Sim:setLogicData(pCustomers, pRouters, pCables)
	--print("in Sim:setLogicData()")
	self.customers = pCustomers
	self.routers = pRouters
	self.cables = pCables
end

function Sim:getBalance()
	--print("in Sim:getBalance()")
	return self.balance
end

function Sim:getSatisfaction()
	--print("in Sim:getSatisfaction()")
	return self.satisfaction
end

--------------------------------------------------------------------------------
-- Function:	Sim:negotiate()
-- Inputs:	monthlyFee: supplied by player
--		customer: a customer O object
-- Returns:	boolean: indicating whether customer acceped offer or not		
function Sim:negotiate(monthlyFee, customer)
	--print("in Sim:negotiate()")
	if	monthlyFee <= customer:getInitialFee() and 
		customer:getLink() ~= nil --and
		--customer:getLink().cable:getBandwidth() >= customer:getInitialBandwidth()
	then
		customer:signContract(monthlyFee)
		self:modified()
		return true
	else
		return false
	end
end

function Sim:getUnitCost(unitType)
	--print("in Sim:getUnitCost()")
	if CABLES[unitType] ~= nil then
		return CABLES[unitType].c
	elseif ROUTERS[unitType] ~= nil then
		return ROUTERS[unitType].c
	else
		return nil
	end
	
end

function Sim:canPurchase(cost)
	--print("in Sim:canPurchase()")
	if self.balance >= cost then
		return true
	else
		return false
	end
end

function Sim:purchase(cost)
	--print("in Sim:purchase()")
	if self.balance < cost then
		return false
	else
		self.balance = self.balance - cost
		-- update balance bar
		update_balance(self.balance)
		if gameScene.state ~= SCENE_STATE.demolish then
			--play sound
			E.play_sound(moneySound)
		end
		return true
	end
end

function Sim:begin()
	--print("in Sim:begin()")
	update_balance(self.balance)
	update_overallCustSatisfaction(self.satisfaction)
end

--------------------------------------------------------------------------------
-- Function:	Sim:getCableEnds()
-- Inputs:	theCable: O object of type cable
-- Returns:	2 O objects: the two ends of the given cable, or nil if not found
function Sim:getCableEnds(theCable)
	--print("in Sim:getCableEnds()")
	-- search for cable reference in customers
	for x, aCustomer in self.customers do
		local link = aCustomer.o:getLink()
		if link ~= nil then
			if link.cable == theCable then
				return aCustomer.o, link.destination
			end
		end
	end
	
	for x, router in self.routers do
		for y, link in router.o:getLinks() do
			if link.cable == theCable then
				return router.o, link.destination
			end
		end
	end
	
	return nil
end

--------------------------------------------------------------------------------
-- Function:	Sim:removeElement()
-- Inputs:	o: O object
-- Returns:	none
function Sim:removeElement(o)
	--print("in Sim:removeElement()")
	local index = nil
	if o:getElementType() == "cable" then
		index = searchTable(self.cables, "o", o)
		
		-- determine the two ends of the cable
		local element1, element2 = self:getCableEnds(o)
		
		-- remove the cable from their list of connections
		element1:removeCable(o)
		element2:removeCable(o)
		table.remove(self.cables, index)
		
	elseif o:getElementType() == "router" then
		index = searchTable(self.routers, "o", o)
		table.remove(self.routers, index)
	elseif o:getElementType() == "customer" then
		index = searchTable(self.customers, "o", o)
		table.remove(self.customers, index)
	end
	
	self:modified()
end
--------------------------------------------------------------------------------
-- Class:	FlowGraph
-- Represents the flow network graph
FlowGraph = {}
FlowGraphMt = {__index = FlowGraph}

--------------------------------------------------------------------------------
-- Function FlowGraph:new()
-- Inputs:	sourceNode:	game entity of customer being simulated
-- Returns:	FlowGraph instance
function FlowGraph:new(sourceNode)
	--print("in FlowGraph:new()")
	local graph = {
		source = {vertex = sourceNode, outgoing = {}},
		sink = {vertex = nil, outgoing = {}, name = "Hey, I'm the sink!"},
		nodes = {}
	}
	setmetatable(graph, FlowGraphMt)
	return graph
end

function FlowGraph:getNodes()
	return self.nodes
end

function FlowGraph:getSource()
	return self.source
end

function FlowGraph:getSink()
	return self.sink
end

function FlowGraph:getEdges(node)
	return node.outgoing
end

function FlowGraph:getEdge(sourceNode, destinationNode)
	for x, edge in self:getEdges(sourceNode) do
		if edge.destination == destinationNode then
			return edge
		end
	end
	
	return nil
end

function FlowGraph:addNode(logicElement)
	--print("in FlowGraph:addNode()")
	table.insert(self.nodes, {vertex = logicElement, outgoing = {}})
	return table.getn(self.nodes)
end

function FlowGraph:getNode(logicElement)
	--print("in FlowGraph:getNode()")
	if logicElement == self.source.vertex then
		return self.source
	end
	
	for key, node in self:getNodes() do
		if logicElement == node.vertex then
			return node
		end
	end
	return nil
end

function FlowGraph:addOutgoingEdge(outLogicElement, inLogicElement, theCable)
	--print("in FlowGraph:addOutgoingEdge()")
	-- see if out node already exist in flow graph
	local outN = self:getNode(outLogicElement)
	local inN = self:getNode(inLogicElement)
	
	if outN == nil then
		outN = self.nodes[self:addNode(outLogicElement)]
	end	
		
	if inN == nil then
		inN = self.nodes[self:addNode(inLogicElement)]
	end
		
	-- now add outgoing edge
	table.insert(outN.outgoing, {
		destination = inN,
		capacity = theCable:getBandwidth(),
		flow = 0,
		cable = theCable	-- pointer to the cable that caused
					-- this flow graph edge to be added
		
	})
end

function FlowGraph:addEdge2Sink(logicElement, peerShare)
	--print("in FlowGraph:addEdge2Sink()")
	local node = self:getNode(logicElement)
	if node == nil then
		node = self.nodes[self:addNode(logicElement)]
	end
	
	table.insert(node.outgoing, {
		destination = self.sink,
		capacity = peerShare,
		flow = 0,
	})
end

--------------------------------------------------------------------------------
-- Function:	FlowGraph:buildResidualGrpah()
-- Builds residual graph corresponding to current flow. For information on
-- residual graph, please see Wikipedia article on the Ford-Fulkerson algorithm
function FlowGraph:buildResidualGraph()
	--print("in FlowGraph:buildResidualGraph()")
	-- remove previous residual edges of all nodes
	--print("** FlowGraph:buildResidualGraph()...")
	self.source.residual = {}
	self.sink.residual = {}
	
	for x, node in self:getNodes() do
		--print("\tReseting residual edges of node:", node.vertex:getElementType(), "out edges:", table.getn(node.outgoing))
		node.residual = {}
	end
	
	-- loop through nodes
	for i=1, 2 do
		local vertexSet
		if i == 1 then
			-- first iteration builds residual edges for source
			vertexSet = {self.source}
		else
			-- second iteration builds residual edges for other nodes
			vertexSet = self:getNodes()
		end
			
		for a, node in vertexSet do
			--print("\tLooping over outgoing edges of node:", node.vertex:getName())
			-- loop through edges
			for b, edge in self:getEdges(node) do
				--print("\t\tEdge of capacity:", edge.capacity)
				local forward = edge.capacity - edge.flow
				local backward = edge.flow
				
				-- add forward res. edge from current node to destination
				if forward > 0 then
					table.insert(node.residual, {
						destination = edge["destination"],
						label = forward,
						t = "forward",		-- type of res. edge
						flowEdge = edge		-- refrence to the flow edge that
									-- caused this residual edge
					})
				end
					
				-- add backward edge from destination to current node
				if backward > 0 then
					table.insert(edge.destination.residual, {
						destination = node,
						label = backward,
						t = "backward",		-- type of res. edge
						flowEdge = edge	
					})
				end
			end --for edges
		end --for vertexSet
	end --for i
			
end

--------------------------------------------------------------------------------
-- Function FlowGraph:findPath()
-- Inputs:	currentP: current path
--------------------------------------------------------------------------------
function FlowGraph:findPath(minB, curB, node, destination, currentP)
	--print("in FlowGraph:findPath()")
	if node.vertex ~= nil then
		--print("** findPath, from:", node.vertex:getName(), "minB:", minB, ",curB:", curB)
	else
		--print("** findPath, from: SINK")
	end

	if node == destination then
		return true, currentP, curB
	elseif node.vertex == nil then
		-- reached sink, but we're not simulating the path to sink
		--print("** WARNING: findPath: reached sink but did not succeed!")
		return false, nil, nil
	elseif node.discovered then
		--print("\tDiscovered node. Backtracking.")
		return false, nil, nil
	end
	
	node.discovered = true
	
	-- do DFS
	for x, resEdge in node.residual do
		-- make sure we select edges with label greater of equal to
		-- bottle-neck. This ensures that the Ford-Fulkerson algorithm
		-- advances at a much faster rate; O(n^3)
		if resEdge.label >= minB then
			-- update minimum bottle neck
			if curB > resEdge.label then
				curB = resEdge.label
			end
			
			-- copy sub path: because we're using recursion, we need
			-- seperate copies
			local subPath = copyTable(currentP)
			
			-- insert a pointer to the residual edge we have followed
			table.insert(subPath, resEdge)
			
			if resEdge.destination ~= self.sink then
				if resEdge.destination.vertex == nil then
					-- shouldn't get here	- (Khairi Nov/22)
					print("** Opps, we're following an edge that does not have a name, it's not even the sink!")
					print("** I'm currently at:", node.vertex:getName(), ". Offending residual edge is indexed at: ", x)
					print("** Here are the fields in this node:")
					for key, value in resEdge.destination do
						print("\tKey:",key,", Val:", value)
					end
					--print("I'm giving up :(")
				end
				--print("\tFollowing residual edge to: ", resEdge.destination.vertex:getName())
			else
				--print("\tFollowing residual edge to: SINK")
			end
			
			table.insert(subPath, resEdge.destination)
			
			local res, path, bottleNeck = self:findPath(minB, curB, resEdge.destination, destination, subPath)
			
			if res == true then
				-- we have reached a path, return it
				--print("Found path with bottle neck:", curB)
				return true, path, bottleNeck
			end
			--print("\t** Again at: ", node.vertex:getName())
			--print("\tNo path, proceeding to try next residual edge.")
		end
		
	end
	
	--print("\tCompleted all residual edge at: ", node.vertex:getName())
	
	return false, nil
end

--------------------------------------------------------------------------------
-- Function:	FlowGraph:FindAugmentingPath()
-- Inputs:	minBottleNeck: minimum bottleneck to accept in augmenting path
-- Returns:	Augmenting path, or nil for no path
-- Algorithm tries to find source-sink path with minimum bottle-neck not
-- exceeding b.
--------------------------------------------------------------------------------
function FlowGraph:findAugmentingPath(minBottleNeck, node, destination)
	--print("in FlowGraph:finAugmentingPath()")
	--print("findAugmentingPath, minB:",minBottleNeck)
	self.source.discovered = false
	for x, node in self:getNodes() do
		node.discovered = false
	end
	
	local res, path, bottleNeck = self:findPath(minBottleNeck, INFINITY, node, destination, {})
	return res, path, bottleNeck
end

--------------------------------------------------------------------------------
-- Function:	FlowGraph:augment()
-- Inputs:	path: returned from findAugmentingPath()
--		bottleNeck: the bottle neck residual edge in path
-- Returns:	none
-- Augments given augmenting path increasing the flow by bottleNeck
--------------------------------------------------------------------------------
function FlowGraph:augment(path, bottleNeck)
	--print("in FlowGraph:augment()")
	--print("**** Augmenting path.")
	
	-- start from source
	local currentNode = self.source
	
	while table.getn(path) > 0 do
		-- pop reference residual edge
		local resEdge = table.remove(path, 1)
		local flowEdge = resEdge.flowEdge
		
		if resEdge.t == "forward" then
			-- forward edge: increase flow by bottle neck
			flowEdge.flow = flowEdge.flow + bottleNeck
			if flowEdge.cable == nil then
				-- this edge is going to sink, it is a virtual one
				-- therefore, it does not have an actual corresponding
				-- cable
			else
				flowEdge.cable:adjustConsumption(self.source.vertex, flowEdge.flow)
			end
		else
			-- backward edge: decrese flow
			flowEdge.flow = flowEdge.flow - bottleNeck
			if flowEdge.cable ~= nil then
				flowEdge.cable:adjustConsumption(self.source.vertex, flowEdge.flow)
			end
		end
	
		-- move to next node in path
		currentNode = table.remove(path, 1)
	end
end
--------------------------------------------------------------------------------
-- Function:	FlowGraph:Ford_Fulkerson 
-- implementation of Ford-Fulkerson algorithm for determining the minimum cut,
-- max flow
function FlowGraph:Ford_Fulkerson()
	--print("in FlowGraph:Ford_Fulkerson()")
	self:buildResidualGraph()
	local delta = START_DELTA
	local res, path, bottleNeck = self:findAugmentingPath(delta, self.source, self.sink)
	
	-- while there's still an augmenting path
	while delta >= 1 do
	
		self:buildResidualGraph()
		local res, path, bottleNeck = self:findAugmentingPath(delta, self.source, self.sink)
		
		while res do
			self:augment(path, bottleNeck)
			self:buildResidualGraph()
			res, path, bottleNeck = self:findAugmentingPath(delta, self.source, self.sink)
		end
		
		delta = delta / 2
	end
		
end

--------------------------------------------------------------------------------
-- Function:	FlowGraph:getDeliveredBandwidth()
-- Inputs:	peer: game logic of some peer
-- Returns:	how much bandwidth this customer can deliver to that peer
function FlowGraph:getDeliveredBandwidth(peer)
	--print("in FlowGraph:getDeliveredBandwidth()")
	local node = self:getNode(peer)
	if node == nil then
		print("** WARNING: getDeliveredBandwidth: could not retrieve argument peer's node.")
		return nil
	end
	
	-- see how much bandwidth flows from peer to sink
	local sinkEdge = self:getEdge(node, self.sink)
	if sinkEdge ~= nil then
		return sinkEdge.flow
	else
		return 0
	end
end

function FlowGraph:getSourceFlow()
	--print("in FlowGraph:getSourceFlow()")
	local totalFlow = 0
	for x, edge in self:getEdges(self.source) do
		totalFlow = totalFlow + edge.flow
	end
	
	return totalFlow
end

--------------------------------------------------------------------------------
-- Function:	FlowGraph:traceFlow()
-- Inputs:	none
-- Returns:	table describing traffic flow of customer
function FlowGraph:traceFlow()

	for x, edge in self:getEdges(self.source) do
		edge.traced = false
	end
	
	for x, node in self:getNodes() do
		for y, edge in self:getEdges(node) do
			edge.traced = false
		end
	end
	
	local queue = {self.source}
	local trafficFlow = {}
	
	-- do BFS
	while table.getn(queue) > 0 do
		local node = table.remove(queue, 1)
		--print("Node type:", node:getElementType())
		
		for x, edge in self:getEdges(node) do
			if not (edge.traced) then
				if edge.flow > 0 then
					if edge.cable ~= nil then
						table.insert(trafficFlow, { 
							cable = edge.cable,
							source = node.vertex, 
							dest = edge.destination.vertex})
					end
					table.insert(queue, edge.destination)
					edge.traced = true
				end
			end -- if not
		end --if
	end --while
	
	return trafficFlow
end
--------------------------------------------------------------------------------
-- Function:	Sim:constructFlowGraph()
-- Inputs:	customer: a game logic entity
-- Return:	flowGraph: instance of FlowGraph representing flow network
-- from customer to his peer(s)
function Sim:constructFlowGraph(customer)
	--print("in FlowGraph:constructFlowGraph()")
	local flowGraph = FlowGraph:new(customer)
	local queue = {}
	
	-- mark all customers and routers as not discovered
	for key, cus in self.customers do
		cus.o.discovered = false
	end
	
	for key, router in self.routers do
		router.o.discovered = false
	end
	
	-- add source node to queue
	table.insert(queue, customer)
	
	-- do BFS
	--print("Starting BFS")
	while table.getn(queue) > 0 do
		-- pop 1st node from queue
		--print("Poped 1 node")
		local node = table.remove(queue, 1)
		--print("Node type:", node:getElementType())
		
		if not (node.discovered) then
			--print("Number of links to node:", table.getn(node:getLinks()))
			
			-- add all descendants nodes to queue
			for x, link in node:getLinks() do
				--print("  * Link to:", link.destination:getElementType())
				
				-- for each cable, add a two directed edges
				flowGraph:addOutgoingEdge(node, link.destination, link.cable)
				table.insert(queue, link.destination)
			end --for
			node.discovered = true
		end --if
	end --while
	
	-- link customer's peer to sink
	for key, peer in customer:getPeers() do
		-- make sure we add peers to the flow graph
		if not flowGraph:getNode(peer) then
			flowGraph:addNode(peer)
		end
		
		-- connect peers to sink
		if peer:getState() == STATE.CUSTOMER.contracted then
			-- only add sink edges to contracted peers
			flowGraph:addEdge2Sink(peer, customer:getDesiredPeerBandwidth())
		end
	end
	
	return flowGraph
end

--------------------------------------------------------------------------------
-- Function:	recapFlow
-- Inputs:	none
-- Returns:	none
-- This function is called after the flow is calculated for each customer
-- individually. 
function Sim:recapFlow()
	--print("in Sim:recapFlow()")
end

function Sim:updateGameVariables()
	
	-- calculate over all satisfaction rate
	-- loop through customers
	local sat = 0
	local customerCount = 0
	for i, customer in self.customers do
		if customer.o:getState() == STATE.CUSTOMER.contracted then
			sat = sat + customer.o:getSatisfactionRate()
			customerCount = customerCount + 1
		end	
	end

	if customerCount > 0 then
		self.overallSat = math.floor(sat / customerCount)
	else
		self.overallSat = 100
	end
	
	-- looser?
	if self.overallSat < GAME_LOOSE_SAT or self.balance < 0 then
		gameWinningState = "lost"
		gameState = GAME_STATE.ended
	end
	
	-- winning
	if	self:allCustomersContracted()	and 
		self.overallSat > GAME_WIN_SAT 
		and self.customersLeft == 0 then
		gameWinningState = "won"
	end
	
end

function Sim:allCustomersContracted()
	for i, customer in self.customers do
		if customer.o:getState() ~= STATE.CUSTOMER.contracted then
			return false
		end
	end
	
	return true
end

function Sim:do_timer()
	--print("in Sim:do_timer()")
	-- pace game: do_timer should be called 
	if getTime() - self.lastDoTimer < TIME_STEP then
		return
	end
	self.lastDoTimer = getTime()
	
	-- loop through contracted customers and add their fee to the balance
	for x, customer in self.customers do
		local cust = customer.o
		cust:timeTick()
		if monthPassed(cust:getTime()) then
			if cust.getState == STATE.CUSTOMER.contracted then
				self.balance = self.balance + cust:getMonthlyFee()
			end
			cust:resetTime()
		end
	end
	
	-- loop through cables and routers and deduct their maintenance costs
	for i = 1, 2 do
		local table = nil
		if i == 1 then
			table = self.routers
		else
			table = self.cables
		end
		
		for x, anElement in table do
			local element = anElement.o
			element:timeTick()
			if monthPassed(element:getTime()) then
				self.balance = self.balance - element:getMaintenanceCost()
				element:resetTime()
			end
		end
	end
	
	-- calculate game variables
	self:updateGameVariables()
	
	-- render new information
	update_overallCustSatisfaction(self.overallSat)
	update_balance(self.balance)
	update_customersLeft(self.customersLeft)
	
	-- Fuzz
	self:do_fuzzy()
end

function Sim:randCustomerPosition()
	
	--[[
	local hX = math.floor(OBJECTS.floor.bounds[1]/2)
	local hZ = math.floor(OBJECTS.floor.bounds[1]/2)
	
	local rX = hX + hX * math.floor(table.getn(self.customers) / MAX_CUSTOMERS)
	local rZ = hZ + hZ * math.floor(table.getn(self.customers) / MAX_CUSTOMERS)
	
	self.newCusX = math.random(rX) - math.floor(rX / 2)
	self.newCusZ = math.random(rZ) - math.floor(rZ / 2)
	--]]
	self.newCusX = math.random(OBJECTS.floor.bounds[1]) - math.floor(OBJECTS.floor.bounds[1] / 2)
        self.newCusZ = math.random(OBJECTS.floor.bounds[3]) - math.floor(OBJECTS.floor.bounds[3] / 2)
end

function Sim:wakeup_fuzzy()
	

	-- we should have the answer now
	if gameScene:getTestCollisionResult() then
		
		-- generate new position
		self:randCustomerPosition()
		local bounds = OBJECTS[self.newCusType].bounds
		local offset = OBJECTS[self.newCusType].offset
		--print("wakeup_fuzzy(): positive test. New coordinates:",self.newCusX, self.newCusZ)
		gameScene:testCollision(bounds, v_add({self.newCusX, 0, self.newCusZ}, offset))
		self.waiting = true

	else
		-- area clear, proceed and create customer
		-- create customer
		--print("wakeup_fuzzy: AREA CLEAR. Proceeding to create customer")
		newCustomer = gameScene:createElement(self.newCusType, {self.newCusX, 0, self.newCusZ})
			
		-- add some peers to this customer
		-- choose two peers at random
		local peer1 = math.random(table.getn(self.customers) - 1)
		local peer2 = math.random(table.getn(self.customers) - 1)
		newCustomer.o:addPeer(self.customers[peer1].o)
		if peer1 ~= peer2 then
			newCustomer.o:addPeer(self.customers[peer2].o)
		end
			
		self.customersLeft = self.customersLeft - 1
		self.lastCustomer = getTime()
		self.waiting = false
	end
end

function Sim:debug_creation(cusType, x, y, z)
	self.newCusType = cusType
	self.newCusX = x
	self.newCusZ = z
	
	local bounds = OBJECTS[cusType].bounds
	local offset = OBJECTS[cusType].offset
	
	gameScene:testCollision(bounds, {self.newCusX, 0, self.newCusZ})
	
	self.waiting = true
	
end


function Sim:do_fuzzy()
	
	-- create new customer?
	if getTime() - self.lastCustomer > math.floor(MINUTE / self.level) and not self.waiting then
		--print("Creating new customer")
		
		-- now make sure new position is clear
		self.newCusType = math.random(CUSTOMER_COUNT)
		self.newCusType = CUSTOMER_TYPE_MAP[self.newCusType]
		
		-- --generate new location for customer
		self:randCustomerPosition()
		
		-- ask the scene to test the collision
		local bounds = OBJECTS[self.newCusType].bounds
		local offset = OBJECTS[self.newCusType].offset
		gameScene:testCollision(bounds, {self.newCusX, 0, self.newCusZ})
		
		-- take a nap until we got the answer back
		self.waiting = true
		return
	end
end


function Sim:simulate()
	--print("in Sim:simulate()")
	-- clock tick
	self:do_timer()	

	-- if game state did not change since the last call, we have
	-- nothing to do
	if self.stateChanged == false then
		return
	end
	
	print("** simulate **")
	-- reset status of customers, routers, and cables
	for x, cable in self.cables do
		cable.o:resetFlow()
	end
	
	for x, customer in self.customers do
		customer.o:resetFlow()
	end
	
	-- simulate individual traffic for each customer
	for x, customer in self.customers do
		local o = customer.o
		
		-- simulate traffic for contracted customers only
		if o:getState() == STATE.CUSTOMER.contracted then
		
			o.graph = self:constructFlowGraph(o)
			o.graph:Ford_Fulkerson()
			
			-- update current bandwidth
			o:setSuppliedBandwidth(o.graph:getSourceFlow())
			
			-- update peer delivered bandwidth
			for y, peer in o:getPeers() do
				o:setDeliveredBandwidth(y, o.graph:getDeliveredBandwidth(peer))
			end
		end
	end
	
	self.stateChanged = false
	
end
