% Matrix Generator Parameters
% mgp([shot])

classdef mgp < mgc
 
 properties (Hidden, SetAccess = protected)
  NAME
  EQDP
  DIM
  DEF
  VAL
 end
 
 methods
  
  function M = mgp(shot)
   % mgp([shot])
   mgc.mdso(-1,'r');
   M.NAME = lower(deblank(mdsdata('GETNCI("\\MGAMS.DATA:*","NODE_NAME")')));
   M.EQDP = logical(mdsdata('GETNCI("\\MGAMS.DATA:*:EQDEP","RECORD")'))';
   M.DIM  = mdsdata('GETNCI("\\MGAMS.DATA:*:SIZE","RECORD")')';
   nvar = numel(M.NAME);
   for k = 1:nvar
    p = addprop(M,M.NAME{k});
    p.SetAccess = 'public';
    p.SetMethod = @(h,x)set(h,x,k);
    p.GetMethod = @(h)get(h,k);
    [M.DEF.(M.NAME{k}),M.VAL.(M.NAME{k})] = deal(mdsr(sprintf('\\MGAMS.DATA:%s:SIZE>0 ? \\MGAMS.DATA:%s:DEFAULT : (\\MGAMS.DATA:%s:EQDEP ? $ROPRAND : *)',M.NAME{[k k k]})));
   end
   for ii = 1:9 % 9 debug nodes
    k = nvar+ii;
    M.NAME{k} = sprintf('debug_p%1d',ii);
    M.EQDP(k) = false;
    M.DIM(k) = 0;
    p = addprop(M,M.NAME{k});
    p.SetAccess = 'public';
    p.SetMethod = @(h,x)set(h,x,k);
    p.GetMethod = @(h)get(h,k);
    [M.DEF.(M.NAME{k}),M.VAL.(M.NAME{k})] = deal('');
   end
   if nargin, M.load(shot); end
  end
  
  function y = ind(M,p)
   % mgp.ind(k|[k1,...]|par|{par1,...})
   if nargin == 1
    y = [];
   elseif isnumeric(p)
    y = p;
   elseif ischar(p)
    y = find(strcmp(p,M.NAME));
   elseif iscellstr(p)
    y = zeros(size(p));
    for k = 1:numel(y)
     tmp  = find(strcmp(p{k},M.NAME));
     % Answer 0 if there is no match
     if ~isempty(tmp), y(k) = tmp;end
    end
   else
    error('MatrixGenerator:mgp:ind','Argument must be numeric, string or cell string')
   end
  end
  
  function y = name(M,varargin)
   % mgp.name[(k|[k1,...]|par|{par1,...})]
   y = prop(M,'NAME',varargin{:});
  end
  
  function y = eqdp(M,varargin)
   % mgp.eqdp[(k|[k1,...]|par|{par1,...})]
   y = prop(M,'EQDP',varargin{:});
  end
  
  function y = dim(M,varargin)
   % mgp.dim[(k|[k1,...]|par|{par1,...})]
   y = prop(M,'DIM',varargin{:});
  end
  
  function y = size(M)
   % mgp.size
   y = numel(M.NAME);
  end
  
  function set(M,x,k,keq)
   % mgp.set(x,k|name[,keq])
   k = M.ind(k); eqdp = M.EQDP(k); dim = M.DIM(k); name = M.NAME{k};
   if isfield(M.VAL,name)
    X = M.VAL.(name);
   else
    X = [];
   end
   if eqdp
    if dim == 1
     x = x(:)';
    elseif nargin == 4 && numel(keq) == 1 
     x = x(:);
    end
    if dim
     x(dim+1:end,:) = [];
     x(end+1:dim,:) = repmat(M.DEF.(name)(size(x,1)+1:end),1,size(x,2));
     [i,j] = find(isnan(x)); x(sub2ind(size(x),i,j)) = M.DEF.(name)(i);
    else
     if     size(X,1) > size(x,1)
      x(end+1:size(X,1),:) = NaN;
     elseif size(X,1) < size(x,1)
      X(end+1:size(x,1),:) = NaN;
     end
    end
    if nargin == 4
     assert(numel(keq) == size(x,2),'Equilibrium assignment dimension mismatch')
     X(:,keq) = x; x = X;
    end
   else
    x = x(:)';
    if dim
     x(dim+1:end) = [];
     x(end+1:dim) = M.DEF.(name)(numel(x)+1:end);
     i = isnan(x); x(i) = M.DEF.(name)(i);
    end
   end
   i = all(isnan(x),2); x(i(1:end-1),:) = [];
   M.VAL.(name) = x;
  end
  
  function y = get(M,k)
   y = M.VAL.(M.NAME{k});
  end
  
  function y = def(M,k)
   y = M.DEF.(M.name(k));
  end
  
  function load(M,shot,mode)
   % load(shot[,'numeq'])
   mgp.mdso(shot,'r');
   for k = 1:size(M)-9 % 9 debug nodes
    M.(M.NAME{k}) = mdsr(sprintf('IF_ERROR( \\MGAMS.DATA:%s , \\MGAMS.DATA:%s:CALC , * )',M.NAME{[k k]}));
   end
   for ii = 1:9
    k = k+1;
    M.(M.NAME{k}) = mdsr(sprintf('IF_ERROR( \\MGAMS.DEBUG:P%1d , "" )',ii));
   end
   if nargin > 2 && strcmp(mode,'numeq')
    for k = find(M.EQDP)
     M.(M.NAME{k})(:,M.numeq+1:end) = [];
    end
   end
  end
  
  function save(M,shot)
   % mgp.save(shot)
   mgp.mdso(shot,'w')
   for k = 1:size(M)-9 % 9 debug nodes
    X = M.(M.NAME{k});
    if M.EQDP(k) && M.DIM(k) ~= 1
     s = mdsput(M.NAME{k},'set_range(long($1),long($2),$3)','x',size(X,1),size(X,2),X);
    else
     s = mdsput(M.NAME{k},X);
    end
    assert(logical(rem(s,2)),'MatrixGenerator:mgp:save','Cannot write node %s',M.NAME{k})
   end
   for ii = 1:9
    k = k+1;
    X = M.(M.NAME{k});
    s = mdsput(sprintf('\\MGAMS.DEBUG:P%1d',ii),X);
    assert(logical(rem(s,2)),'MatrixGenerator:mgp:save','Cannot write node DEBUG:P%1d',ii)
   end
  end
  
  function y = format(M,par,keq)
   % mgp.format(k|name[,eq#])
   if ~isempty(par)
    n = numel(par);
    y(1:n) = {''};
    % Eliminate incorrect indices/names
    k = M.ind(par);
    mask = k>0;
    k = k(mask);
    nk = numel(k);
    % Temporary results array
    ytmp(1:nk) = {''};
    % NOTE: since we already checked k, we could use M.NAME, M.EQDP
    name = M.name(k);
    eqdp = M.eqdp(k);
    if nk==1, name = {name};end
    for ii = 1:nk
     x = M.(name{ii}); if eqdp(ii), x = x(:,keq); end
     if ischar(x)
      ytmp{ii} = x;
     else
      if any(strcmp(name{ii},{'rmajo1' 'zmajo1' 'rmino1' 'rlim1' 'zlim1' 'rlia1' 'zlia1' 'rbro' 'zbro' 'rbzo' 'zbzo'}))
       h = '%+6.3f';
      elseif any(strcmp(name{ii},{'zeecorr'}))
       h = '%.5g';
      else
       h = '%.4g';
      end
      x = x(~isnan(x));
      if numel(x) > 1
       ytmp{ii} = sprintf([h ','],x); ytmp{ii} = ['[' ytmp{ii}(1:end-1) ']'];
      elseif isscalar(x)
       ytmp{ii} = sprintf(h,x);
      end
     end
    end
    y(mask) = ytmp;
   else
    y = '';
   end
  end
  
  function fix(M)
   [~,n] = check(M);
   numeq = max(n);
   for k = find(n >= 0 & n ~= numeq)
    if M.DIM(k)
     M.(M.NAME{k}) = repmat(M.DEF.(M.NAME{k}),1,numeq);
    else
     M.(M.NAME{k}) = NaN(1,numeq);
    end
   end
  end
  
  function [s,n] = check(M)
   n = repmat(-Inf,1,size(M));
   for k = find(M.EQDP)
    [i,j] = size(M.VAL.(M.NAME{k}));
    n(k) = (i > 0) * j;
   end
   s = isempty(find(n >= 0 & n ~= max(n),1));  
  end
 
  function M = single(M)
   for n = M.name.'
    M.(n{1}) = single(M.(n{1}));
   end
  end
 
  function M = fix_int(M)
   for n = M.name.'
    if ismember(n{1}(1),{'i','j','k','l','m','n'})
     M.(n{1}) = fix(M.(n{1}));
    end
   end
  end
  
  function S = debug_struct(M,ii)
   % Evaluates debug node strings into structures
   
   node = sprintf('debug_p%1d',ii);
   try % Syntax may not work
    S = eval(['struct(',M.(node),')']);
   catch ME
    error('mgp:debug_syntax','Wrong syntax for converting %s to struct:\n%s',node,getReport(ME));
   end
   
   fields = fieldnames(S);
   for ii = 1:numel(fields)
     assert(isempty(M.ind(fields{ii})),'Field %s is a valid MGAMS parameter, remove it from %s', fields{ii},node);
   end
  end
  
  function S = debug_cell(M,ii)
   % Evaluates debug node strings into cell arrays
   
   node = sprintf('debug_p%1d',ii);
   try % Syntax may not work
    S = eval(['{',M.(node),'}']);
   catch ME
    error('mgp:debug_syntax','Wrong syntax for converting %s to cell:\n%s',node,getReport(ME));
   end
  end

  function S = struct(M)
  % Converts mgp object to structure
   
    S = M.VAL;

  end
  
  function S = struct_with_debug(M,idebug)
  % Converts mgp object to structure and add debug fields
  %
  % Examples:
  %   S = struct_with_debug(MG,2); 
    
    S = struct(M);
    
    % Add debug fields if necessary
    if nargin>1
      P = debug_struct(M,idebug);
      % Check for duplicate fields has already been performed
      fields = fieldnames(P);
      for ii = 1:numel(fields)
        field = fields{ii};
        S.(field) = P.(field);
      end
    end
  end
  
 end % methods
 
end % classdef

function y = prop(M,P,p)
 if nargin == 2
  y = M.(P);
 else
  p = M.ind(p);
  if numel(p) == 1 && iscell(M.(P))
   y = M.(P){p};
  else
   y = M.(P)(p);
  end
 end
end

function x = mdsr(e)
 [x,s] = mdsdata(e);
 assert(logical(rem(s,2)),'MatrixGenerator:mgp:mdsr','Cannot evaluate MDS expression %s',e)
end
