library ieee;
    use ieee.std_logic_1164.all;
    use ieee.numeric_std.all;

library work;
    use work.reg32.all;
    use work.float.all;
    use work.task.all;

entity sine is
    port (
        clk : in std_logic;
        reset : in std_logic;

        task_start : in std_logic;
        task_state : out work.task.State;

        step_size : in work.reg32.word;
        phase : in work.reg32.word;
        amplitude : in work.reg32.word;

        signal_write : out std_logic;
        signal_writedata : out std_logic_vector( 31 downto 0 )
    );
end entity sine;

architecture rtl of sine is

    signal current_task_state : work.task.State;
    signal next_task_state : work.task.State;
    signal index : integer range 0 to work.task.STREAM_LEN;
	signal angle_calc : signed(31 downto 0);
	signal angle_busy : std_logic;
	signal angle_valid : std_logic;
	signal angle_result : signed(31 downto 0);
	signal START : std_logic;
	signal angle_amplitude : reg32.word;

	type angle_matrix is ARRAY(natural range<>) of signed;
	signal angle_lut : angle_matrix (31 downto 0) (31 downto 0);
	angle_lut(0) <= "0";
	angle_lut(1) <= "138547331";
	angle_lut(2) <= "277094662";
	angle_lut(3) <= "415641993";
	angle_lut(4) <= "554189324";
	angle_lut(5) <= "692736655";
	angle_lut(6) <= "831283986";
	angle_lut(7) <= "969831317";
	angle_lut(8) <= "1108378649";
	angle_lut(9) <= "1246925980";
	angle_lut(10) <= "1385473311";
	angle_lut(11) <= "1524020642";
	angle_lut(12) <= "1662567973";
	angle_lut(13) <= "1801115304";
	angle_lut(14) <= "1939662635";
	angle_lut(15) <= "2078209966";
	angle_lut(16) <= "2216757298";
	angle_lut(17) <= "2355304629";
	angle_lut(18) <= "2493851960";
	angle_lut(19) <= "2632399291";
	angle_lut(20) <= "2770946622";
	angle_lut(21) <= "2909493953";
	angle_lut(22) <= "3048041284";
	angle_lut(23) <= "3186588615";
	angle_lut(24) <= "3325135947";
	angle_lut(25) <= "3463683278";
	angle_lut(26) <= "3602230609";
	angle_lut(27) <= "3740777940";
	angle_lut(28) <= "3879325271";
	angle_lut(29) <= "4017872602";
	angle_lut(30) <= "4156419933";
	angle_lut(31) <= "4294967265";

    type AngleState is (
        ANGLE_IDLE,
        ANGLE_SET_SIGNALS,
		ANGLE_RUNNING,
        ANGLE_DONE
    );
	signal current_angle_state : AngleState;
	signal next_angle_state : AngleState;


begin

	f1:ENTITY work.float_sine PORT MAP(CLK => CLK, RESET => RESET, data_valid => START, angle =>angle_calc, busy => angle_busy, result_valid => angle_valid, sine => angle_result);

    task_state_transitions : process ( current_task_state, task_start, index ) is
    begin
        next_task_state <= current_task_state;
        case current_task_state is
            when work.task.TASK_IDLE =>
                if ( task_start = '1' ) then
                    next_task_state <= work.task.TASK_RUNNING;
                end if;
            when work.task.TASK_RUNNING =>
                if ( index = work.task.STREAM_LEN - 1 ) then
                    next_task_state <= work.task.TASK_DONE;
                end if;
            when work.task.TASK_DONE =>
                if ( task_start = '1' ) then
                    next_task_state <= work.task.TASK_RUNNING;
                end if;
        end case;
    end process task_state_transitions;

 	sync : process ( clk, reset ) is
    begin
        if ( reset = '1' ) then
            current_task_state <= work.task.TASK_IDLE;
            index <= 0;
        elsif ( rising_edge( clk ) ) then
            current_task_state <= next_task_state;
            case next_task_state is
            when work.task.TASK_IDLE =>
                index <= 0;
            when work.task.TASK_RUNNING =>
				if(current_angle_state = ANGLE_DONE) then
		   			index <= index + 1;
				end if;
            when work.task.TASK_DONE =>
                index <= 0;
            end case;
        end if;
    end process sync;
    end process sync;

    task_state <= current_task_state;


	angle_state_transitions : process ( current_angle_state, angle_valid, current_angle_state)
	begin
		next_angle_state <= current_angle_state;
		case current_angle_state is
		when ANGLE_IDLE =>
			if(current_angle_state = work.task.TASK_RUNNING) then
				next_angle_state <= ANGLE_SET_SIGNALS;
			end if;
		when ANGLE_SET_SIGNALS =>
			next_angle_state <= ANGLE_RUNNING;
		when ANGLE_RUNNING =>
			if(angle_valid = '1') then
				next_angle_state <= ANGLE_DONE;
			end if;
		when ANGLE_DONE =>
			next_angle_state <= angle_IDLE;
		end case;
	end process angle_state_transitions;

    angle_calc : process (clk, reset) is
	begin
		if ( reset = '1' ) then
			current_angle_state <= ANGLE_IDLE;
		elsif ( rising_edge( clk ) ) then
			current_ANGLE_state <= next_angle_state;
			case next_angle_state is
			when ANGLE_IDLE =>
				START <= '0';
				signal_write <= '0';
			when ANGLE_SET_SIGNALS =>
				angle = i
			when ANGLE_RUNNING =>
				
				START <= '1';
			when ANGLE_DONE =>
				START <= '0';
				signal_write <= '1';
			end case;
		end if;
	end process add;
	

end architecture rtl;
