library IEEE;
use IEEE.std_logic_1164.all;
USE ieee.numeric_std.all;
use std.textio.all;
use IEEE.std_logic_textio.all;

entity testbench is

end testbench;

architecture behavioral of testbench is

    -- CLOCK SIGNAL
    constant clkper : time := 10 ns; --Clock period
    signal CLK      : std_logic;

    -- INPUT / OUTPUT SIGNALS
    signal RESET       : std_logic;
    signal DATA_IN     : std_logic_vector (63 downto 0);
    signal DATA_VLD    : std_logic;
    signal LAST_BLOCK  : std_logic;
    signal BYTES_VLD   : std_logic_vector (3 downto 0);
    signal BUFFER_FULL : std_logic;
    signal HASH_OUT    : std_logic_vector (511 downto 0);
    signal HASH_VLD    : std_logic;
 --   signal PER_DONE    : std_logic;

    type st_type is (init, read_input, wait_for_hash, rst, wait_buffer_full, STOP);
    signal st : st_type;
    
    begin
    
    sha3 : entity work.SHA3_BLOCK
    port map(
        CLK         => CLK,  
        RESET       => RESET,  
        DATA_IN     => DATA_IN,  
        DATA_VLD    => DATA_VLD,  
        LAST_BLOCK  => LAST_BLOCK,  
        BYTES_VLD   => BYTES_VLD,  
        BUFFER_FULL => BUFFER_FULL,  
        HASH_OUT    => HASH_OUT,  
        HASH_VLD    => HASH_VLD
 --       PER_DONE    => PER_DONE
    );

    --! generate clock
    clk_gen_p : process
    begin
        CLK <= '1';
        wait for clkper/2;
        CLK <= '0';
        wait for clkper/2;
    end process clk_gen_p;

    --! Simulating SHA3 functionality
    testbench : process(CLK, BUFFER_FULL)

        variable counter   : integer := 0;
        variable num       : integer := 0;
        variable read_set  : std_logic := '0';

        variable data_tmp  : std_logic_vector(63 downto 0);	
        variable bytes_tmp : std_logic_vector(3 downto 0);
        variable hash_tmp  : std_logic_vector(511 downto 0);

        variable text_line : line;

        file filein      : text open read_mode is "./test_msgs/parsed.txt";
        file file_check  : text open read_mode is "./test_msgs/out.txt";
        file fileout     : text open write_mode is "./test_msgs/results.txt";

    begin

        if(rising_edge(CLK)) then
            case st is
                when init =>
                    RESET      <= '1';
                    DATA_IN    <= (others => '0');
                    DATA_VLD      <= '0';
                    LAST_BLOCK <= '0';
                    BYTES_VLD   <= (others => '0');
                    if (counter = 5) then
                        RESET <= '0';
                        counter := 1;
                        st <= read_input;
                    else
                        counter := counter+1;
                        st <= init;
                    end if;

                when read_input =>
                    if (read_set = '0') then
                        readline(filein,text_line);
                        read(text_line, num);
                        readline(filein,text_line);
                        read(text_line, bytes_tmp);
                        read_set := '1';
                    end if;
                    if (BUFFER_FULL = '1') then
                        DATA_IN <= (others => '0');
                        BYTES_VLD <= (others => '0');
                        DATA_VLD <= '0';
                        st <= wait_buffer_full;
                    else
                        DATA_VLD <= '1';
                        if ( counter = num ) then
                            readline(filein,text_line);
                            hread(text_line, data_tmp);
                            DATA_IN <= data_tmp;
                            BYTES_VLD <= bytes_tmp;
                            LAST_BLOCK <= '1';
                            counter := 1;
                            st <= wait_for_hash;
                        else
                            counter := counter+1;
                            readline(filein,text_line);
                            hread(text_line, data_tmp);
                            DATA_IN <= data_tmp;
                            BYTES_VLD <= "1000";
                            st <= read_input;
                        end if;
                    end if;
                
                when wait_buffer_full =>
                    if (BUFFER_FULL = '1') then
                        st <= wait_buffer_full;
                    else
                        st <= read_input;
                    end if;

                when  wait_for_hash =>
                    DATA_IN <= (others => '0');
                    BYTES_VLD <= (others => '0');
                    DATA_VLD <= '0';
                    LAST_BLOCK <= '0';
                    if(HASH_VLD = '1') then
                        hwrite(text_line, HASH_OUT);
                        writeline(fileout, text_line);
                        readline(file_check, text_line);
                        hread(text_line, hash_tmp);
                        if(hash_tmp  /= HASH_OUT) then
                            assert false report "Hash is not valid!" severity error;
                        else
                            report "Hash is valid!" severity note;
                        end if;
                        RESET <= '1';
                        st <= rst;
                    else
                        st <= wait_for_hash;
                    end if;
    
                when rst =>
                    read_set := '0';
                    RESET <= '0';
                    readline(filein,text_line);
                    if(text_line(1) = '.') then
                        report "Simulation completed" severity note;
                        st <= STOP;
                    elsif(text_line(1) = '-') then
                        st <= read_input;
                    else
                        report "Something went very wrong! Expected - or ." severity failure;
                        st <= STOP;
                    end if;

                when STOP =>
                    FILE_CLOSE(filein);
                    FILE_CLOSE(fileout);
                    FILE_CLOSE(file_check);
                    null;

            end case;
        end if;

    end process testbench;

end architecture;
