module control
	(
		input	wire			clock, reset,

		input	wire			rx_empty, tx_full,
		input	wire	[7:0]	rx_data,

		output	wire			rx_rdreq, tx_wrreq,
		output	wire	[7:0]	tx_data,

		output	wire			bus_wren,
		output	wire	[31:0]	bus_addr,
		output	wire	[15:0]	bus_mosi,

		input	wire	[15:0]	bus_miso,
		input	wire			bus_busy,

		output	wire			led
	);

	reg		[23:0]	led_counter;

	reg 			int_bus_wren;
	reg 	[31:0]	int_bus_addr;
	reg 	[31:0]	int_bus_cntr;
	reg 	[15:0]	int_bus_mosi;

	reg				int_rdreq, int_wrreq;
	reg		[7:0]	int_data;
	reg				int_led;

	reg		[1:0]	byte_counter;
	reg		[4:0]	idle_counter;

	reg		[4:0]	state;

	reg		[31:0]	address, counter;

	reg		[15:0]	prefix;

	wire	[15:0]	dest, data;

	reg		[7:0]	buffer [3:0];

	assign	dest = {buffer[0], buffer[1]};
	assign	data = {buffer[2], buffer[3]};

	always @(posedge clock)
	begin
		if (~rx_empty)
		begin
			int_led <= 1'b0;
			led_counter <= 24'd0;
		end
		else
		begin
			if (&led_counter)
			begin
				int_led <= 1'b1;
			end
			else
			begin
				led_counter <= led_counter + 24'd1;
			end
		end

		case(state)
			0:
			begin
				int_rdreq <= 1'b1;
				int_wrreq <= 1'b0;
				idle_counter <= 5'd0;
				byte_counter <= 2'd0;
				state <= 5'd1;
			end

			1: 
			begin
				// read 4 bytes
				if (~rx_empty)
				begin
					idle_counter <= 5'd0;
					byte_counter <= byte_counter + 2'd1;
					buffer[byte_counter] <= rx_data;
					if (&byte_counter)
					begin
						int_rdreq <= 1'b0;
						state <= 5'd2;
					end
				end
				else if(|byte_counter)
				begin
					idle_counter <= idle_counter + 5'd1;
					if (&idle_counter)
					begin
						int_rdreq <= 1'b0;
						state <= 5'd0;
					end
				end
			end
			
			2: 
			begin
				case (dest)
					16'h0000:
					begin
						// reset
						prefix <= 16'd0;
						state <= 5'd0;
					end


					16'h0001:
					begin
						// prefix register
						prefix <= data;
						state <= 5'd0;
					end


					16'h0002:
					begin
						// address register
						address <= {prefix, data};
						prefix <= 16'd0;
						state <= 5'd0;
					end

					16'h0003:
					begin
						// counter register
						counter <= {prefix, data};
						prefix <= 16'd0;
						state <= 5'd0;
					end

					16'h0004:
					begin
						// single write
						int_bus_addr <= address;
						int_bus_mosi <= data;
						int_bus_wren <= 1'b1;
						prefix <= 16'd0;
						state <= 5'd3;
					end

					16'h0005:
					begin
						// multi read
						int_bus_addr <= address;
						int_bus_cntr <= counter;
						int_bus_wren <= 1'b0;
						prefix <= 16'd0;
						state <= 5'd4;
					end

					default:
					begin
						prefix <= 16'd0;
						state <= 5'd0;
					end
				endcase
			end

			// single write
			3:
			begin
				if (~bus_busy)
				begin
					int_bus_addr <= 32'd0;
					int_bus_mosi <= 16'd0;
					int_bus_wren <= 1'b0;
					state <= 5'd0;
				end
			end

			// multi read
			4:
			begin
				if (bus_busy)
				begin
					buffer[0] <= 8'd1;
					buffer[1] <= 8'd0;
					int_bus_cntr <= 32'd0;
				end
				else
				begin
					buffer[0] <= 8'd0;
					buffer[1] <= 8'd0;
				end
				state <= 5'd7;
			end

			5:
			begin
				buffer[0] <= bus_miso[7:0];
				buffer[1] <= bus_miso[15:8];
				int_bus_addr <= int_bus_addr + 32'd1;
				int_bus_cntr <= int_bus_cntr - 32'd1;
				state <= 5'd6;
			end

			6:
			begin
				state <= 5'd7;
			end

			7:
			begin
				int_data <= buffer[0];
				int_wrreq <= 1'b1;
				state <= 5'd8;
			end

			8:
			begin
				if (~tx_full)
				begin
					int_data <= buffer[1];
					state <= 5'd9;
				end
			end

			9:
			begin
				if (~tx_full)
				begin
					int_wrreq <= 1'b0;
					state <= 5'd10;
				end
			end

			10:
			begin
				if (|int_bus_cntr)
				begin
					state <= 5'd5;
				end
				else
				begin
					state <= 5'd0;
				end
			end

			default:
			begin
				state <= 5'd0;
			end
		endcase
	end

	assign	bus_wren = int_bus_wren;
	assign	bus_addr = int_bus_addr;
	assign	bus_mosi = int_bus_mosi;
	assign	rx_rdreq = int_rdreq & (~rx_empty);
	assign	tx_wrreq = int_wrreq & (~tx_full);
	assign	tx_data = int_data;
	assign	led = int_led;

endmodule
