% Компилирует S-function
clear, clc
close;


Ts = 0.00001;

delete("*.mexw64")
delete("*.mexw64.pdb")
delete(".\MCU_Wrapper\Outputs\*.*");
set_param(gcb, 'consoleOutput', '');

% Флаг режима отладки
definesArg = buildWrapperDefinesString();
definesUserArg = parseDefinesMaskText();
definesAllArg = [definesArg + " " + definesUserArg];    


if read_checkbox('enableDebug')
    modeArg = "debug";
else
    modeArg = "release";
end

if read_checkbox('fullOutput') || read_checkbox('extConsol') 
    echoArg = 'echo_enable';
else
    echoArg = 'echo_disable';

end

[includesArg, codeArg] = make_mex_arguments('incTable', 'srcTable');

% Вызов батника с двумя параметрами: includes и code
cmd = sprintf('.\\MCU_Wrapper\\run_mex.bat "%s" "%s" "%s" %s %s', includesArg, codeArg, definesAllArg, modeArg, echoArg);



if read_checkbox('extConsol')
    cmdout = runBatAndShowOutput(cmd);
else
    [status, cmdout]= system(cmd);
end

% Сохраним вывод в параметр маски с именем 'consoleOutput'
set_param(gcb, 'consoleOutput', cmdout);

% % Обновим Mask Display для показа
% maskDisplayStr = sprintf('disp(''%s'')', cmdout);
% set_param(gcb, 'MaskDisplay', maskDisplayStr);

beep



%% DEFINE PARAMS


function [includesArg, codeArg] = make_mex_arguments(incTableName, srcTableame)
%MAKE_MEX_ARGUMENTS Формирует строки аргументов для вызова mex-компиляции через батник
%
% [includesArg, codeArg] = make_mex_arguments(includesCell, codeCell)
%
% Вход:
%   includesCell — ячейковый массив путей к директориям include
%   codeCell     — ячейковый массив исходных файлов
%
% Выход:
%   includesArg  — строка для передачи в батник, например: "-I"inc1" -I"inc2""
%   codeArg      — строка с исходниками, например: ""src1.c" "src2.cpp""


    % Здесь пример получения из маски текущего блока (замени по своему)
    blockHandle = gcbh;  % или замени на нужный блок

    includesCell = parseCellString(get_param(blockHandle, incTableName));
    codeCell = parseCellString(get_param(blockHandle, srcTableame));

    % Оборачиваем пути в кавычки и добавляем -I
    includesStr = strjoin(cellfun(@(f) ['-I"' f '"'], includesCell, 'UniformOutput', false), ' ');

    % Оборачиваем имена файлов в кавычки
    codeStr = strjoin(cellfun(@(f) ['"' f '"'], codeCell, 'UniformOutput', false), ' ');

    % Удаляем символ переноса строки и пробел в конце, если вдруг попал
    codeStr = strtrim(codeStr);
    includesStr = strtrim(includesStr);

    % Оборачиваем всю строку в кавычки, чтобы батник корректно понял
    % includesArg = ['"' includesStr '"'];
    % codeArg = ['"' codeStr '"'];
    includesArg = includesStr;
    codeArg = codeStr;

end


function definesWrapperArg = buildWrapperDefinesString()
    blockHandle = gcbh;

    % Получаем MaskValues и MaskNames
    maskValues = get_param(blockHandle, 'MaskValues');
    paramNames = get_param(blockHandle, 'MaskNames');

    % Индексы параметров
    idxCycles = find(strcmp(paramNames, 'threadCycles'));
    idxClk    = find(strcmp(paramNames, 'mcuClk'));

    % Значения
    cyclesVal = maskValues{idxCycles};
    clkMHz    = str2double(maskValues{idxClk});
    clkHz     = round(clkMHz * 1e6);

    % Формируем defines в формате: -D"NAME=VALUE"
    if read_checkbox('enableThreading')
        def1 = ['-D"RUN_APP_MAIN_FUNC_THREAD"'];
    else
        def1 = [''];
    end

    if read_checkbox('enableDeinit')
        def2 = ['-D"DEINITIALIZE_AFTER_SIM"'];
    else
        def2 = [''];
    end

    def3 = ['-D"DEKSTOP_CYCLES_FOR_MCU_APP__EQ__' cyclesVal '"'];
    def4 = ['-D"MCU_CORE_CLOCK__EQ__' num2str(clkHz) '"'];

    definesArg = strjoin({def1, def2, def3, def4}, ' ');
end


function definesUserArg = parseDefinesMaskText()
    blockHandle = gcbh;

    % Получаем MaskValues и MaskNames
    maskValues = get_param(blockHandle, 'MaskValues');
    paramNames = get_param(blockHandle, 'MaskNames');

    % Индекс параметра userDefs
    idxUserDefs = find(strcmp(paramNames, 'userDefs'));
    definesText = maskValues{idxUserDefs}; % Текст с пользовательскими определениями

    % Убираем буквальные символы \n и \r
    definesText = strrep(definesText, '\n', ' ');
    definesText = strrep(definesText, '\r', ' ');

    % Разбиваем по переносам строк
    lines = split(definesText, {'\n', '\r\n', '\r'});

    parts = strings(1,0); % пустой массив строк

    for k = 1:numel(lines)
        line = strtrim(lines{k});
        if isempty(line)
            continue;
        end

        % Разбиваем по пробелам, чтобы получить отдельные определения в строке
        tokens = split(line);

        for t = 1:numel(tokens)
            token = strtrim(tokens{t});
            if isempty(token)
                continue;
            end

            eqIdx = strfind(token, '=');
            if isempty(eqIdx)
                % Просто ключ без значения
                parts(end+1) = sprintf('-D"%s"', token);
            else
                key = strtrim(token(1:eqIdx(1)-1));
                val = strtrim(token(eqIdx(1)+1:end));
                parts(end+1) = sprintf('-D"%s__EQ__%s"', key, val);
            end
        end
    end

    definesUserArg = strjoin(parts, ' ');
end


%% PARSE FUNCTIONS

function out = parseCellString(str)
    str = strtrim(str);
    if startsWith(str, '{') && endsWith(str, '}')
        str = str(2:end-1);
    end

    parts = split(str, ';');
    out = cell(numel(parts), 1);
    for i = 1:numel(parts)
        el = strtrim(parts{i});
        if startsWith(el, '''') && endsWith(el, '''')
            el = el(2:end-1);
        end
        out{i} = el;
    end

    if isempty(out) || (numel(out) == 1 && isempty(out{1}))
        out = {};
    end
end

function str = cellArrayToString(cellArray)
    quoted = cellfun(@(s) ['''' s ''''], cellArray, 'UniformOutput', false);
    str = ['{' strjoin(quoted, ';') '}'];
end



function checkbox_state = read_checkbox(checkboxName)
    maskValues = get_param(gcbh, 'MaskValues');
    paramNames = get_param(gcbh, 'MaskNames');
    
    inxCheckBox = find(strcmp(paramNames, checkboxName));
    
    checkbox_state_str = maskValues{inxCheckBox};
    if strcmpi(checkbox_state_str, 'on')
        checkbox_state = 1;
    else
        checkbox_state = 0;
    end
end


%% CONSOLE FUNCTIONS

function cmdret = runBatAndShowOutput(cmd)
    import java.io.*;
    import java.lang.*;
    cmdEnglish = ['chcp 437 > nul && ' cmd];
    pb = java.lang.ProcessBuilder({'cmd.exe', '/c', cmdEnglish});
    pb.redirectErrorStream(true);
    process = pb.start();

    reader = BufferedReader(InputStreamReader(process.getInputStream()));

    cmdret = ""; % Здесь будем накапливать весь вывод

    while true
        if reader.ready()
            line = char(reader.readLine());
            if isempty(line)
                break;
            end
            cmdret = cmdret + string(line) + newline; % сохраняем вывод
            % Здесь выводим только новую строку
            safeLine = strrep(line, '''', ''''''); % Экранируем апострофы
            logWindow_append(safeLine);
            drawnow; % обновляем GUI
        else
            if ~process.isAlive()
                % дочитываем оставшиеся строки
                while reader.ready()
                    line = char(reader.readLine());
                    if isempty(line)
                        break;
                    end
                    cmdret = cmdret + string(line) + newline; % сохраняем вывод
                    safeLine = strrep(line, '''', '''''');
                    logWindow_append(safeLine);
                    drawnow;
                end
                break;
            end
            pause(0.2);
        end
    end
    process.waitFor();
end


function logWindow_append(line)
    persistent fig hEdit jScrollPane jTextArea

    if isempty(fig) || ~isvalid(fig)
        fig = figure('Name', 'Log Window', 'Position', [100 100 600 400]);
        hEdit = uicontrol('Style', 'edit', ...
            'Max', 2, 'Min', 0, ...
            'Enable', 'on', ...
            'FontName', 'Courier New', ...
            'Position', [10 10 580 380], ...
            'HorizontalAlignment', 'left', ...
            'BackgroundColor', 'white', ...
            'Tag', 'LogWindowFigure');

        jScrollPane = findjobj(hEdit);                  % JScrollPane
        jTextArea = jScrollPane.getViewport.getView;   % JTextArea внутри JScrollPane
    end

    oldText = get(hEdit, 'String');
    if ischar(oldText)
        oldText = {oldText};
    end

    set(hEdit, 'String', [oldText; {line}]);
    drawnow;
    % Автоскролл вниз:
    jTextArea.setCaretPosition(jTextArea.getDocument.getLength);
    drawnow;
end
