local ffi = FFI or require("ffi")

GateDefinition = {
	ports = {},
	logic = function(gate) end,
	input = function(gate, argv) end
}

function GateDefinition.new(objref, name, description, init, logic, input, global, ports)
	
	name = collapseescape(name)
	init  = collapseescape(init)
	logic = collapseescape(logic)
	input = collapseescape(input)
	global = collapseescape(global)
	description = collapseescape(description)
	
	local def = {
		objref = objref,
		name = name,
		description = description,
		ports = ports or {},
		num_in_ports = 0,
		num_out_ports = 0,
		data_size_c = cDataSizeByName[name:lower()] or 0,
		logic_function_c = cFuncsByName[name:lower()] or 0,
	}
	
	local initfunc = loadstring(tostring(init))
	if initfunc~=nil then
		def.init = initfunc() or function()end
	else
		print("Error loading init func for ".. (name or ""))
		print(init)
	end
	
	local logicfunc = loadstring(tostring(logic))
	if logicfunc ~= nil then
		def.logic = logicfunc() or function()end
	else
		print("Error loading logic function for " .. (name or ""))
		print(logic)
	end
	
	local inputfunc = loadstring(tostring(input))
	if inputfunc ~= nil then
		def.input = inputfunc() or function()end
	else
		print("Error loading input function for " .. (name or ""))
		print(input)
	end
	
	local globalfunc = loadstring(tostring(global))
	if globalfunc~=nil then
		globalfunc()
	else
		print("Error loading global function for ".. (name or ""))
		print(global)
	end
	
	for i = 1, #def.ports do
		local portd = def.ports[i]
		if portd.type==PortTypes.output then
			def.num_out_ports = def.num_out_ports + 1
		elseif portd.type==PortTypes.input then
			def.num_in_ports = def.num_in_ports + 1
		else error("invalid port type: "..name.." port "..i) end
	end
	
	return def
end

function GateDefinition.constructgate(def, objref, position, rotation)
	local gate = Gate.new(objref, def)
	
	for i = 1, #def.ports do
		local portd = def.ports[i]
		local type = portd.type
		local pos = {portd.position[1], portd.position[2], portd.position[3]}
		local dir = portd.direction

		if dir < 4 then
			dir = (dir + rotation) % 4
		end

		local x = pos[1]

		if rotation == 1 then
			pos[1] = pos[2]
			pos[2] = -x
		elseif rotation == 2 then
			pos[1] = -pos[1]
			pos[2] = -pos[2]
		elseif rotation == 3 then
			pos[1] = -pos[2]
			pos[2] = x
		end
		
		local port = Port.new(type, dir, {position[1]+pos[1], position[2]+pos[2], position[3]+pos[3]}, portd.causeupdate, i, gate)
		
		gate.ports[port.idx] = port
		gate.port_nets[port.idx] = nil
		gate.port_states[port.idx] = 0
		
		gate.port_net_state    [port.idx] = ffi.cast("int*", 0)
		gate.port_net_state_num[port.idx] = ffi.cast("int*", 0)
		gate.port_net_in_queue [port.idx] = ffi.cast("int*", 0)
		gate.port_nets_c       [port.idx] = ffi.cast("struct Net*", 0)
	end
	
	return gate
end