## -*- texinfo -*-
## @deftypefn  {Function File} dsppack_launch ()
## Launches the Filter Design and Analysis Tool GUI.
## @end deftypefn

function dsppack_launch()
    % Check dependencies
    if isempty(pkg("list", "signal"))
        error("The 'signal' package is not loaded. Run 'pkg load signal' first.");
    end

    % 1. Create the Main Window
    h.fig = dsp_new_window("FDA Tool - Diploma Project");

    % 2. Left Panel (Inputs)
    h.panel = uipanel("Title", "Filter Specifications", ...
                      "FontSize", 11, ...
                      "Position", [0.02, 0.08, 0.28, 0.90]);

    % -- Preset Selector --
    uicontrol(h.panel, "Style", "text", "String", "Preset:", ...
              "Units", "normalized", "Position", [0.05, 0.95, 0.28, 0.03], ...
              "HorizontalAlignment", "left", "FontWeight", "bold");
    h.popup_Preset = uicontrol(h.panel, "Style", "popupmenu", ...
                               "String", {"(Custom)", ...
                                          "Anti-aliasing (CD Audio)", ...
                                          "Voice Band-pass", ...
                                          "Bass Isolation", ...
                                          "Treble Isolation", ...
                                          "Sub-bass Crossover", ...
                                          "50 Hz Mains Notch", ...
                                          "60 Hz Mains Notch", ...
                                          "DC Blocker", ...
                                          "Wideband Noise Reduction", ...
                                          "FIR Anti-aliasing (CD Audio)", ...
                                          "FIR Voice Band-pass", ...
                                          "FIR Noise Suppression"}, ...
                               "Units", "normalized", ...
                               "Position", [0.05, 0.91, 0.9, 0.04], ...
                               "Callback", @cb_preset_wrapper);

    % -- Sampling Freq (Fs) --
    uicontrol(h.panel, "Style", "text", "String", "Sampling Freq (Fs):", ...
              "Units", "normalized", "Position", [0.05, 0.86, 0.50, 0.04], ...
              "HorizontalAlignment", "left");
    h.edit_Fs = uicontrol(h.panel, "Style", "edit", "String", "44100", ...
                          "Units", "normalized", "Position", [0.58, 0.86, 0.37, 0.04]);

    % -- Cutoff Freq 1 (Fc) --
    uicontrol(h.panel, "Style", "text", "String", "Cutoff Freq (Fc):", ...
              "Units", "normalized", "Position", [0.05, 0.81, 0.50, 0.04], ...
              "HorizontalAlignment", "left");
    h.edit_Fc = uicontrol(h.panel, "Style", "edit", "String", "1000", ...
                          "Units", "normalized", "Position", [0.58, 0.81, 0.37, 0.04]);

    % -- Cutoff Freq 2 (Fc2) -- used for bandpass / bandstop
    uicontrol(h.panel, "Style", "text", "String", "Cutoff Freq 2 (Fc2):", ...
              "Units", "normalized", "Position", [0.05, 0.76, 0.50, 0.04], ...
              "HorizontalAlignment", "left");
    h.edit_Fc2 = uicontrol(h.panel, "Style", "edit", "String", "4000", ...
                           "Units", "normalized", "Position", [0.58, 0.76, 0.37, 0.04], ...
                           "TooltipString", "Only used for Band Pass / Band Stop filters");

    % -- Filter Order (N) --
    uicontrol(h.panel, "Style", "text", "String", "Filter Order (N):", ...
              "Units", "normalized", "Position", [0.05, 0.71, 0.50, 0.04], ...
              "HorizontalAlignment", "left");
    h.edit_Order = uicontrol(h.panel, "Style", "edit", "String", "4", ...
                             "Units", "normalized", "Position", [0.58, 0.71, 0.37, 0.04]);

    % -- Architecture Dropdown (IIR / FIR) --
    uicontrol(h.panel, "Style", "text", "String", "Architecture:", ...
              "Units", "normalized", "Position", [0.05, 0.66, 0.50, 0.03], ...
              "HorizontalAlignment", "left");
    h.popup_Arch = uicontrol(h.panel, "Style", "popupmenu", ...
                             "String", {"IIR", "FIR"}, ...
                             "Units", "normalized", "Position", [0.05, 0.62, 0.9, 0.04], ...
                             "Callback", @cb_arch_wrapper);

    % -- Filter Type Dropdown --
    uicontrol(h.panel, "Style", "text", "String", "Filter Type:", ...
              "Units", "normalized", "Position", [0.05, 0.57, 0.50, 0.03], ...
              "HorizontalAlignment", "left");
    h.popup_Type = uicontrol(h.panel, "Style", "popupmenu", ...
                             "String", {"Low Pass", "High Pass", "Band Pass", "Band Stop"}, ...
                             "Units", "normalized", "Position", [0.05, 0.53, 0.9, 0.04]);

    % -- Topology / Method Dropdown --
    uicontrol(h.panel, "Style", "text", "String", "Topology / Method:", ...
              "Units", "normalized", "Position", [0.05, 0.48, 0.90, 0.03], ...
              "HorizontalAlignment", "left");
    h.popup_Topo = uicontrol(h.panel, "Style", "popupmenu", ...
                             "String", {"Butterworth", "Chebyshev I", "Chebyshev II", "Elliptic"}, ...
                             "Units", "normalized", "Position", [0.05, 0.44, 0.9, 0.04]);

    % -- DESIGN Button --
    h.btn_Design = uicontrol(h.panel, "Style", "pushbutton", "String", "DESIGN FILTER", ...
                             "Units", "normalized", "Position", [0.05, 0.03, 0.9, 0.07], ...
                             "FontSize", 10, "FontWeight", "bold", ...
                             "Callback", @cb_design_wrapper);

    % -- EXPORT Buttons (side by side) --
    h.btn_Export = uicontrol(h.panel, "Style", "pushbutton", ...
                             "String", "Export to Workspace", ...
                             "Units", "normalized", ...
                             "Position", [0.05, 0.12, 0.43, 0.06], ...
                             "Callback", @cb_export_wrapper);
    h.btn_CodeExport = uicontrol(h.panel, "Style", "pushbutton", ...
                                  "String", "Export Code", ...
                                  "Units", "normalized", ...
                                  "Position", [0.52, 0.12, 0.43, 0.06], ...
                                  "Callback", @cb_code_export_wrapper);

    % -- SAVE / LOAD Buttons (side by side) --
    h.btn_Save = uicontrol(h.panel, "Style", "pushbutton", ...
                            "String", "Save Design", ...
                            "Units", "normalized", ...
                            "Position", [0.05, 0.21, 0.43, 0.06], ...
                            "Callback", @cb_save_wrapper);
    h.btn_Load = uicontrol(h.panel, "Style", "pushbutton", ...
                            "String", "Load Design", ...
                            "Units", "normalized", ...
                            "Position", [0.52, 0.21, 0.43, 0.06], ...
                            "Callback", @cb_load_wrapper);

    % -- DETAILED ANALYSIS Button --
    h.btn_Analyze = uicontrol(h.panel, "Style", "pushbutton", ...
                              "String", "DETAILED ANALYSIS >", ...
                              "Units", "normalized", ...
                              "Position", [0.05, 0.30, 0.9, 0.06], ...
                              "Callback", @cb_analyze_wrapper);

    % -- INTERACTIVE P/Z EDITOR Button --
    h.btn_PZEditor = uicontrol(h.panel, "Style", "pushbutton", ...
                               "String", "Interactive P/Z Editor", ...
                               "Units", "normalized", ...
                               "Position", [0.05, 0.39, 0.9, 0.06], ...
                               "FontWeight", "bold", ...
                               "BackgroundColor", [0.18, 0.42, 0.60], ...
                               "ForegroundColor", [1, 1, 1], ...
                               "Callback", @cb_pz_editor_wrapper);

    % -- COMPARE Button --
    h.btn_Compare = uicontrol(h.fig, "Style", "pushbutton", ...
                              "String", "COMPARE FILTERS", ...
                              "Units", "normalized", ...
                              "Position", [0.34, 0.02, 0.15, 0.05], ...
                              "FontSize", 10, "FontWeight", "bold", ...
                              "ForegroundColor", [0.15, 0.15, 0.15], ...
                              "Callback", @cb_compare_wrapper);

    % -- REPORT Button --
    h.btn_Report = uicontrol(h.fig, "Style", "pushbutton", ...
                             "String", "GENERATE REPORT", ...
                             "Units", "normalized", ...
                             "Position", [0.51, 0.02, 0.15, 0.05], ...
                             "FontSize", 10, "FontWeight", "bold", ...
                             "ForegroundColor", [0.15, 0.15, 0.15], ...
                             "Callback", @cb_report_wrapper);

    % -- AUDIO DEMO Button --
    h.btn_AudioDemo = uicontrol(h.fig, "Style", "pushbutton", ...
                                "String", "AUDIO DEMO", ...
                                "Units", "normalized", ...
                                "Position", [0.68, 0.02, 0.15, 0.05], ...
                                "FontSize", 10, "FontWeight", "bold", ...
                                "ForegroundColor", [0.15, 0.15, 0.15], ...
                                "Callback", @cb_audio_demo_wrapper);

    % -- EXIT Button --
    h.btn_Exit = uicontrol(h.fig, "Style", "pushbutton", ...
                           "String", "EXIT", ...
                           "Units", "normalized", ...
                           "Position", [0.86, 0.02, 0.12, 0.05], ...
                           "FontSize", 10, "FontWeight", "bold", ...
                           "ForegroundColor", [0.8, 0, 0], ...
                           "Callback", @cb_exit_wrapper);

    % 3. Specification Mask controls (above magnitude plot)
    h.chk_mask = uicontrol(h.fig, "Style", "checkbox", ...
                           "String", "Show Spec Mask", ...
                           "Units", "normalized", ...
                           "Position", [0.38, 0.95, 0.12, 0.03], ...
                           "Value", 0);

    uicontrol(h.fig, "Style", "text", "String", "Rp (dB):", ...
              "Units", "normalized", "Position", [0.51, 0.95, 0.06, 0.03], ...
              "HorizontalAlignment", "left");
    h.edit_Rp = uicontrol(h.fig, "Style", "edit", "String", "1", ...
                           "Units", "normalized", "Position", [0.57, 0.95, 0.05, 0.03]);

    uicontrol(h.fig, "Style", "text", "String", "Rs (dB):", ...
              "Units", "normalized", "Position", [0.63, 0.95, 0.06, 0.03], ...
              "HorizontalAlignment", "left");
    h.edit_Rs = uicontrol(h.fig, "Style", "edit", "String", "40", ...
                           "Units", "normalized", "Position", [0.69, 0.95, 0.05, 0.03]);

    % Right Panel: Magnitude (top) and Phase (bottom) axes
    h.ax_mag = axes("Units", "normalized", "Position", [0.38, 0.58, 0.58, 0.36]);
    title("Magnitude Response");
    dsp_freq_xticks();
    ylabel("Magnitude (dB)");
    grid on;

    h.ax_phase = axes("Units", "normalized", "Position", [0.38, 0.10, 0.58, 0.36]);
    title("Phase Response");
    dsp_freq_xticks();
    ylabel("Phase (degrees)");
    grid on;

    % Stability indicator strip between the two plots
    h.txt_stability = uicontrol(h.fig, "Style", "text", ...
                                "String", "", ...
                                "Units", "normalized", ...
                                "Position", [0.38, 0.50, 0.58, 0.04], ...
                                "FontSize", 10, "FontWeight", "bold", ...
                                "HorizontalAlignment", "center");

    % Store handles
    guidata(h.fig, h);

    % 4. Callbacks
    function cb_preset_wrapper(src, ~)
        idx = get(src, "value");
        if idx <= 1; return; end
        handles = guidata(src);

        %                   Fs      Fc     Fc2    N  type topo arch
        defs = {
            44100,  20000,  0,     8, 1, 1, 1;   % Anti-aliasing (CD Audio)
            8000,   300,    3400,  4, 3, 1, 1;   % Voice Band-pass
            44100,  250,    0,     4, 1, 2, 1;   % Bass Isolation
            44100,  4000,   0,     4, 2, 1, 1;   % Treble Isolation
            44100,  80,     0,     4, 1, 1, 1;   % Sub-bass Crossover
            1000,   45,     55,    4, 4, 1, 1;   % 50 Hz Mains Notch
            1000,   55,     65,    4, 4, 1, 1;   % 60 Hz Mains Notch
            44100,  10,     0,     2, 2, 1, 1;   % DC Blocker
            44100,  8000,   0,     6, 1, 3, 1;   % Wideband Noise Reduction
            44100,  20000,  0,    64, 1, 1, 2;   % FIR Anti-aliasing (CD Audio)
            8000,   300,    3400, 48, 3, 1, 2;   % FIR Voice Band-pass
            44100,  4000,   0,    48, 1, 3, 2;   % FIR Noise Suppression
        };

        d = defs(idx - 1, :);
        set(handles.edit_Fs,    "string", num2str(d{1}));
        set(handles.edit_Fc,    "string", num2str(d{2}));
        set(handles.edit_Fc2,   "string", num2str(d{3}));
        set(handles.edit_Order, "string", num2str(d{4}));
        set(handles.popup_Type, "value",  d{5});
        set(handles.popup_Arch, "value",  d{7});
        if d{7} == 1
            set(handles.popup_Topo, "String", ...
                {"Butterworth", "Chebyshev I", "Chebyshev II", "Elliptic"});
        else
            set(handles.popup_Topo, "String", ...
                {"Hamming Window", "Hanning Window", "Blackman Window", ...
                 "Kaiser Window", "Parks-McClellan", "Least-Squares"});
        end
        set(handles.popup_Topo, "value",  d{6});

        dsp_design_callback(handles);
    end

    function cb_arch_wrapper(src, ~)
        arch_idx = get(src, "value");
        handles = guidata(src);
        if arch_idx == 1
            set(handles.popup_Topo, "String", ...
                {"Butterworth", "Chebyshev I", "Chebyshev II", "Elliptic"}, "Value", 1);
        else
            set(handles.popup_Topo, "String", ...
                {"Hamming Window", "Hanning Window", "Blackman Window", ...
                 "Kaiser Window", "Parks-McClellan", "Least-Squares"}, "Value", 1);
        end
    end

    function cb_design_wrapper(src, ~)
        handles = guidata(src);
        dsp_design_callback(handles);
    end

    function cb_export_wrapper(src, ~)
        handles = guidata(src);
        dsp_export_callback(handles);
    end

    function cb_code_export_wrapper(src, ~)
        handles = guidata(src);
        dsp_code_export(handles);
    end

    function cb_save_wrapper(src, ~)
        handles = guidata(src);
        dsp_save_callback(handles);
    end

    function cb_load_wrapper(src, ~)
        handles = guidata(src);
        dsp_load_callback(handles);
    end

    function cb_report_wrapper(src, ~)
        handles = guidata(src);
        dsp_generate_report(handles);
    end

    function cb_compare_wrapper(src, ~)
        handles = guidata(src);
        dsp_compare_window(handles);
    end

    function cb_analyze_wrapper(src, ~)
        handles = guidata(src);
        Fs = str2double(get(handles.edit_Fs, "string"));

        if isnan(Fs) || Fs <= 0
            errordlg("Please enter a valid Sampling Frequency (Fs)."); return;
        end

        % Use the coefficients from the last design action (DESIGN FILTER
        % button or P/Z editor submit). Fall back to recomputing from the
        % UI controls only if no filter has been designed yet.
        if isfield(handles, "last_b") && ~isempty(handles.last_b)
            b = handles.last_b;
            a = handles.last_a;
        else
            Fc       = str2double(get(handles.edit_Fc,    "string"));
            Fc2      = str2double(get(handles.edit_Fc2,   "string"));
            N        = str2double(get(handles.edit_Order, "string"));
            type_idx = get(handles.popup_Type, "value");
            topo_idx = get(handles.popup_Topo, "value");

            if isnan(Fc) || isnan(N)
                errordlg("Please design a filter first."); return;
            end
            if Fc >= Fs/2
                errordlg("Cutoff Fc must be less than Nyquist (Fs/2)."); return;
            end
            if type_idx >= 3 && (isnan(Fc2) || Fc2 >= Fs/2 || Fc2 <= Fc)
                errordlg("For Band Pass/Stop, Fc2 must satisfy: Fc < Fc2 < Fs/2."); return;
            end

            arch_idx = get(handles.popup_Arch, "value");
            Rp = str2double(get(handles.edit_Rp, "string"));
            Rs = str2double(get(handles.edit_Rs, "string"));
            if isnan(Rp) || Rp <= 0; Rp = 3; end
            if isnan(Rs) || Rs <= 0; Rs = 40; end
            try
                [b, a] = dsp_compute_filter(Fs, Fc, Fc2, N, type_idx, topo_idx, arch_idx, Rp, Rs);
            catch err
                errordlg(sprintf("Filter design failed:\n%s", err.message), "Design Error");
                return;
            end
        end

        current_fig = ancestor(src, "figure");
        set(current_fig, "Visible", "off");
        dsp_result_window(current_fig, b, a, Fs);
    end

    function cb_pz_editor_wrapper(src, ~)
        handles = guidata(src);
        Fs = str2double(get(handles.edit_Fs, "string"));
        if isnan(Fs) || Fs <= 0
            errordlg("Please enter a valid Sampling Frequency (Fs) first."); return;
        end
        current_fig = ancestor(src, "figure");
        set(current_fig, "Visible", "off");
        fda_pz_editor(current_fig, Fs);
    end

    function cb_audio_demo_wrapper(src, ~)
        handles = guidata(src);
        Fs = str2double(get(handles.edit_Fs, "string"));

        if isnan(Fs) || Fs <= 0
            errordlg("Please enter a valid Sampling Frequency (Fs)."); return;
        end

        if ~isfield(handles, "last_b") || isempty(handles.last_b)
            errordlg("Please design a filter first."); return;
        end

        current_fig = ancestor(src, "figure");
        set(current_fig, "Visible", "off");
        dsp_audio_demo(current_fig, handles.last_b, handles.last_a, Fs);
    end

    function cb_exit_wrapper(~, ~)
        choice = questdlg("Are you sure you want to exit?", ...
                          "Exit Tool", "Yes", "No", "No");
        if strcmp(choice, "Yes")
            % Close only this tool's own windows (tagged by dsp_new_window),
            % leaving any unrelated Octave figures untouched.
            delete(findobj(0, "type", "figure", "tag", "dsppack_window"));
        end
    end
end
