function hdlToolStrip( fcnname, varargin)
%HDLTOOLSTRIP - Handles HDL coder toolstrip actions

%   Copyright 2017-2019 The MathWorks, Inc.
    fnc = str2func( fcnname );
    fnc(varargin{:});
end

function buildConvertRF_HDL(userdata, cbinfo, action)
% sync enabled state with system selector
    buildConvertRF(userdata, cbinfo, action);
    checkReferenceHierarchy(cbinfo, action)
end

function HDLCoderBlockPropsRF(cbinfo, action) %#ok<*DEFNU>
% Control enabled behavior of HDL Block Properties button
    if isSelectionValidHDLBlock(cbinfo)
        action.enabled = true;
    end
end

function systemSelectorRF_HDL(userdata, cbinfo, action)
% Wrap the System Selector refresher with extra logic to pull system
% information from config params
    systemSelectorRF(userdata, cbinfo, action);
    checkReferenceHierarchy(cbinfo, action)
    if action.selected && hasHDLConfig(cbinfo)
        % system is currently pinned, make sure it is synchronized with the
        % config set
        hdlSystem = hdlget_param(cbinfo.model.Name, 'HDLSubsystem');
        if ~action.description.isequal(slservices.StringOrID(hdlSystem))
            insertPinnedSystem(hdlSystem, cbinfo, action);
            if ~action.description.isequal(slservices.StringOrID(hdlSystem))
                % if HDL Subsystem was not propagated to system selector,
                % then the SL object was either changed or the entered HDL
                % Subsystem is invalid
                % Copy the system selector information into the HDL
                % Subsystem to keep things synchronized
                hdlset_param(cbinfo.model.Name, 'HDLSubsystem', getSelectedSystem(cbinfo))
            end
        end
    end
end

function HDLCoderTraceCodeRF(cbinfo, action) %#ok<*DEFNU>
% Control enabled behavior of HDL Block Properties button
    modelName = get_param(cbinfo.Context.StudioApp.blockDiagramHandle,'Name');
    if isSelectionValidHDLBlock(cbinfo) && ...
            coder.internal.slcoderReport('enableHighlightHDLCodeMenu', modelName) % if traceability report is available
        if ~action.enabled %avoid updating flag if unnecessary
            action.enabled = true;
        end
    else
        action.enabled = false;
    end
end

function HDLTestbenchRF(userdata, cbinfo, action)
% check if system is top level of the model for testbench generation
    [~, ~, selectedSystem] = getSelectedSystem(cbinfo);
    if isa(selectedSystem, 'Simulink.BlockDiagram')
        % testbench generation not supported at top level of model
        action.enabled = false;
    else
        % continue on calling general refresher for system selector related
        % actions
        buildConvertRF_HDL(userdata, cbinfo, action);
    end
end

function HDLCoderVerifyRF(~, action)
% verify if HDL Verifier is installed
    persistent hasHDLVerifierLicense;
    if isempty(hasHDLVerifierLicense)
        % Check HDL Verifier license
        tooldir = fullfile(matlabroot, 'toolbox', 'hdlverifier');
        hasHDLVerifierLicense = license('test', 'EDA_Simulator_Link') && exist(tooldir, 'dir');
        % enable if license and install are valid
        action.enabled = hasHDLVerifierLicense;
    elseif action.enabled ~= hasHDLVerifierLicense
        % don't continue to check for license after the first time
        action.enabled = hasHDLVerifierLicense;
    end
end

function OpenHDLRptRF(cbinfo, action)
% refresher to enable/disable the HDL Report button
    rptPath = hdlcoder.report.ReportInfo.getSavedRptPath(cbinfo.model.Name, false);
    % only enable if a HDL check report has been generated and the file
    % still exists
    % if the check report exists, then a code generation report may also
    % exist
    action.enabled = ~isempty(rptPath) && exist(rptPath, 'file');
end

function toggleHDLAppCB(userdata, cbinfo)
% callback for when HDL app is selected in app gallery
% Set system selector in pinned state when app is opened after being closed
% and a HDL System exists in the config params
    contextManager = cbinfo.studio.App.getAppContextManager;
    customContext = contextManager.getCustomContext(userdata);
    isAppPreviouslyOpen = ~isempty(customContext);

    if ~isAppPreviouslyOpen
        % open new HDL tab
        c = dig.Configuration.get();
        app = c.getApp(userdata);
        contextProvider = app.contextProvider;
        customContext = feval( contextProvider, app, cbinfo);
        contextManager.activateApp(customContext)
        
        % set system selector to pinned state so it reflects the HDL system
        % in the config params with the HDL Toolstrip is opened after being
        % closed
        % The system selector refresher will handle updating the value
        updatePinnedSystem(cbinfo);
    elseif isempty(cbinfo.EventData) || cbinfo.EventData
        % HDL app button was clicked while tab was open, bring to focus
        ts = cbinfo.studio.getToolStrip;
        ts.ActiveTab = customContext.DefaultTabName;
    else
        % HDL app is closing, remove listeners to prevent memory leaks
        customContext.BDListener = [];
        customContext.CSListener = [];
        contextManager.deactivateApp(userdata)
    end
end

function OpenHDLLIBCB(~)
% hdl block library callback
    isHDLLIBOn = privhdllib_library_browser('checkState');
    
    if(isHDLLIBOn)
        % hdl library browser view is already generated, open it
        slLibraryBrowser;
    else
        % generate hdl library browser view and open it
        hdllib;
    end
end

function OpenHDLCoderOptionsCB(userdata, cbinfo)
% HDL options and report options callback
if nargin < 2
    % called without userdata, only cbinfo was passed in but it is
    % currently held in userdata variable
    cbinfo = userdata;
    userdata = '';
end

mdlName = cbinfo.model.Name;
sectionName = DAStudio.message('HDLShared:hdldialog:hdlccHDLCodername');
if ~isempty(userdata)
    % open a subsection of the HDL settings
    sectionName = [sectionName '/' DAStudio.message(userdata)];
end

hcc = gethdlcc(mdlName);
if ~isempty(hcc)
    hcc.createCLI;
    configset.showParameterGroup(mdlName, {sectionName});
end
end

function systemSelectorCB_HDL(~, cbinfo)
% Wrapper for systemSelectorCB to catch pin press
    if cbinfo.EventData
        % A system has been pinned
        % Check if it is a new valid system and set the system in the
        % config params if so
        mdlName = cbinfo.model.Name;
        currCPSystem = hdlget_param(mdlName, 'HDLSubsystem');
        selectedSystem = getSelectedSystem(cbinfo);
        if ~strcmp(currCPSystem, selectedSystem)
            % System was updated in toolstrip
            hdlset_param(mdlName, 'HDLSubsystem', selectedSystem);
        end
    end
end

function GenerateHDLCB(cbinfo)
% makehdl callback

    % the diagnostic viewer stage is created a new for evey run
    Simulink.output.Stage('HDLCoder', 'ModelName', cbinfo.model.Name, 'UIMode', true);
    runWorkflow('makehdl', cbinfo);
end

function WorkflowAdvisorCB(cbinfo)
% hdl workflow advisor callback
    
    [~, ~, ~, isPinned] = getSelectedSystem(cbinfo);
    if isPinned
        % run WFA on the pinned system
        runWorkflow('hdladvisor', cbinfo);
    else
        % open the system selector for the WFA on the model
        hdladvisor(cbinfo.model.name, 'SystemSelector');
    end
end

function ModelAdvisorCB(cbinfo)
% hdl model advisor callback
    systemPath = getSelectedSystem(cbinfo);

    hdlmodelchecker(systemPath);
end

function HDLConfigAttachmentCB(cbinfo)
% hdl configuration callback
    hdlcodermenus('HDLC_AddHDLCoderCB', cbinfo);
    updatePinnedSystem(cbinfo);
end

function HDLConfigDetachmentCB(cbinfo)
% hdl configuration callback
    hdlcodermenus('HDLC_RemoveHDLCoderCB', cbinfo);
end

function GenerateHDLTBCB(cbinfo)
% makehdltb callback
    
    pv_args = [...
        '''GenerateHDLTestBench'', ''on'', '...
        '''GenerateCoSimModel'', ''none'', ',...
        '''GenerateSVDPITestBench'', ''none'''...
        ];
    
    if isCallFromToolstrip(cbinfo)
        runWorkflow('makehdltb', cbinfo, pv_args);
    else % dont' set any PV pairs for legacy callback
        runWorkflow('makehdltb', cbinfo);
    end
end

function GenerateHDLCosimCB(cbinfo)
% makehdltb with cosim callback
% get tool info, massage to acceptable CLI name
    tool = getHDLSimTool(cbinfo.model.Name);
    
    pv_args = [...
        '''GenerateHDLTestBench'', ''off'', '...
        '''GenerateCoSimModel'', ''' tool ''', ',...
        '''GenerateSVDPITestBench'', ''none'''...
        ];
    
    runWorkflow('makehdltb', cbinfo, pv_args)
end

function GenerateSVDPICB(cbinfo)
% makehdltb with System Verilog DPI-C callback
    tool = getHDLSimTool(cbinfo.model.Name);
    
    pv_args = [...
        '''GenerateHDLTestBench'', ''off'', '...
        '''GenerateCoSimModel'', ''none'', ',...
        '''GenerateSVDPITestBench'', ''' tool ''''...
        ];
    
    runWorkflow('makehdltb', cbinfo, pv_args);
end

function OpenHDLRptCB(cbinfo)
% open either the HDL Check Report or HDL Codegen Report
    % fetch codegen report path
    rptPath = hdlcoder.report.ReportInfo.getSavedRptPath(cbinfo.model.Name, true);
    if isempty(rptPath)
        % no codegen report exists, open check report instead
        rptPath = hdlcoder.report.ReportInfo.getSavedRptPath(cbinfo.model.Name, false);
    end
    % open in new window in case user has web window already open
    web(rptPath);
end

function [path, name, selectedSystem, isPinned] = getSelectedSystem(cbinfo)
    % get chosen system in the system selector
    [isUsingToolstrip, systemSelectorAction] = isCallFromToolstrip(cbinfo);
    if isUsingToolstrip
        % call back executed using toolstrip
        pinnedSystem = cbinfo.studio.App.getPinnedSystem(systemSelectorAction.name);
        isPinned = ~isempty(pinnedSystem);
        if isPinned
            % give priority to pinned system if it exists
            selectedSystem = pinnedSystem;
        else
            selection = cbinfo.getSelection;
            % check if a single system is selected
            if size(selection) == 1
                % pass the selection
                if ~isprop(selection, 'name') || isempty(selection.name)
                    % selected object is not a system or block, grab the
                    % system in the current view instead
                    selectedSystem = cbinfo.uiObject;
                else
                    selectedSystem = selection;
                end
            else
                % pass the system in the current view
                selectedSystem = cbinfo.uiObject;
            end
        end
        name = selectedSystem.name;
        path = selectedSystem.getFullName;
    else
        % callback is from legacy global menu
        % get selected system from config params
        hdlcc = hdlcodermenus('loc_getHDLCC', cbinfo);
        if ~isempty(hdlcc)
            path = hdlcc.getCLI.HDLSubsystem;
            name = get_param(path, 'Name');
        else
            path = [];
            name = [];
        end
        selectedSystem = [];
        isPinned = false;
    end
end

function val = hasHDLConfig(cbinfo)
    val = ~isempty(hdlcodermenus('loc_getHDLCC', cbinfo));
end

function [val, systemSelectorAction] = isCallFromToolstrip(cbinfo)
% return false if callback is not from the toolstrip
% callback is from a legacy schema
    toolstrip = cbinfo.studio.getToolStrip();
    systemSelectorAction = toolstrip.getAction('selectSystemHdlCoderComboBoxAction');
    if ~isempty(systemSelectorAction)
        val = true;
    else
        val = false;
    end
end

function runWorkflow(fcnStr, cbinfo, varargin)
% central call for functions which print to the diagnostic viewer
    systemPath = getSelectedSystem(cbinfo);
    
    try
        if nargin == 2
            eval([fcnStr '(''' systemPath ''')']);
        else
            eval([fcnStr '(''' systemPath ''', ' varargin{1} ')']);
        end
    catch e
        hdl_coder_auto_build_stage = Simulink.output.Stage('HDLCoder', 'ModelName', cbinfo.model.Name, 'UIMode', true);             %#ok<NASGU>
        Simulink.output.error( e,'Component','HDLCoder','Category','HDL');
    end
end

function tool = getHDLSimTool(mdlName)
    tool = hdlget_param(mdlName, 'SimulationTool');
    if strcmpi(tool, 'mentor graphics modelsim')
        tool = 'ModelSim';
    elseif strcmpi(tool, 'cadence incisive')
        tool = 'Incisive';
    end
end

function checkReferenceHierarchy(cbinfo, action)
    [selectedSystemPath, ~, obj] = getSelectedSystem(cbinfo);
    if strcmp(systemSelectorStateFcn(obj), 'nonsupported')
        % invalid system for code generation, no need to check hierarchy
        return
    end
    selectedModel = bdroot(selectedSystemPath);
    topModel = cbinfo.model.Name;
    if ~strcmp(selectedModel, topModel)
        % top model and system for code generation must be the same for HDL
        % toolstrip to avoid confusion on config settings
        action.enabled = false;
        if strcmp(action.name, 'selectSystemHdlCoderComboBoxAction')
            % override any other previously set messaging
            action.validationState = 'error';
            action.errorText = message('hdlcoder:toolstrip:SystemSelectorReferenceHierarchy').string;
        end
    end
end

function val = isSelectionValidHDLBlock(cbinfo)
% Determines if the selected object in Simulink is a possible block for HDL code generation
% Used to enable HDL Block Properties and Navigate to Code buttons.
    val = ...
        ~contains('simulinkCanvas',cbinfo.Context.TypeChain) && ... % something is selected
        ~contains('signal',cbinfo.Context.TypeChain) && ... % not a Simulink signal
        ~contains('state',cbinfo.Context.TypeChain) && ... % not inside Stateflow chart
        ~contains('area',cbinfo.Context.TypeChain) && ... % not a Simulink area
        ~contains('annotation',cbinfo.Context.TypeChain) && ... % not a Simulink annotation
        numel(cbinfo.getSelection) == 1; % only one block can be selected
end

function updatePinnedSystem(cbinfo)
% if a HDL System exists in the config params and the system selector is
% not pinned, then override the system selector with the HDL System and set
% it to be pinned
    hdlSystem = hdlget_param(cbinfo.model.Name, 'HDLSubsystem');
    [~, ~, ~, isPinned] = getSelectedSystem(cbinfo);
    [~, systemSelectorAction] = isCallFromToolstrip(cbinfo);
    if ~isPinned && ~isempty(systemSelectorAction)
        % system selector should only be updated if visible
        insertPinnedSystem(hdlSystem, cbinfo, systemSelectorAction);
    end
end

function insertPinnedSystem(systemPath, cbinfo, action)
    try
        name = get_param(systemPath, 'Name');
        action.validateAndSetEntries({name});
        action.selectedItem = name;
        action.description = systemPath;

        obj = get_param(systemPath, 'object');
        cbinfo.studio.App.insertPinnedSystem(action.name, obj, Simulink.ID.getSID(obj));
    catch me
        % if the HDL Subsystem is not a valid Simulink object, don't do
        % anything
        assert(strcmp(me.identifier, 'Simulink:Commands:InvSimulinkObjectName'), ...
            'unexpected error found while trying to pin HDL Subsystem');
    end
end
