% FROMWAVEFORM Construct an ao from a waveform
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% FUNCTION:    fromWaveform
%
% DESCRIPTION: Construct an ao from a waveform
%
% CALL:        a = fromWaveform(pl)
%
% PARAMETER:   pl: Parameter list object
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function a = fromWaveform(a, pli, callerIsMethod)
  
  VERSION = '$Id: fromWaveform.m,v 1.44 2011/08/15 10:42:38 hewitson Exp $';
  
  if callerIsMethod
    % do nothing
  else
    % get AO info
    ii = ao.getInfo('ao', 'From Waveform');
    
    % Set the method version string in the minfo object
    ii.setMversion([VERSION '-->' ii.mversion]);
  end
  
  if callerIsMethod
    pl        = pli;
  else
    % Combine input plist with default values
    pl = applyDefaults(ii.plists, pli);
  end
  
  nsecs   = find(pl, 'nsecs');
  fs      = find(pl, 'fs');
  t0      = find(pl, 't0');
  
  waveform = find(pl, 'waveform');
  if numel(nsecs) == 1
    if isempty(nsecs) || nsecs == 0
      error('### Please provide ''Nsecs'' for waveform constructor.');
    end
  end
  if  isempty(fs) || fs == 0
    error('### Please provide ''fs'' for waveform constructor.');
  end
  
  % Override defaults
  if isempty(t0)
    t0 = time(0);
  elseif ischar(t0) || isnumeric(t0)
    t0 = time(t0);
  end
  if isempty(find(pl, 'name'))
    pl.pset('Name', waveform);
  end
  
  
  switch lower(waveform)
    %------------ Sine Wave
    case {'sine wave', 'sinewave', 'sine-wave', 'sine'}
      ampl = find(pl, 'A');
      freq = find(pl, 'f');
      phi  = find(pl, 'phi');
      Toff = find(pl, 'Toff');
      gaps = find(pl, 'gaps');
      
      % If Toff is a time-string or a time object then convert this time
      % into a number of seconds depending to T0
      if ischar(Toff) || iscell(Toff) || isa(Toff, 'plist')
        
        % If the t0 is the default value then set it to the first value of Toff
        if strcmpi(find(pl, 't0'), '1970-01-01 00:00:00.000')
          t0 = time(Toff(1));
        end
        
        newToff = [];
        if ischar(Toff)
          newToff = (time(Toff).utc_epoch_milli - t0.utc_epoch_milli) /1e3;
        else
          Toff = time(Toff);
          for zz = 1:numel(Toff)
            newToff = [newToff (Toff(zz).utc_epoch_milli - t0.utc_epoch_milli)/1e3];
          end
        end
        
        Toff = newToff;
      end
      
      % The user specified gaps instead of offsets.
      % (The gap is before the signal starts)
      if isempty(pli.find('Toff')) && ~isempty(gaps)
        Toff(1) = gaps(1);
        for ww = 2:numel(nsecs)
          Toff(ww) = sum(gaps(1:ww)) + sum(nsecs(1:ww-1));
        end
      end
      
      % If the number of  Amplitude, frequency and phase are not the same
      % then duplicate the last specified value.
      max_waves = max([numel(ampl), numel(freq), numel(phi), numel(nsecs), numel(Toff)]);
      
      ampl  = [ampl, repmat(ampl(end),   1, max_waves - numel(ampl))];
      freq  = [freq, repmat(freq(end),   1, max_waves - numel(freq))];
      phi   = [phi,  repmat(phi(end),    1, max_waves - numel(phi))];
      nsecs = [nsecs, repmat(nsecs(end), 1, max_waves - numel(nsecs))];
      Toff  = [Toff, repmat(Toff(end),   1, max_waves - numel(Toff))];
      
      %%%%%%%%%%%%%%%%%%%%   add different sine waves with time offset  %%%%%%%%%%%%%%%%%%%%
      
      maxValues = 0;
      % Predefine the total result with zeros
      for kk = 1:numel(Toff)
        values = (Toff(kk) + nsecs(kk)) * fs;
        if maxValues < values
          maxValues = values;
        end
      end
      total = zeros(maxValues, 1);
      
      % Over all sine waves
      for kk = 1:numel(Toff)
        
        % Compute the y-values of each sine wave
        t = 0 : 1/fs : nsecs(kk)-1/fs;
        y = ampl(kk) * sin(2*pi*freq(kk)*t + phi(kk));
        
        % Add the computed values to the total result
        begT = floor(Toff(kk)*fs);
        endT = begT + nsecs(kk)*fs;
        idx  = begT+1:endT;
        
        total(idx) = total(idx) + y.';
        
      end
      
      if isempty(total)
        error('### You have defined a sine-wave with the length zero.');
      end
      
      ts = tsdata(total, fs);
      ts.setXunits(find(pl, 'xunits'));
      ts.setYunits(find(pl, 'yunits'));
      ts.setT0(t0);
      
      % Make an analysis object
      a.data = ts;
      if callerIsMethod
        % do nothing
        % At the moment, we keep adding the history.
        % There might be something we need for rebuilding, like RAND_STREAM
        a.addHistory(ii, pl, [], []);
      else
        % Add history
        a.addHistory(ii, pl, [], []);
      end
      
      % Set some procedure information
      a.procinfo = plist('start times', time(t0.utc_epoch_milli/1e3 + Toff));
      
      % Set the object properties from the plist
      a.setObjectProperties(pl);
      
      % This is a special case where we don't evaluate a string function
      % but build the values according to the recipe. As such we have
      % already handled the setting of properties and history and we can
      % return here.
      return
      
    case 'noise'
      ntype = find(pl, 'type');
      pl.getSetRandState();
      
      if isempty(ntype)
        ntype = 'Normal';
      end
      sigma = find(pl, 'sigma');
      if isempty(sigma)
        sigma = 1;
      end
      
      switch lower(ntype)
        case 'normal'
          tsfcn = sprintf('%g.*randn(size(t))', sigma);
          
        case 'uniform'
          tsfcn = sprintf('%g.*rand(size(t))', sigma);
          
      end
      %------------ Chirp
    case 'chirp'
      f0  = find(pl, 'f0');
      fe  = find(pl, 'f1');
      te  = find(pl, 't1');
      if isempty(f0)
        f0 = 0;
      end
      if isempty(fe)
        fe = fs/2;
      end
      if isempty(te)
        te = nsecs;
      end
      tsfcn = sprintf('chirp(t,%g,%g,%g)', f0, fe, te);
      %------------ Gaussian pulse
    case {'gaussian pulse', 'gaussian-pulse'}
      fc  = find(pl, 'f0');
      bw  = find(pl, 'bw');
      if isempty(fc)
        fc = 1;
      end
      if isempty(bw)
        bw = fs/2;
      end
      tsfcn = sprintf('gauspuls(t,%g,%g)', fc, bw);
    case {'square wave', 'squarewave', 'square-wave', 'square'}
      freq = find(pl, 'f');
      duty = find(pl, 'duty');
      if isempty(freq)
        freq = 1;
      end
      if isempty(duty)
        duty = 50;
      end
      tsfcn = sprintf('square(2*pi*%g*t,%g)', freq, duty);
    case {'saw tooth', 'sawtooth', 'saw-tooth', 'saw'}
      freq  = find(pl, 'f');
      width = find(pl, 'width');
      if isempty(freq)
        freq = 1;
      end
      if isempty(width)
        width = 0.5;
      end
      tsfcn = sprintf('sawtooth(2*pi*%g*t,%g)', freq, width);
    otherwise
        error('### Unknown waveform type [%s]', waveform);
  end
  
  % construct tsdata
  t = 0 : 1/fs : nsecs-1/fs; % t = linspace(0, nsecs(kk)-1/fs, nsecs(kk)*fs);
  % make y data
  y = eval([tsfcn ';']);
  
  ts = tsdata(t, y');
  ts.setT0(t0);
  ts.setXunits(find(pl, 'xunits'));
  ts.setYunits(find(pl, 'yunits'));
  
  % Make an analysis object
  a.data = ts;
  
  if callerIsMethod
    % do nothing
    % At the moment, we keep adding the history.
    % There might be something we need for rebuilding, like RAND_STREAM
    a.addHistory(ii, pl, [], []);
  else
    % Add history
    a.addHistory(ii, pl, [], []);
  end
    
  % Set the object properties from the plist
  a.setObjectProperties(pl);
  
end

