`define VGA_W 160
`define VGA_H 120

module circle(clk, rst_n, colour, centre_x, centre_y, radius, start, done,
	vga_x, vga_y, vga_colour, vga_plot);

	input logic clk, rst_n, start;
	input logic [2:0] colour;
	input logic [6:0] centre_y;
	input logic [7:0] centre_x, radius;

	output logic done, vga_plot;
	output logic [2:0] vga_colour;
	output logic [6:0] vga_y;
	output logic [7:0] vga_x;

	logic clear, ready;
	logic [2:0] octant;
	logic [7:0] offset_x, offset_y, offset_x_next, offset_y_next;
	/* One bit larger since these are signed. */
	logic signed [7:0] vga_y_next;
	logic signed [8:0] vga_x_next, crit, crit_next;

	assign vga_colour = clear ? 3'b000 : colour;

	always_comb case (octant)
		3'd0: begin
			vga_x_next = centre_x + offset_x;
			vga_y_next = centre_y + offset_y;
		end
		3'd1: begin
			vga_x_next = centre_x + offset_y;
			vga_y_next = centre_y + offset_x;
		end
		3'd2: begin
			vga_x_next = centre_x - offset_y;
			vga_y_next = centre_y + offset_x;
		end
		3'd3: begin
			vga_x_next = centre_x - offset_x;
			vga_y_next = centre_y + offset_y;
		end
		3'd4: begin
			vga_x_next = centre_x - offset_x;
			vga_y_next = centre_y - offset_y;
		end
		3'd5: begin
			vga_x_next = centre_x - offset_y;
			vga_y_next = centre_y - offset_x;
		end
		3'd6: begin
			vga_x_next = centre_x + offset_y;
			vga_y_next = centre_y - offset_x;
		end
		3'd7: begin
			vga_x_next = centre_x + offset_x;
			vga_y_next = centre_y - offset_y;
		end
	endcase

	always_comb begin
		offset_x_next = offset_x;
		offset_y_next = offset_y + 1;

		if (crit <= 0)
			crit_next = crit + 2 * offset_y_next + 1;
		else begin
			offset_x_next = offset_x - 1;
			crit_next = crit
				+ 2 * (offset_y_next - offset_x_next) + 1;
		end
	end

	always_ff @(posedge clk) begin
		if (~rst_n) begin
			done <= 1'b0;
			ready <= 1'b0;

			/* Start clearing the screen. */
			clear <= 1'b1;
			vga_x <= 8'b0;
			vga_y <= 7'b0;
			vga_plot <= 1'b1;
		end

		/* Clear the screen. */
		if (clear) begin
			if (vga_y < 120) begin
				/* Check for one column less since it takes
				 * one clock cycle to reset and increment. */
				if (vga_x < 159)
					vga_x <= vga_x + 1;
				else begin
					vga_x <= 8'b0;
					vga_y <= vga_y + 1;
				end
			end else begin
				clear <= 1'b0;
				ready <= 1'b1;
				vga_x <= 8'b0;
				vga_y <= 7'b0;
				vga_plot <= 1'b0;
			end
		/* Initialise the registers for the circle algorithm. */
		end else if (ready && start && ~done) begin
			ready <= 1'b0;
			octant <= 3'b0;
			offset_y <= 8'b0;
			offset_x <= radius;
			crit <= 1 - radius;
		/* Draw the circle using the Bresenham circle algorithm. */
		end else if (start && ~done) begin
			if (offset_y <= offset_x) begin
				if ((vga_x_next >= 0) && (vga_x_next <= `VGA_W))
					vga_x <= vga_x_next;
				if ((vga_y_next >= 0) && (vga_y_next <= `VGA_H))
					vga_y <= vga_y_next;

				/* Plot only within the monitor's geometry. */
				vga_plot <= ((vga_x_next >= 0)
					&& (vga_x_next <= `VGA_W)
					&& (vga_y_next >= 0)
					&& (vga_y_next <= `VGA_H));

				octant <= octant + 1;

				/* The last octant. */
				if (octant == 7) begin
					offset_x <= offset_x_next;
					offset_y <= offset_y_next;
					crit <= crit_next;
				end
			/* Finished. */
			end else begin
				done <= 1'b1;
				ready <= 1'b1;
				vga_plot <= 1'b0;
			end
		/* Wait for start to be deasserted. */
		end else if (~start && done)
			done <= 1'b0;
	end
endmodule
