## Copyright (C) 2026 Torten Lilge
##
## This function is part of the GNU Octave Control Package
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Octave; see the file COPYING.  If not, see
## <http://www.gnu.org/licenses/>.

## -*- texinfo -*-
## @deftypefn {Function File} {[@var{idx_1}, ..., @var{idx_N}]=} __control_args__ (@var{args}, @var{types})
## @deftypefnx {Function File} {[@var{idx_1}, ..., @var{idx_N}, @var{sys_names}, @var{styles}]=} __control_args__ (@var{args}, @var{types}, @var{names})
##
## Internal function for analyting the parameters of some typical functions
## of the control package togehter with an optional list of names of systems
## that are included in the argument list.
##
## @strong{Inputs}
## @table @var
## @item args
## Cell array of all arguments that have to be analyzed.
## @item types
## Cell array of N types that have to be extraceted from the arguments.
## This array con contain the function handles that are used to test for
## the desired type, like @code{@@ischar} or the string @code{"@@xyz"} for
## testing for the class @code{@@xyz}.
## @item names
## The input names of the args, generated by @*
## @code{names = arrayfun (@@inputname, 1:nargin, "UniformOutput", false)}@*
## If this input argument is given and is non-empty the last two output arguments
## are cells artays with the system names and possibly provided custom styles.
## @end table
##
## @strong{Outputs}
## @table @var
## @item idx_X
## The indices of the occurences of positive tests for @code{types@{X@}},
## like, e.g., @code{[0,1,0,0,1]} if the 2nd and 5th argument matches @code{types@{X@}}.
## @item sys_names
## Cell array with the names of all systems in @var{args}. Systems are searched
## in the order @code{@@lti}, @code{@@ss}, @code{@@tf}, where the search stops
## whenever a system was found. For empty system names, like, e.g. when passing
## @code{tf (1, [1 1])} as argument, @code{SysX} is used with @code{X} the
## number of the system in @var{args}.
## @item styles
## Cell array of possibly provided custom styles. As @var{sys_names}, this
## output argument is only available if the input argument @var{names} is
## given.
## @end table
## @end deftypefn

function varargout = __control_args__ (args, types, names={})

  if nargin < 2
    print_usage ();
  endif

  n_types = length (types);
  n_names = length (names);

  if (n_names)

    if (nargout != n_types + 2)   # names and styles also in varargout
      error (["__control_args__: number of output parameters must ",...
              "match number of types plus two (system names and styles)."]);
    endif
    if (length (args) != n_names)
      error ("__control_args__: args and names must have same lengths");
    endif

  else

    if (nargout < n_types)
      error (["__control_args__: number of output parameters cannot ",...
              "be smaller than number of types."]);
    endif

  endif

  ## analyze args
  for i = 1:n_types

    if (ischar (types{i})) && (types{i}(1) == "@")
      varargout{i} = cellfun (@isa, args, {types{i}(2:end)});
    else
      varargout{i} = cellfun (types{i}, args);
    endif

  endfor

  ## if system names and styles have to be separated
  varargout{end+1} = {};
  varargout{end+1} = {};

  if n_names

    ## get positon in varargout of sys_idx and sty_idx
    sys_type = {"@lti"};
    tmp = cellfun (@(x) @ischar(x) && strcmp (x,sys_type), types);
    sys_idx = find(tmp);
    if (isempty (sys_idx))
      error ("__control_args__: system names requested but no system type found in args.");
    endif
    sys_idx = varargout{sys_idx};   # get system indices in args

    ## crate a list of system names and replace empty names by "SysX"
    sys_names = names(sys_idx);
    idx_no_name = cellfun (@isempty, sys_names);  # no system name
    sys_no_name = find (idx_no_name);
    sys_names(sys_no_name) = cellstr (arrayfun (@(x) ['Sys',num2str(x)], ...
                                      sys_no_name(:), "uniformoutput", false));
    varargout{end-1} = sys_names;

    ## get positon in varargout of sty_idx
    sty_type = "ischar";
    tmp = cellfun (@(x) @is_function_handle(x) && strcmp (func2str(x),sty_type), types);
    sty_idx = find(tmp);
    if (isempty (sty_idx))
      error ("__control_args__: styles requested but no style type found in args.");
    endif
    sty_idx = varargout{sty_idx};   # get style indices in args

    ## extract plotting styles
    tmp = cumsum (sys_idx);
    tmp(sys_idx | ! sty_idx) = 0;
    n = nnz (sys_idx);
    sty = arrayfun (@(x) args(tmp == x), 1:n, "uniformoutput", false);

    ## default plotting styles if empty
    if isempty (findall(0,'type','figure'))
      ## no figure open to get the current colororder, get default one
      colororder = get(groot, "defaultaxescolororder");
    else
      ## there is an oprn figure, get colororder from current axes
      colororder = get (gca, "colororder");
    endif
    rc = rows (colororder);

    ## compose styles from custom styles, default colors and system names
    for k = 1:length (sty)
      if (isempty (sty{k}))
        ## no style at all, add default color and name
        sty{k} = {"color", colororder(1+rem (k-1, rc), :), [";",sys_names{k},";"]};
      else
        if (length (strfind (sty{k}{1}, ";")) < 2)
          ## style wihtout description included
          sty{k}{1} = [sty{k}{1}, ';',sys_names{k},';'];
        elseif (sty{k}{1}(1) == ";") && (sty{k}{1}(end) == ";")
          ## only description included
          sty{k} = {sty{k}{1}, "color", colororder(1+rem (k-1, rc), :)};
        endif
      endif
    endfor

    varargout{end} = sty;

  endif

