-- -------------------------------------------------------------------
-- 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 = 1},
		cable10 =	{b = 10, c = 2},
		cable100 =	{b = 100, c = 3}, 
	},
		
	router = {
				-- # of ports (p), maximum bandwidth (b), cost (c)
		router4 =	{p = 4, b = 30, c = 10},
		router6 =	{p = 6, b = 100 , c = 70},
		router8 =	{p = 8, b = 512, c = 200},
	},
		
	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

ROUTERS		= ELEMENTS.router

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:getName()
	return self.name
end

function Customer:getLinks()
	return self.links
end

function Customer:getPeers()
	return self.peers
end

function Customer:getPeer(index)
	return self.peers[index]
end

function Customer:getPeerCount()
	return table.getn(self.peers)
end

function Customer:getCurrentBandwidth()
	return self.currentBandwidth
end

function Customer:getPeerConnection(index)
	
end

function Customer:getLink()
	if table.getn(self.links) > 0 then
		return self.links[1]
	else
		return nil
	end
end

function Customer:asymAddConnection(someElement, theCable)
	if self:getLink() ~= nil then
		-- this element is already linked
		return "link_max"
	end
	table.insert(self.links, {destination = someElement, cable = theCable})
	return "success"
end

function Customer:addConnection(someElement, theCable)
	
	-- only one connection per customer
	if not self:canAddNewConnection() then
		return "link_max"
	end
	
	local res = someElement:asymAddConnection(self, theCable)
	if res ~= "success" then
		return res
	end
	table.insert(self.links, {destination = someElement, cable = theCable})
	return "success"
end

function Customer:getElementType()
	return "customer"
end

function Customer:getSubtype()
	return self.customerType
end

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

function Customer:canAddNewConnection()
	if table.getn(self.links) > 0 then
		return false
	else
		return true
	end
end

function Customer:canDemolish()
	return false
end

function Customer:removeCable(theCable)
	if table.getn(self.links) == 1 then
		table.remove(self.links)
	else
		-- we shouldn't be here
		E.print_console(" ** WARNING: trying to remove a link from customer that has no cables!N")
	end
end

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

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

function Router:getE()
	return self.e
end

function Router:getName()
	return self.name
end

function Router:asymAddConnection(someElement, theCable)
	if self:getUsedPorts() == self:getPortCount() then
		-- will exceed number of available ports
		return "link_max"
	elseif self:getLink(someElement) ~= nil then
		-- this element is already linked
		return "already_linked"
	end
	table.insert(self.links, {destination = someElement, cable = theCable})
	return "success"
end

function Router:addConnection(someElement, theCable)

	if not self:canAddNewConnection() then
		-- will exceed number of available ports
		return "link_max"
	elseif self:getLink(someElement) ~= nil then
		-- this element is already linked
		return "already_linked"
	end
	
	local res = someElement:asymAddConnection(self, theCable)
	if res ~= "success" then
		return res
	end
	table.insert(self.links, {destination = someElement, cable = theCable})
	
	return "sucess"
end

function Router:getLinks()
	return self.links
end

function Router:getLink(someElement)
	for x, link in self:getLinks() do
		if link.destination == someElement then
			return link
		end
	end
	return nil
end

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

function Router:getElementType()
	return "router"
end

function Router:getSubtype()
	return self.routerType
end

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

function Router:getPortCount()
	return ROUTERS[self.routerType].p
end

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

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

function Router:canAddNewConnection()
	if self:getUsedPorts() == self:getPortCount() then
		return false
	else
		return true
	end
end

function Router:canDemolish()
	if self:getUsedPorts() > 0 then
		return false
	else
		return true
	end
end

function Router:removeCable(cable)
	local index = searchTable(self.links, "cable", cable)
	table.remove(self.links, index)
end

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

function Cable:new(cblType, cableLength, cableE)

	local newCable = {
		cableType = cblType,	-- string; "cable1", "cable10", etc...
		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 = 0,
		length = cableLength,
		
		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:getCost()
	return (CABLES[self.cableType].c) * self.length
end

function Cable:setSegments(segments)
	self.e = segments
end

function Cable:setLength(len)
	self.length = len
end

function Cable:canDemolish()
	return true
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

Demolish = {}
DemolishMt = {__index = Demolish}
setmetatable(Demolish, DemolishMt)

function Demolish:getCost()
	return 10
end
