module crack #(
	parameter logic [23:0] KeyStart = '0,
	parameter logic [23:0] KeyIncrement = 24'd1
) (
	input logic clk,
	input logic rst_n,
	input logic en,
	output logic rdy,
	output logic [23:0] key,
	output logic key_valid,
	output logic [7:0] ct_addr,
	input logic [7:0] ct_rddata,
	input logic [7:0] pt_addr_ext,
	output logic [7:0] pt_rddata_ext
);

	logic is_printable;
	logic [23:0] key_d, key_q;

	logic a4_rst_n, a4_en, a4_rdy;

	logic pt_wren;
	logic [7:0] pt_addr, pt_addr_mem;
	logic [7:0] pt_wrdata, pt_rddata;

	enum logic [2:0] {
		INIT,
		ARC4_INIT,
		ARC4_RDY,
		ARC4,
		NOT_FOUND,
		FOUND
	} state_d, state_q;

	pt_core_mem pt (
		.address(pt_addr_mem),
		.clock(clk),
		.data(pt_wrdata),
		.wren(pt_wren),
		.q(pt_rddata)
	);

	arc4 a4 (
		.clk,
		.rst_n(rst_n & a4_rst_n),
		.en(a4_en),
		.rdy(a4_rdy),
		.key,
		.ct_addr,
		.ct_rddata,
		.pt_addr,
		.pt_rddata,
		.pt_wrdata,
		.pt_wren
	);

	assign key = key_q;
	assign pt_rddata_ext = pt_rddata;
	assign pt_addr_mem = ((state_q == FOUND) || (state_q == NOT_FOUND)) ? pt_addr_ext : pt_addr;

	assign is_printable = (pt_wrdata >= 8'h20) && (pt_wrdata <= 8'h7E);

	always_ff @(posedge clk) begin
		if (~rst_n) begin
			state_q <= INIT;
			key_q <= KeyStart;
		end else begin
			state_q <= state_d;
			key_q <= key_d;
		end
	end

	always_comb unique case (state_q)
		INIT:
			if (en) state_d = ARC4_INIT;
			else state_d = INIT;
		ARC4_INIT: state_d = ARC4_RDY;
		ARC4_RDY: begin
			if (a4_rdy) state_d = ARC4;
			else state_d = ARC4_RDY;
		end
		ARC4: begin
			state_d = ARC4;
			if (a4_rdy) state_d = FOUND;
			else if (pt_wren && (pt_addr != '0)) begin
				if (~is_printable) begin
					if (key_q == '1) state_d = NOT_FOUND;
					else state_d = ARC4_INIT;
				end
			end
		end
		NOT_FOUND: state_d = NOT_FOUND;
		FOUND: state_d = FOUND;
		default: begin end
	endcase

	always_comb begin
		rdy = '0;
		key_valid = '0;

		a4_rst_n = '1;
		a4_en = '0;

		key_d = key_q;

		unique case (state_q)
			INIT: rdy = '1;
			ARC4_INIT: a4_rst_n = '0;
			ARC4_RDY: a4_en = a4_rdy;
			ARC4: begin
				if (~a4_rdy && pt_wren && (pt_addr != '0)) begin
					if (~is_printable) begin
						key_d = key_q + KeyIncrement;
					end
				end
			end
			NOT_FOUND: rdy = '1;
			FOUND: begin
				rdy = '1;
				key_valid = '1;
			end
			default: begin end
		endcase
	end

endmodule: crack
