const DEFAULT_SOURCE = 'FROM';
 
let sourceInstance = 0;
function sourceName(name) {
  return name || `${DEFAULT_SOURCE}${++sourceInstance}`;
}
 
function sourceControl(source) {
  return { source };
}
 
export default class Source {
  constructor(kernel, toSrc, name, fromSrc) {
    if (!kernel || !kernel._network) {
      throw new Error(
        'must provide `kernel` to attach the Source to a network'
      );
    }
    this._network = kernel._network;
    this.forwardAppCtx = (ac, control) => {
      kernel.forwardAppCtx(ac, control);
    };
    if (!toSrc) {
      throw new Error('must provide `toSrc` way to send ACs to the source');
    }
    if (typeof name === 'function') {
      fromSrc = name;
      name = null;
    }
    // Make fromSrc optional for binding a handler
    // if not passed it is a function exposed by the Source i.e. setCtx
    if (fromSrc) {
      Iif (typeof fromSrc !== 'function') {
        throw new Error('optional `fromSrc` must be a function');
      }
      fromSrc((tao, data) =>
        this._network.setCtxControl(
          tao,
          data,
          sourceControl(this.name),
          this.forwardAppCtx
        )
      );
    }
    this._toSrc = toSrc;
    this._name = sourceName(name);
    this._middleware = (handler, ac, fwd, control) =>
      this.handleAppCon(handler, ac, fwd, control);
    this._network.use(this._middleware);
  }
 
  get name() {
    return this._name;
  }
 
  handleAppCon = (handler, ac, forwardAppCtx, control) => {
    if (!control || control.source !== this.name) {
      this._toSrc(ac.unwrapCtx(), ac.data);
    }
  };
 
  setCtx = ({ t, term, a, action, o, orient }, data) => {
    this._network.setCtxControl(
      { t, term, a, action, o, orient },
      data,
      sourceControl(this.name),
      this.forwardAppCtx
    );
  };
 
  dispose() {
    this._network.stop(this._middleware);
  }
}
  |