function savemsh(node,elem,fname,rname)
%
% savemsh(node,elem,fname,rname)
%
% save a tetrahedral mesh to GMSH mesh format
%
% author: Riccardo Scorretti (riccardo.scorretti<at> univ-lyon1.fr)
% date: 2013/07/22
%
% input:
%      node: input, node list, dimension (nn,3)
%      elem: input, tetrahedral mesh element list, dimension (ne,4) or (ne,5) for multi-region meshes
%      fname: output file name
%      rname: name of the regions, cell-array of strings (optional)
%
% -- this function is part of iso2mesh toolbox (http://iso2mesh.sf.net)
%

if nargin < 4 , rname = {} ; end
if size(elem,2) < 5 , elem(:,5) = 1 ; end
fid = fopen(fname,'wt');
if(fid==-1)
    error('You do not have permission to save mesh files.');
end

% Check that all the elements are correctly oriented
elem(:,1:4)=meshreorient(node,elem(:,1:4));

nbNodes = size (node,1);
reg = unique (elem(:,5));
reg(reg<=0)=max(reg)+1-reg(reg<=0); % convert label 0 to max(reg)+1, -1 to max(reg)+2, and so on
nbRegion = length (reg);
nbElements = size (elem, 1);

% Create the skeleton of the mesh structure
M.Info.version = [];

M.Nodes.nb = 0;
M.Nodes.x = [];
M.Nodes.y = [];
M.Nodes.z = [];

M.Elements.nb = 0;
M.Elements.type = zeros (0, 0, 'uint8');
M.Elements.tableOfNodes = zeros (0, 0, 'uint32');
M.Elements.region = zeros (0, 0, 'uint16');

M.Regions.nb = 0;
M.Regions.name = {};
M.Regions.dimension = [];

% Build the table of nodes

M.Nodes.nb = nbNodes;
M.Nodes.x = node(:,1);
M.Nodes.y = node(:,2);
M.Nodes.z = node(:,3);
clear node

% Build the table of elements

M.Elements.nb = nbElements;
M.Elements.type = uint8(4*ones(nbElements, 1));
M.Elements.tableOfNodes = uint32(elem(:,1:4));
M.Elements.region = uint16(elem(:,5));
clear elem

% Build the table of regions

M.Regions.nb = max(reg);
for k = 1 : nbRegion
   if length(rname) < k , rname{k} = sprintf('region_%d', k) ; end
   M.Regions.name{reg(k)} = sprintf ('%s', rname{k});
   M.Regions.dimension(reg(k)) = 3;
end

% Writhe the header
fprintf (fid, '$MeshFormat\n2.2 0 8\n$EndMeshFormat\n');

% Write the physical names
if M.Regions.nb > 0 
   fprintf (fid, '$PhysicalNames\n');
   fprintf (fid, '%d\n', M.Regions.nb);
   for r = 1 : M.Regions.nb
      name = M.Regions.name{r};
      if isempty (name)
         name = sprintf ('Region_%d', r);
      end
      fprintf (fid, '%d %d "%s"\n', M.Regions.dimension(r), r, name);
   end
   fprintf (fid, '$EndPhysicalNames\n');
end

% Write the nodes
fprintf (fid, '$Nodes\n');
fprintf (fid, '%d\n', size(M.Nodes.x,1));
buffer = [ 1:M.Nodes.nb ; M.Nodes.x' ; M.Nodes.y' ; M.Nodes.z' ];
fprintf (fid, '%d %10.10f %10.10f %10.10f\n', buffer);
fprintf (fid, '$EndNodes\n');

% Write the elements
%
% In order to accelerate the printing, the elements are printed in groups of (blockSize) elements, and
% are grouped by (homogeneous) type. This variable sets the size of each group.
%
blockSize = 100000;

fprintf (fid, '$Elements\n');
fprintf (fid, '%d\n', M.Elements.nb);
for h = 1 : blockSize : ceil(length(M.Elements.type)/blockSize)*blockSize
   e = h : min(length(M.Elements.type) , h+blockSize-1);  % = elements being considered
   type = unique (M.Elements.type(e));                    % = types of elements found in this group
   
   %
   % Process each type of element separately
   %
   for k = 1 : length(type)
      if type(k) == 0, continue; end
      et = e(find(M.Elements.type(e) == type(k)));  % = elements of the group of the same type
      
      %
      % Determine the format for printing the elements
      %
      elementFormat = '%d %d %d %d %d %d\n';
      for n = 1 : 4
         elementFormat = [ elementFormat '%d ' ];
      end
      elementFormat = [ elementFormat '\n' ];
      
      %
      % Collect in a buffer all the data of the elements of index (et)
      %
      buffer = zeros (10 , length(et));
      buffer(1,:) = et;
      buffer(2,:) = type(k);
      buffer(3,:) = 3;
      buffer(4,:) = M.Elements.region(et);
      buffer(5,:) = M.Elements.region(et);
      buffer(6,:) = 0;
      for n = 1 : 4
         buffer(6+n,:) = M.Elements.tableOfNodes(et,n);
      end
      
      %
      % Print all the homogeneous elements in the group with a single instruction
      %
      fprintf (fid, elementFormat, buffer);
   end
end
fprintf (fid, '$EndElements\n');


fclose(fid);
