-- top_modul.vhd

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity top_modul is
    Port ( CLK : in STD_LOGIC;
           RST : in STD_LOGIC;
           START : in STD_LOGIC;
           SW_MODE  : in  STD_LOGIC; -- '0' = HH:MM, '1' = MM:SS
           SW_ALARM_SET  : in  STD_LOGIC; -- '0' = Display Clock, '1' = Set Alarm
           SW_STOP_SET  : in  STD_LOGIC; -- '0' = Display Clock, '1' = Set Stopky
           BTN_INC : in STD_LOGIC;
           -- SW_DIN   : in STD_LOGIC_VECTOR (3 downto 0); -- Value to set
           -- BTN_LOAD : in STD_LOGIC_VECTOR (3 downto 0); -- Which digit to set
           SW_DIGIT_SEL : in STD_LOGIC_VECTOR (3 downto 0); -- '0001'=M_UNITS, '0010'=M_TENS, '0100'=H_UNITS, '1000'=H_TENS
           RST_B : in STD_LOGIC;
           RST_C : in STD_LOGIC;
           SEGMENTS : out STD_LOGIC_VECTOR (7 downto 0);
           ANODS : out STD_LOGIC_VECTOR (3 downto 0);
           ALARM_LED : out STD_LOGIC -- LED lights up when alarm triggers
        ); 
end top_modul;

architecture Behavioral of top_modul is
    component divider is
        Port ( CLK : in STD_LOGIC;
               RST : in STD_LOGIC;
               CLK_1_Hz : out STD_LOGIC); -- Enable pulse
    end component;
    
    component divider_400Hz is
        Port ( CLK : in STD_LOGIC;
               RST : in STD_LOGIC;
               CLK_400_Hz : out STD_LOGIC); -- Enable pulse
    end component;
    
    signal clk_1_Hz   : std_logic;
    signal clk_400_Hz : std_logic;
    signal s_ce_units   : std_logic;
    
    -- Top_modul can carry data between the two submodules thanks to this
    signal sig_s_units, sig_s_tens : std_logic_vector(3 downto 0);
    signal sig_m_units, sig_m_tens : std_logic_vector(3 downto 0);
    signal sig_h_units, sig_h_tens : std_logic_vector(3 downto 0);

    -- Alarm display clock
    signal alrm_s_units, alrm_s_tens : std_logic_vector(3 downto 0);
    signal alrm_m_units, alrm_m_tens : std_logic_vector(3 downto 0);
    signal alrm_h_units, alrm_h_tens : std_logic_vector(3 downto 0);

    -- stopky clock
    signal stop_s_units, stop_s_tens : std_logic_vector(3 downto 0);
    signal stop_m_units, stop_m_tens : std_logic_vector(3 downto 0);
    signal stop_h_units, stop_h_tens : std_logic_vector(3 downto 0);
    -- cap stopky clock
    signal cap_s_units, cap_s_tens : std_logic_vector(3 downto 0);
    signal cap_m_units, cap_m_tens : std_logic_vector(3 downto 0);
    signal cap_h_units, cap_h_tens : std_logic_vector(3 downto 0);
    
    -- Signals to send to the display
    signal d0, d1, d2, d3 : std_logic_vector(3 downto 0);
    signal pulse_to_clock : std_logic;
    signal pulse_to_alarm : std_logic;

    signal btn_inc_pulse : std_logic;

    -- stopky ci behaju
    signal stops_running : std_logic := '0';
    signal stops_reset: std_logic := '0';
    signal sw_stop_prev : std_logic := '0';
    
    -- led
    signal sig_alarm_active : std_logic := '0';
begin

    pulse_to_clock <= clk_1_Hz and BTN_INC and (not SW_ALARM_SET) and (not SW_STOP_SET);
    pulse_to_alarm <= clk_1_Hz and BTN_INC and SW_ALARM_SET;
    
    U_DIV_1HZ : divider
        port map (
            CLK => CLK,
            RST => RST,
            CLK_1_Hz => clk_1_Hz
        );
        
    U_DIV_REFRESH : divider_400Hz
        port map (
            CLK => CLK, 
            RST => RST, 
            CLK_400_Hz => clk_400_Hz
        );

    s_ce_units <= clk_1_Hz and START and not SW_ALARM_SET;
    btn_inc_pulse <= clk_1_Hz and SW_ALARM_SET and BTN_INC;
    -- Clock Engine submodule
    U_CLOCK_CORE : entity work.clock_logic
        port map (
            CLK      => CLK,
            RST      => RST,
            CE_1HZ   => s_ce_units,
            DIGIT_SEL=> SW_DIGIT_SEL,
            BTN_INC  => pulse_to_clock,
            S_UNITS  => sig_s_units,
            S_TENS   => sig_s_tens,
            M_UNITS  => sig_m_units,
            M_TENS   => sig_m_tens,
            H_UNITS  => sig_h_units,
            H_TENS   => sig_h_tens
        );

    -- Clock Engine submodule for alarm
    U_ALARM_CORE : entity work.clock_logic
        port map (
            CLK      => CLK,
            RST      => RST,
            CE_1HZ   => '0',
            DIGIT_SEL=> SW_DIGIT_SEL,
            BTN_INC  => pulse_to_alarm,
            S_UNITS  => alrm_s_units,
            S_TENS   => alrm_s_tens,
            M_UNITS  => alrm_m_units,
            M_TENS   => alrm_m_tens,
            H_UNITS  => alrm_h_units,
            H_TENS   => alrm_h_tens
        );

    stops_running  <= s_ce_units;
    stops_reset <= RST or RST_C;
    -- Clock Engine submodule for stopky
    U_STOPKY_CORE : entity work.clock_logic
        port map (
            CLK      => CLK,
            RST      => stops_reset,
            CE_1HZ   => stops_running,
            DIGIT_SEL   => "0000",
            BTN_INC => '0',
            S_UNITS  => stop_s_units,
            S_TENS   => stop_s_tens,
            M_UNITS  => stop_m_units,
            M_TENS   => stop_m_tens,
            H_UNITS  => stop_h_units,
            H_TENS   => stop_h_tens
        );

    -- Comparator Logic for alarm LED to be ON or OFF
    -- TODO BUG proste niekedy na zaciatku ledka svieti aj ked ma byt zhasnuta
    process(CLK)
    begin
        if rising_edge(CLK) then
            sw_stop_prev <= SW_STOP_SET;
            -- alarm
            if RST = '1' or RST_B = '1' then
                sig_alarm_active <= '0';
            -- Match condition (HH:MM)
            elsif (START = '1' and SW_ALARM_SET = '0' and 
                sig_h_tens = alrm_h_tens and sig_h_units = alrm_h_units and
                sig_m_tens = alrm_m_tens and sig_m_units = alrm_m_units and
                sig_s_tens = "0000" and sig_s_units = "0001") then
                sig_alarm_active <= '1';
            end if;
            if RST = '1' or RST_C = '1' then
                cap_s_units <= "0000";
                cap_s_tens  <= "0000";
                cap_m_units <= "0000";
                cap_m_tens  <= "0000";
                cap_h_units <= "0000";
                cap_h_tens  <= "0000";
            elsif SW_STOP_SET = '1' and sw_stop_prev = '0' then 
                cap_s_units <= stop_s_units;
                cap_s_tens  <= stop_s_tens;
                cap_m_units <= stop_m_units;
                cap_m_tens  <= stop_m_tens;
                cap_h_units <= stop_h_units;
                cap_h_tens  <= stop_h_tens;
            end if;
        end if;
    end process;
    
    U_ALARM_BLINKER : entity work.alarm_led
        port map (
            CLK          => CLK,
            RST          => RST,
            CE_1HZ       => clk_1_Hz,
            ALARM_ACTIVE => sig_alarm_active,
            LED_OUT      => ALARM_LED 
        );

    -- -- Mode Multiplexing
    -- -- If SW_MODE = '1', show MM:SS. If '0', show HH:MM.
    -- d0 <= sig_s_units when SW_MODE = '1' else sig_m_units;
    -- d1 <= sig_s_tens  when SW_MODE = '1' else sig_m_tens;
    -- d2 <= sig_m_units when SW_MODE = '1' else sig_h_units;
    -- d3 <= sig_m_tens  when SW_MODE = '1' else sig_h_tens;

    -- Mode Multiplexing (4 digit display)
    process(SW_ALARM_SET, SW_MODE, 
            sig_s_units, sig_s_tens, sig_m_units, sig_m_tens, sig_h_units, sig_h_tens,
            alrm_m_units, alrm_m_tens, alrm_h_units, alrm_h_tens, stop_s_units, stop_s_tens,
            stop_m_units, stop_m_tens, stop_h_units, stop_h_tens )
    begin
        if SW_ALARM_SET = '1' then
            -- While setting alarm, always show Alarm HH:MM
            d0 <= alrm_m_units;
            d1 <= alrm_m_tens;
            d2 <= alrm_h_units;
            d3 <= alrm_h_tens;
        elsif SW_STOP_SET = '1' then
            if SW_MODE = '1' then
                -- Stopky (MM:SS)
                d0 <= cap_s_units;
                d1 <= cap_s_tens;
                d2 <= cap_m_units;
                d3 <= cap_m_tens;
            else
                -- Stopky (HH:MM)
                d0 <= cap_m_units;
                d1 <= cap_m_tens;
                d2 <= cap_h_units;
                d3 <= cap_h_tens;
            end if;
        else
            -- Normal Operation
            if SW_MODE = '1' then
                -- Show Seconds and Minutes (MM:SS)
                d0 <= sig_s_units;
                d1 <= sig_s_tens;
                d2 <= sig_m_units;
                d3 <= sig_m_tens;
            else
                -- Show Minutes and Hours (HH:MM)
                d0 <= sig_m_units;
                d1 <= sig_m_tens;
                d2 <= sig_h_units;
                d3 <= sig_h_tens;
            end if;
        end if;
    end process;
    
    U_DISPLAY : entity work.display_driver
        port map (
            CLK         => clk_400_Hz,
            RST         => RST,
            DIGIT_0     => d0,
            DIGIT_1     => d1,
            DIGIT_2     => d2,
            DIGIT_3     => d3,
            SEGMENTS    => SEGMENTS,
            ANODES      => ANODS
        );    
end Behavioral;
