% Add a link between two optickle nodes.
%
% Arguments:
% opt - the optickle model
% snFrom - the serial number or name of the source optic (field origin)
% nameOut - the number or name of the output port (1:4, or {fr, bk, pi, po})
% snTo - the serial number or name of the sink optic (fiend destination)
% nameIn - the number or name of the input port (1:2 or {fr, bk} or {HR, AR})
% len - the length of the link
% n, m - output, input field indices
%
% The serial numbers of the optics (for snFrom and snTo) are
% returned by addOptic.  The name of the optic can also be used.
%
% The input/output ports of an optic depend on the type of optic.
% See dispPortNames for more information.
%
% opt = addLink(opt, snFrom, nameOut, snTo, nameIn, dist)

function opt = addLink(opt, snFrom, nameOut, snTo, nameIn, len)

  % check/parse field source
  snFrom = getSerialNum(opt, snFrom);
  portFrom = getPortNum(opt, nameOut, opt.optic(snFrom).outNames);

  sn = opt.optic(snFrom).out(portFrom);
  if sn ~= 0
    error('Unavailable Source: %s already linked to %s', ...
          getSourceName(opt, sn), getSinkName(opt, sn));
  end

  % check/parse field sink
  snTo = getSerialNum(opt, snTo);
  portTo = getPortNum(opt, nameIn, opt.optic(snTo).inNames);
  
  sn = opt.optic(snTo).in(portTo);
  if sn ~= 0
    error('Unavailable Sink: %s already linked to %s', ...
          getSourceName(opt, sn), getSinkName(opt, sn));
  end

  % create new link
  snLink = opt.Nlink + 1;    % link serial number
  newLink = struct('sn', snLink, 'len', len, ...
                   'snSource', snFrom, 'portSource', portFrom, ...
                   'snSink', snTo, 'portSink', portTo);

  % add new link to optical model
  opt.link(snLink, 1) = newLink;
  opt.Nlink = snLink;

  % link the optics
  opt.optic(snFrom).out(portFrom) = snLink;
  opt.optic(snTo).in(portTo) = snLink;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% modal stuff for next version
function modalStuff

  % update sink output basis
  qx = opt.optic(snFrom).qx(portFrom);
  qy = opt.optic(snFrom).qy(portFrom);
  if qx == 0 | qy == 0
    error('%s basis not yet defined.  Try linking an input to %s.', ...
          getOutputName(opt, snFrom, portFrom), opt.optic(snFrom).name);
  end

  qm = opt.optic(snTo).qm(:, portTo);
  for n = 1:opt.optic(snTo).Nout
    % x-axis
    if ~isempty(qm(n).x)
      oldQX = opt.optic(snTo).qx(n);
      newQX = applyOpHG(qm(n).x, qx + len);
      if oldQX == 0
        opt.optic(snTo).qx(n) = newQX;
      elseif abs(newQX - oldQX) / abs(oldQX) > 0.01
        warning('Basis badly matched qa = (%f, %f), qb = (%f, %f)', ...
                real(oldQX), imag(oldQX), real(newQX), imag(newQX))
      end
    end
  
    % y-axis
    if opt.optic(snTo).qy(n) == 0 & ~isempty(qm(n).y)
      opt.optic(snTo).qy(n) = applyOpHG(qm(n).y, qy + len);
    end
  end
