#include <assert.h>

struct Net;
struct Gate;

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;
};
struct Net {
	int* state;
	int* state_num;
	int* in_queue;
	int* update_tick;
	int* num_gates_update;
	struct Gate** gates_update_c;
	int id;
};

struct Net** net_queue;
int* num_net_queue;
struct Gate** gate_queue;
int* num_gate_queue;
int* current_tick;
static const int queue_max = 65536;

typedef void(*GateFunc)(struct Gate*);
int sim_gate_get_port(struct Gate* gate, int port);
void sim_gate_set_port(struct Gate* gate, int port, int val);
int sim_gate_get_data(struct Gate* gate, int addr);
void sim_gate_set_data(struct Gate* gate, int addr, int val);
#define GATEFUNCID(name) GateFunc_##name##_F
#define GATEFUNC(name) void GATEFUNCID(name)(struct Gate* gate)
#define setport(i, v) sim_gate_set_port(gate, i, v)
#define getport(i) sim_gate_get_port(gate, i)
#define setdata(i, v) sim_gate_set_data(gate, i, v)
#define getdata(i) sim_gate_get_data(gate, i)

#include "compiled_sim_gates.c"

#undef setport
#undef getport

void sim_set_data(struct Net** net_queue, int* num_net_queue, struct Gate** gate_queue, int* num_gate_queue, int* current_tick, int queue_max);
void sim_update_nets();
void sim_update_gates();

////

void sim_set_data(struct Net** net_queue_in, int* num_net_queue_in, struct Gate** gate_queue_in, int* num_gate_queue_in, int* current_tick_in, int queue_max_in) {
	net_queue = net_queue_in;
	num_net_queue = num_net_queue_in;
	gate_queue = gate_queue_in;
	num_gate_queue = num_gate_queue_in;
	current_tick = current_tick_in;
	//queue_max = queue_max_in;
}

void sim_update_net(struct Net* net);
void sim_update_nets() {
	for(int i=0; i<*num_net_queue; i++) {
		struct Net* net = net_queue[i];
		sim_update_net(net);
		*(net->in_queue) = 0;
		net_queue[i] = 0;
	}
	*num_net_queue = 0;
}

void sim_update_gate(struct Gate* gate);
void sim_dequeue_gate(int i) {
	gate_queue[i] = gate_queue[*num_gate_queue-1];
	gate_queue[*num_gate_queue-1] = 0;
	(*num_gate_queue)--;
}
void sim_update_gates() {
	for(int i=0; i<*num_gate_queue; i++) {
		struct Gate* gate = gate_queue[i];
		if(gate->logic_function != 0) {
			sim_dequeue_gate(i);
			sim_logic_functions[gate->logic_function](gate);
			i--;
		}
	}
}

////

void sim_queue_gate(struct Gate* gate) {
	assert(*num_gate_queue < queue_max - 1);
	gate_queue[(*num_gate_queue)++] = gate;
	*(gate->in_queue) = 1;
}

void sim_queue_net(struct Net* net) {
	assert(*num_net_queue < queue_max - 1);
	net_queue[(*num_net_queue)++] = net;
	*(net->in_queue) = 1;
}

////

void sim_update_net(struct Net* net) {
	int state = *(net->state_num) > 0;
	if(state != *(net->state)) {
		*(net->state) = state;
		*(net->update_tick) = *current_tick;
		
		for(int i=0; i<*(net->num_gates_update); i++) {
			struct Gate* gate = net->gates_update_c[i];
			if(!*(gate->in_queue)) {
				sim_queue_gate(gate);
			}
		}
	}
}

////

int sim_gate_get_port(struct Gate* gate, int port) {
	return *(gate->port_net_state[port]);
}
void sim_gate_set_port(struct Gate* gate, int port, int state) {
	if(state != gate->port_states[port]) {
		*(gate->port_net_state_num[port]) += state - gate->port_states[port];
		gate->port_states[port] = state;
		
		if(
			( (*(gate->port_net_state_num[port]) > 0) != *(gate->port_net_state[port]) ) &&
			( !*(gate->port_net_in_queue[port]) )
		) {
			sim_queue_net(gate->port_nets_c[port]);
		}
	}
}

int sim_gate_get_data(struct Gate* gate, int addr) {
	return gate->data[addr];
}
void sim_gate_set_data(struct Gate* gate, int addr, int val) {
	gate->data[addr] = val;
}