-- -------------------------------------------------------------------
-- SimNetwork
-- data.lua
-- Defines data structures of game
-- -------------------------------------------------------------------

SimNetwork_DATA = true

-- constant: Elements
-- This constant describes cables, routers, and types of customers available
-- in the game
-- Cables have 1 property: bandwidth
-- Customers have 2 properties: number of ports (p), maximum bandwidth
-- they can handle (b)
-- Customers have 4 properties: (minB
ELEMENTS = 
{
	cable = 
	{ 
		cable1 =	{b = 1, c = 10},
		cable10 =	{b = 10, c = 70},
		cable100 =	{b = 100, c = 500},
		
		statusBar = {
			spriteGreen = "data/statusBarGreen.png",
			spriteBlue = "data/statusBarBlue.png",
			utilization = 100	-- percentage of utilization
		}, 
	},
		
	router = {
				-- # of ports (p), maximum bandwidth (b), cost (c)
		router2 =	{p = 2, b = 5, c = 0},
		router4 =	{p = 4, b = 30, c = 0},
		router8 =	{p = 8, b = 512, c = 0},
		 
		statusBar = {
			spriteGreen = "data/statusBarGreen.png",
			spriteBlue = "data/statusBarBlue.png",
			utilization = 100	-- percentage of utilization
		},

	},
		
	customer = 
	{ 
		resedential = 
		{
			-- min bandwidth (minB), max bandwidth (maxB)
			-- the actual bandwidth requirement can be
			-- any value between minB and maxB
			houses = {minB = 5, maxB = 99},
			library = {},
			church = {minB = 6, maxB = 10}
		},
		
		commercial = 
		{
			offices = {minB = 100, maxB = 300},
			corporate = {minB = 600, maxB = 2000},

		},
		
		educational = 
		{
			lab = {minB = 1000, maxB = 10000},
			university = {}
		},
		
		industrial = 
		{
			factory = {minB = 1000, maxB = 10000},
		}
	}
		
}

STATE = {
	CUSTOMER = {
		justAppeared = 0,
		unInterested = 1,
		interested = 2,
		contracted = 3,
	},
	
}
		
-- shortcuts (i.e. say 'CABLE.b1' instead of 'Elements.cable.b1')
CABLES		= ELEMENTS.cable
CABLESSTATUSBAR = CABLES.statusBar

ROUTERS		= ELEMENTS.router
ROUTERSSTATUSBAR = ROUTERS.statusBar

CUSTOMERS	= ELEMENTS.customer
INFINITY	= 4294967295

-- class GObject
-- A base class for game objects: cables, routers, and customers
-- -------------------------------------------------------------
GObject = {}	
GObjectMt = {__index = GObject}

function GObject:new()
	newObject = {
		e = nil,	-- electro's entity
		o = nil,	-- game logic instance
	}
	
	setmetatable(newObject, GObjectMt)
	return newObject
end

function GObject:animate()
	if self.o.animate ~= nil then
		self.o:animate()
	end
end

function GObject:timer()
	if self.o.timer ~= nil then
		self.o:timer()
	end
end

-- class: Customer
-- Represents a customer
-- ---------------------
Customer = {}
customerMt = {__index = Customer}
function Customer:new(cusType, cusName, customerE)

	local newCustomer = {
		customerType = cusType,
		name = cusName,
		minBandwidth = nil,
		currentBandwidth = nil,
		initialFee = nil,
		currentFee = nil,
		age = 0,
		satisfaction = 100,
		
		connections = {},		-- adjacency list to directly
						-- connected entities		
		peers = {},			-- adjacency list of pears
		links = {},			-- adjacency list of directly
						-- connected elements (routers or
						-- other customers)
		-- current state of customer
		state = STATE.CUSTOMER.justAppeared,

		e = customerE
		
	}
	
	setmetatable(newCustomer, customerMt)
	return newCustomer
end

function Customer:getE()
	return self.e
end

function Customer:addPeer(anotherCustomer)

	table.insert(self.peers, anotherCustomer)
	table.insert(anotherCustomer.peers, self)
end

function Customer:getLink(someElement)
	for key, value in self.links do
		if value["element"] == someElement then
			return value["cable"]
		end
	end
	return nil
end

function Customer:addConnection(someElement, theCable)
	-- only one connection between any pair of elements can exist
	-- make sure there's previous connection to the same entity
	if self:getLink(someElement) then
		return false
	end
	
	table.insert(self.links, {element = someElement, cable = theCable})
	table.insert(someElement.links, {element = self, cable = theCable})
	
	return true
end

function Customer:getElementType()
	return "customer"
end

function Customer:getSubtype()
	return customerType
end

function Customer:getProperties()
	return CUSTOMERS[customerType]
end

-- class: Router
-- Router's meta table
-- --------------------
Router = {}
routerMt = {__index = Router}
function Router:new(rtrType, routerE)

	local newRouter = {
		routerType = rtrType,
		age = 0,
		currentLoad = nil,		-- number of traffic units (Mbps)
						-- router currently handling
		connections = {},		-- adjacency list of connections
		
		e = routerE,
	}
	
	setmetatable(newRouter, routerMt)
	return newRouter
end

function Router:getE()
	return self.e
end

function Router:getUsedPorts()
	return table.getn(self.connections)
end

function Router:getElementType()
	return "router"
end

function Router:getSubtype()
	return routerType
end

function Router:getProperties()
	return ROUTERS[routerType]
end

function Router:getMaxBandwidth()
	return ROUTERS[routerType].b
end

function Router:getCost()
	return ROUTERS[routerType].c
end

-- class: Cable
-- ------------
Cable = {}
cableMt = {}
cableMt.__index = Cable

function Cable:new(cblType, cableE)

	local newCable = {
		cableType = cblType,
		age = 0,
			
		-- a sequence of points (at least two) defining the
		-- layout of cable in the 2D floor, the first and last of which
		-- defines the end points of the cable
		currentLoad = nil,
		length = nil,
		
		e = cableE,
	}
	
	setmetatable(newCable, cableMt)
	return newCable
end

function Cable:getE()
	return self.e
end

function Cable:getElementType()
	return "cable"
end

function Cable:getSubtype()
	return cableType
end

function Cable:getProperties()
	return CABLES[self.cableType]
end

function Cable:getBandwidth()
	return CABLES[self.cableType].b
end

function Cable:getMileCost()
	return CABLES[self.cableType].c
end

function Cable:getTotalCost()
	return (CABLES[self.cableType].c) * math.floor(self.length)
end

-- class: Connection
-- Represents a connection (possibly through multiple routers) between two 
-- elements (two customers, a router and a customer). Describes the quality
-- of the link
--------------------------------------------------------------------------------
Connection = {}
connectionMt = {}
connectionMt.__index = Connection

function Connection:new(pointA, pointB)

	local newConnection = {
		a = pointA,	
		b = pointB,
		
		latency = INFINITY,
		bottleNeck = INFINITY,
		hops = INFINITY,
	}
	
	setmetatable(newConnection, connectionMt)
	return newConnection
end
