## Copyright (C) 2025
##
## This file is part of Octave.
##
## Octave 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 3 of the License, or
## (at your option) any later version.
##
## Octave 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
## <https://www.gnu.org/licenses/>.

## -*- texinfo -*-
## @deftypefn {} {@var{tbl} =} vertcat (@var{tbl1}, @var{tbl2}, @dots{})
## Vertical concatenation of tables.
##
## Concatenate tables vertically.  All tables must have the same variable
## names.  Rows are stacked on top of each other.
##
## @seealso{horzcat, table}
## @end deftypefn

function tbl = vertcat (varargin)
  if (nargin < 2)
    error ("vertcat: at least two tables required");
  endif

  ## Check if any input is not a table - delegate to built-in vertcat
  for i = 1:nargin
    if (! istable (varargin{i}))
      ## At least one input is not a table, use built-in vertcat
      tbl = builtin ("vertcat", varargin{:});
      return;
    endif
  endfor

  ## All inputs are tables - proceed with table-specific concatenation
  for i = 1:nargin
    varargin{i} = __ensure_properties__ (varargin{i});
  endfor

  ## Get variable names from first table
  var_names = varargin{1}.Properties.VariableNames;

  ## Check all tables have same variable names
  for i = 2:nargin
    next_vars = varargin{i}.Properties.VariableNames;
    if (numel (next_vars) != numel (var_names))
      error ("vertcat: all tables must have the same number of variables");
    endif
    if (! isequal (next_vars, var_names))
      ## Try to match by position if names differ
      warning ("vertcat: variable names differ, matching by position");
    endif
  endfor

  ## Start with first table
  tbl = varargin{1};

  ## Concatenate each variable vertically
  for i = 1:numel (var_names)
    var_name = var_names{i};
    var_data = tbl.(var_name);

    ## Concatenate from all tables
    for j = 2:nargin
      next_tbl = varargin{j};
      if (isfield (next_tbl, var_name))
        next_data = next_tbl.(var_name);
        try
          var_data = [var_data; next_data(:)];
        catch
          error ("vertcat: cannot concatenate variable '%s'", var_name);
        end_try_catch
      else
        ## Missing variable - fill with NaN or empty
        ## Use size() directly instead of height() to avoid dependency issues
        num_rows = size (next_tbl, 1);
        if (isnumeric (var_data))
          var_data = [var_data; NaN(num_rows, 1)];
        else
          var_data = [var_data; cell(num_rows, 1)];
        endif
      endif
    endfor

    tbl.(var_name) = var_data;
  endfor

  ## Update Properties
  tbl.Properties.VariableNames = var_names;
  tbl.Properties.RowNames = {};  ## Clear row names on concatenation
  tbl.Properties.Description = varargin{1}.Properties.Description;
  
  ## Preserve VariableDescriptions and VariableUnits from first table
  if (isfield (varargin{1}.Properties, "VariableDescriptions"))
    tbl.Properties.VariableDescriptions = varargin{1}.Properties.VariableDescriptions;
  else
    tbl.Properties.VariableDescriptions = cell (1, numel (var_names));
  endif
  
  if (isfield (varargin{1}.Properties, "VariableUnits"))
    tbl.Properties.VariableUnits = varargin{1}.Properties.VariableUnits;
  else
    tbl.Properties.VariableUnits = cell (1, numel (var_names));
  endif
endfunction


