local ffi = FFI or require("ffi")

Gate = {}

ffi.cdef [[
	struct Net;
	struct Gate {
		int* in_queue;
		int* port_states;
		int** port_net_state;
		int** port_net_state_num;
		int** port_net_in_queue;
		struct Net** port_nets_c;
		int objref;
		int logic_function;
		int data_size;
		int* data;
	};
]]

function Gate.new(objref, definition)
	local gate = {
		-- Logic Critical
		in_queue = ffi.new("int[1]"),
		port_states = ffi.new("int["..(#definition.ports+1).."]"),
		logic = definition.logic,
		ports = {},
		port_nets = {},
		port_net_state     = ffi.new("int*["..(#definition.ports+1).."]"),
		port_net_state_num = ffi.new("int*["..(#definition.ports+1).."]"),
		port_net_in_queue  = ffi.new("int*["..(#definition.ports+1).."]"),
		port_nets_c        = ffi.new("struct Net*["..(#definition.ports+1).."]"),
		
		objref = objref,
		definition = definition,
	}
	gate.in_queue[0] = 0
	
	if definition.data_size_c > 0 then
		gate.data_c = ffi.new("int["..(definition.data_size_c).."]")
	end
	
	gate.c = ffi.new("struct Gate")
	gate.c.in_queue = gate.in_queue
	gate.c.port_states = gate.port_states
	gate.c.port_net_state = gate.port_net_state
	gate.c.port_net_state_num = gate.port_net_state_num
	gate.c.port_net_in_queue = gate.port_net_in_queue
	gate.c.port_nets_c = gate.port_nets_c
	gate.c.objref = gate.objref
	gate.c.logic_function = definition.logic_function_c
	gate.c.data_size = definition.data_size_c
	gate.c.data = gate.data_c
	
	return gate
end

-- Logic Critical
function Gate.getportstate(gate, index)
	return gate.port_net_state[index][0]
end

-- Logic Critical
function Gate.setportstate(gate, index, state)
	if state ~= gate.port_states[index] then
		gate.port_net_state_num[index][0] = gate.port_net_state_num[index][0] - gate.port_states[index] + state
		gate.port_states[index] = state
		
		if ((gate.port_net_state_num[index][0]>0) ~= (gate.port_net_state[index][0]==1)) and (gate.port_net_in_queue[index][0]==0) then
			local cnet = gate.port_nets_c[index]
			Simulation.queuegroup_c(GSim, cnet)
		end
	end
end

function Gate.preinit(gate)
	
end

function Gate.initdata(gate)
	gate.data = {}
end

function Gate.getdata(gate)
	return gate.data
end

function Gate.getportisrising(gate, index)
	return Port.isrising(gate.ports[index])
end

function Gate.getportisfalling(gate, index)
	return Port.isfalling(gate.ports[index])
end

function Gate.cb(gate, ...)
	Simulation.queuecallback(GSim, gate, ...)
end

function Gate.queue(gate, delay)
	Simulation.queuegatelater(GSim, gate, delay)
end

function Gate.gettick(gate)
	return GSim.current_tick[0]
end

function Gate.getdefinition(gate)
	return gate.definition
end

-- Logic functions

function Gate.init(gate)
	Gate.getdefinition(gate).init(gate)
end

function Gate.input(gate, argv)
	Gate.getdefinition(gate).input(gate, argv)
end