function write(self, varargin)
% self = write(self, varargin)
%  write method from netcdf class, create and write data to netcdf file
%  also call by save method
%
% % Input
% -----
% self        ........... netcdf object
% varargin{1} ........... netcdf file
% varargin{2} ........... file mode
% varargin{3} ........... waitbar (boolean)
%
% Output
% ------
% self        ........... netcd object
%
% $Id$

%% check arguments
% TODOS: modify and test
% ----------------------
wbool = false;
self.file = char(varargin{1});
if nargin > 2
  self.mode = char(varargin{2});
end
if nargin > 3
  if varargin{3}
    wbool = true;
  end
end

% get keys list of dimensions, variables and attributes from netcdf instance
% --------------------------------------------------------------------------
ncd_keys = keys( get(self.dynaload, 'DIMENSIONS'));
ncv_keys = keys( get(self.dynaload, 'VARIABLES'));
nca_keys = keys( get(self.dynaload, 'ATTRIBUTES'));

% display more info about write file on console
% ---------------------------------------------
fprintf('\nWRITE_NETCDF_FILE\n'); tic;
fprintf('...writing %s : ', self.file);

% Initialize loading NetCDF file waitbar
% --------------------------------------
if wbool
  wb = waitbar(0, 'Initializing NetCDF file. Please  wait...');
  
  % We cannot change title property to 'none' (default set as 'tex') inside
  % waitbar function (line 81), see
  % http://www.mathworks.co.uk/matlabcentral/newsreader/view_thread/115725
  % -----------------------------------------------------------------------
  hchild = get(wb,'children');
  htitle = get(hchild,'title');
  set(htitle,'Interpreter','None');
end

%% Open or create netcdf file
% -------------------------
switch(self.mode)
  case {'NC_CLOBBER'}
    self.nc_id = netcdf.create(self.file, self.mode);
  case 'NC_WRITE'
    self.nc_id = netcdf.open(self.file, self.mode);
  otherwise
end


%% Create NetCDF DIMENSIONS
% -------------------------
for i=1:numel(ncd_keys)
  key__ = ncd_keys{i};
  s = get(get(self.dynaload, 'DIMENSIONS'), key__);
  %value = s.value;
  dimlen = s.dimlen;
  
  % % create unlimited dimension. Any unlimited dimension is therefore last
  % % in the list of dimension IDs in defVar function, todo below...
  % % ---------------------------------------------------------------------
  %   if s.unlimited
  %     unlimDimId = netcdf.defDim(self.nc_id, key__, ...
  %                                netcdf.getConstant('NC_UNLIMITED'));
  %   end
  
  % if dimension is not set, get size from variable
  % a faire evoluer car peu sur, et a voir pour tableaux
  % -----------------------------------------------
  if isempty(dimlen)
    t = get(get(self.dynaload, 'VARIABLES'), key__);
    if isfield(t, 'data__')
      netcdf.defDim(self.nc_id, key__, numel(t.data__));
    end
  else
    try
      netcdf.defDim(self.nc_id, key__, dimlen);
    catch ME
      error('\nError: netcdf.%s (line %d), nc_id: %d, key__: %s dimlen: %d\n\tin function call: %s (%s)',...
        ME.stack(2).name, ME.stack(2).line, self.nc_id, key__, dimlen, ME.stack(1).name, ME.message);
    end
  end
  
end

% display filename after 'Interpreter','None' initialization to prevent
% display console warning
% ---------------------------------------------------------------------
if wbool
  waitbar(1/50,wb, ['Writing file: ' self.file ' Please  wait...']);
end

%% Create and write NetCDF GLOBAL ATTRIBUTES
% -----------------------------------

% get netcdf global attribute constant
% ------------------------------------
nga = self.NC_GLOBAL;
%nga = netcdf.getConstant('NC_GLOBAL');

for i=1:numel(nca_keys)
  key = nca_keys{i};
  s = get(get(self.dynaload, 'ATTRIBUTES'), key);
  %value = s.data__;
  if isfield(s, 'data__')
    
    % Create an attribute associated with the variable.
    % -------------------------------------------------
    try
      netcdf.putAtt(self.nc_id, nga, key, s.data__);
    catch ME
      fprintf('\nError: netcdf.%s (line %d)\n\tin function call: %s (%s)',...
        ME.stack(2).name, ME.stack(2).line, ME.stack(1).name, ME.message);
    end
  end
end

% Leave define mode.
% ------------------
netcdf.endDef(self.nc_id)



%% Adds NetCDF VARIABLES
% ----------------------
for i=1:numel(ncv_keys)
  
  % Re-enter define mode.
  % ---------------------
  netcdf.reDef(self.nc_id);
  
  % initialise varstruct
  % nc_addvar use a structure with four fields:
  % Name
  % Nctype
  % Dimension
  % Attribute is also a structure array
  % see help nc_addvar
  % -----------------------------------
  %varstruct = [];
  
  
  % get variable name
  % -----------------
  key  = ncv_keys{i};
  
  % display waitbar
  % ---------------
  if wbool
    waitbar( i/numel(ncv_keys), wb, ['writing ' key ' variable']);
  end
  
  % uncomment for debug only, display variable
  % ------------------------------------------
  % key
  
  % get structure
  % -------------
  s = get(get(self.dynaload, 'VARIABLES'), key);
  
  % get dimension(s) dimids from ncetcdf file, s.dimension is cell
  % --------------------------------------------------------------
  %dimids = [];
  
  % utiliser numel(s.dimension__) et dimids(i) pour creer le tableau des
  % dimids
  if ~isfield(s, 'dimension__')
    s %#ok<NOPRT>
    error('field dimension__ not defined in variables structure ...');
  end
  
  try
    % preallocate dimids
    % ------------------
    dimids = zeros(size(s.dimension__));
    
    % fill dimids array from dim name
    % -------------------------------
    for ind=1:length(dimids)
      dimid = netcdf.inqDimID(self.nc_id, s.dimension__{ind});
      dimids(ind) = dimid;
    end
    
    % find index of unlimited dimension
    % ---------------------------------
    %ind = find(dimids == unlimDimId);
    
    % reshape dimids array with unlimited  value at the start
    % -------------------------------------------------------
    %     if ~isempty(ind)
    %       dimids = [ dimids(1:ind-1), circshift(dimids(ind+1:end),[0 -1])];
    %       dimids = [circshift(dimids(1:ind),[0 1]), dimids(ind+1:end)];
    %     end
    
  catch ME
    fprintf('\nNotice: netcdf.%s, dimension %s don''t exist or is empty',...
      ME.stack(2).name, s.dimension__{ind});
    
    % Leave define mode and go to next variable (key__)
    % -------------------------------------------------
    netcdf.endDef(self.nc_id);
    continue;
  end
  
  % We need to flip the dimensions
  % ------------------------------
  dimids = fliplr(dimids);
  %netcdf.reDef(self.nc_id);
  
  % create variable and get it's varid
  % ----------------------------------
  varid = netcdf.defVar(self.nc_id, key, s.type__, dimids);
  
  % dynamically reate variable attributes
  % get structure fieldnames from hashtable
  % ---------------------------------------
  ncv_fieldNames  = fieldnames(s);
  
  % loop over fieldname (variable attributes), first is key (code)
  % --------------------------------------------------------------
  for j=1:numel(ncv_fieldNames)
    
    % get fielname and value
    % ----------------------
    fieldName = ncv_fieldNames{j};
    value = s.(fieldName);
    
    % jump each internal fieldname (eg key__, data__, type__, dimension__)
    % or empty fieldname
    % -------------------------------------------------------------
    if ~isempty(regexp(fieldName, '__$','ONCE')) || isempty(value)
      continue;
    end
    
    % change struct fieldname FillValue_ to attribute _FillValue
    % ----------------------------------------------------------
    if strcmp( fieldName, 'FillValue_')
      fieldName = '_FillValue';
    end
    
    % BE CARREFULL: it's an very important notice
    % value assign to attribute _FillValue should have same type
    % as variable, true for all numeric atribute variable
    % ------------------------------------------------------
    if isnumeric(value)
      switch s.type__
        case 'double'
          value = double(value);
        case 'float'
          value = single(value);
        case 'int'
          value = int32(value);
        case 'short'
          value = int16(value);
        case 'byte'
          value = int8(value);
        case 'char'
          value = char(value);
      end
    end
    
    % Write variable netCDF attribute
    % -------------------------------
    netcdf.putAtt(self.nc_id, varid, fieldName, value);
    
  end
  
  % Leave define mode and enter data mode to write data.
  % -----------------------------------------------------
  netcdf.endDef(self.nc_id);
  
  % if there is not data for variable or data__ empty
  % ------------------------------------------------
  if ~isfield(s, 'data__') || isempty(s.data__)
    
    % and it's missing_value attribute is set, fill data with missing_value
    % -------------------------------------------------------------------
    if isfield(s, 'missing_value') && ~isempty(s.missing_value)
      
      % get dimension id from name
      % --------------------------
      dimsize = numel(s.dimension__);
      
      % rajouter un test iscell
      
      % set dimids size array
      % ----------------------
      dimids = zeros(dimsize, 1);
      
      % fill dimids array with dimension(s) Id
      % --------------------------------------
      try
        for dim = 1: dimsize
          dimids(dim) = netcdf.inqDimID(self.nc_id, s.dimension__{dim});
        end
      catch ME
        msg = sprintf('dimension: %s don''t exist', s.dimension__{dim});
        fprintf('\nWarning: netcdf.%s (line %d)\n\tin function call: %s',...
          ME.stack(2).name, ME.stack(2).line, msg);
        continue;
      end
      
      % get size of pre-allocate dimsize
      % -------------------------------
      dimlen = zeros(dimsize, 1);
      
      % get dimension name and length from it's dimid
      % ---------------------------------------------
      for dim = 1: dimsize
        [dimname, dimlen(dim)] = netcdf.inqDim(self.nc_id, dimids(dim));
      end
      
      % fill data with missing_value
      % could use repmat to do that: s.data__ = repmat(1, dimlen, 1));
      % --------------------------------------------------------------
      if dimsize == 1
        s.data__ = ones(dimlen,1) * s.missing_value;
      else
        s.data__ = ones(dimlen) * s.missing_value;
      end
    else
      
      % if variable don't have attribute missing_value, exit loop and go to
      % next variable
      % -------------------------------------------------------------------
      continue;
    end
    
  end
  
  % BE CAREFULL:
  % cast variable with the right netcdf nctype
  % ------------------------------------------
  switch s.type__
    case 'double'
      value = double(s.data__);
    case 'float'
      value = single(s.data__);
    case 'int'
      value = int32(s.data__);
    case 'short'
      value = int16(s.data__);
    case 'byte'
      value = int8(s.data__);
    case 'char'
      value = char(s.data__);
  end
  
  % matlab use FORTRAN indexing (k,j,i) and lib netcdf come from C  style
  % indexing (i,j,k)
  % Matlab documentation is not very clear about this very important tips
  % see:
  % http://www.mathworks.com/access/helpdesk/help/techdoc/index.html?/access/helpdesk/help/techdoc/matlab_prog/brrbr9v-1.html&http://www.mathworks.fr/cgi-bin/texis/webinator/search/
  % -------------------------------------------
  value = permute(value, fliplr(1:ndims(value)));
  
  % If a NetCDF variable has valid scale_factor and add_offset
  % attributes, then the data is scaled accordingly.
  % ----------------------------------------------------------
  if self.autoscale
    if isfield(s, 'scale_factor') && isfield(s, 'add_offset')
      value = (value - s.add_offset) / s.scale_factor;
    end
  end
  
  % If a NetCDF variable has valid _FillValue attributes, then
  % the data is filled accordingly.
  % ----------------------------------------------------------
  if self.autonan && isfield(s, 'FillValue_') && ~isempty(s.FillValue_)
    value(isnan(value)) = s.FillValue_;
  end
  
  % Write data to variable.
  % -----------------------
  try
    netcdf.putVar(self.nc_id, varid, value);
  catch exception
    fprintf('write variable error: %s', s.key__');
    s %#ok<NOPRT>
    fprintf('check your dynaload csv or xls file !!!\n');
    error('Matla:netcdf.write:netcdf.putVar',...
      'Cannot write netcdf file\n%s.', exception.message);  %#ok<CTPCT>
    
  end
  
  % synchronize netcdf file to disk
  % -------------------------------
  netcdf.sync(self.nc_id);
  
end % end for ncv variables loop

% Close waitbar
% -------------
if wbool
  close(wb)
end

% Display time to write file on console
% -------------------------------------
t = toc; fprintf('...done (%6.2f sec).\n\n',t);

% close netcdf file
% -----------------
netcdf.close(self.nc_id)

end % end of write function
