
local tonumber, assert, type, error = tonumber, assert, type, error
local string = string
local Core = require "LuaConfigurationControl.Core"

module((...))




---
-- Checks that table 'table' has a field called 'name' of type 'expectedType'
-- If it is missing, returns 'optionalValue'
--
local function CheckField(table, name, expectedType, optionalValue)
	local value = table[name]
	-- is nil but it is required
	if not value then
		if not optionalValue then
			error( ("field '%s' is not optional"):format(name) )
		else
			return optionalValue
		end
	end
	if type(value) ~= expectedType then
		error( ("field '%s' must be a %s"):format(name, expectedType) )
	end
	return value
end

---
-- Dumb-ass validation of an ipv4 address
local function CheckIpAddress (address)
	assert(type(address) == "string", "address must be a string")
	
	local a, b, c, d = address:match("(%d+)%.(%d+)%.(%d+)%.(%d+)")
	a, b, c, d = tonumber(a), tonumber(b), tonumber(c), tonumber(d)
	assert(a and b and c and d, "must be a valid IPv4 address")
	assert( 0 <= a and a <= 255 and 0 <= b and b <= 255 and 0 <= c and c <= 255 and 0 <= d and d <= 255, 
		"must be a valid IPv4 address")
	
	return address
end

local function CheckNumRange (value, min, max)
	assert(type(value) == "number", "value must be a number")
	assert(min <= value and value <= max, "value is oout of range")
	
	return value
end


---
-- Constructor: Client( arg )
-- Returns a new configuration control client.
-- @param arg is a table containing the following:
--   @field node_type the type of node (i.e. CHATSERVER, IMPROXY, etc) (mandatory)
--   @field node_address the ip address of the node (mandatory)
--   @field node_port the port the node is listening on (mandatory)
--   @field node_name the name given to this node (optional)
--
--   If args has both fields address and port, then it will fetch configuration from the Configuration Server:
--       @field address the ip address of the configuration server (mandatory)
--       @field port the port of the configuration server (mandatory)
--
--   Else, if args has a file field, it will fetch configuration from a file:
--       @field input path to the file with configuration (mandatory)
--       @field output path to the output file (optional)
--
--  @return an instance of LuaConfigurationControl.Client
-- 
function Client (arg)

	local node_type = CheckField(arg, "type", "string")
	local node_address = CheckIpAddress( CheckField(arg, "node_address", "string") )
	local node_port = CheckNumRange( CheckField(arg, "node_port", "number"), 0, 65535)
	local node_name = CheckField(arg, "node_name", "string", "")
	
	if arg.address and arg.port then	-- http
		local address = CheckIpAddress( CheckField(arg, "address", "string") )
		local port = CheckNumRange( CheckField(arg, "port", "number"), 0, 65535 )
		
		local cli = Core.Client(node_type, address, port, "http", node_address, node_port, node_name)
		return cli
	
	elseif arg.file then	-- file
		local input_file = CheckField(arg, "file", "string")
		local output_file = arg.output or "output.cfg"
		
		local cli = Core.Client(node_type, input_file, output_file, "file", node_address, node_port, node_name)
		return cli
	
	else
		error("'source' must be either 'http' or 'file'")
	end
end
