import { m as macro } from '../../macros2.js';
import vtkCell from './Cell.js';

// ----------------------------------------------------------------------------
// Global methods
// ----------------------------------------------------------------------------
const InitLink = {
  ncells: 0,
  cells: null
};
function resize(model, sz) {
  let newSize = sz;
  if (sz >= model.array.length) {
    newSize += model.array.length;
  }
  while (newSize > model.array.length) model.array.push({
    ncells: 0,
    cells: null
  });
  model.array.length = newSize;
}

// ----------------------------------------------------------------------------
// vtkCellLinks methods
// ----------------------------------------------------------------------------

function vtkCellLinks(publicAPI, model) {
  // Set our className
  model.classHierarchy.push('vtkCellLinks');

  /**
   * Build the link list array. All subclasses of vtkAbstractCellLinks
   * must support this method.
   */
  publicAPI.buildLinks = data => {
    const numPts = data.getPoints().getNumberOfPoints();
    const numCells = data.getNumberOfCells();

    // fill out lists with number of references to cells
    const linkLoc = new Uint32Array(numPts);

    // Use fast path if polydata
    if (data.isA('vtkPolyData')) {
      // traverse data to determine number of uses of each point
      for (let cellId = 0; cellId < numCells; ++cellId) {
        const {
          cellPointIds
        } = data.getCellPoints(cellId);
        cellPointIds.forEach(cellPointId => {
          publicAPI.incrementLinkCount(cellPointId);
        });
      }

      // now allocate storage for the links
      publicAPI.allocateLinks(numPts);
      model.maxId = numPts - 1;
      for (let cellId = 0; cellId < numCells; ++cellId) {
        const {
          cellPointIds
        } = data.getCellPoints(cellId);
        cellPointIds.forEach(cellPointId => {
          publicAPI.insertCellReference(cellPointId, linkLoc[cellPointId]++, cellId);
        });
      }
    } // any other type of dataset
    else {
      // traverse data to determine number of uses of each point
      for (let cellId = 0; cellId < numCells; cellId++) {
        // TODO: Currently not supported: const cell = data.getCell(cellId);
        const cell = vtkCell.newInstance();
        cell.getPointsIds().forEach(cellPointId => {
          publicAPI.incrementLinkCount(cellPointId);
        });
      }

      // now allocate storage for the links
      publicAPI.allocateLinks(numPts);
      model.maxId = numPts - 1;
      for (let cellId = 0; cellId < numCells; ++cellId) {
        // TODO: Currently not supported: const cell = data.getCell(cellId);
        const cell = vtkCell.newInstance();
        cell.getPointsIds().forEach(cellPointId => {
          publicAPI.insertCellReference(cellPointId, linkLoc[cellPointId]++, cellId);
        });
      }
    } // end else
  };

  /**
   * Build the link list array with a provided connectivity array.
   */
  // publicAPI.buildLinks = (data, connectivity) => {};

  /**
   * Allocate the specified number of links (i.e., number of points) that
   * will be built.
   */
  publicAPI.allocate = function (numLinks) {
    let ext = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1000;
    model.array = Array(numLinks).fill().map(() => ({
      ncells: 0,
      cells: null
    }));
    model.extend = ext;
    model.maxId = -1;
  };
  publicAPI.initialize = () => {
    model.array = null;
  };

  /**
   * Get a link structure given a point id.
   */
  publicAPI.getLink = ptId => model.array[ptId];

  /**
   * Get the number of cells using the point specified by ptId.
   */
  publicAPI.getNcells = ptId => model.array[ptId].ncells;

  /**
   * Return a list of cell ids using the point.
   */
  publicAPI.getCells = ptId => model.array[ptId].cells;

  /**
   * Insert a new point into the cell-links data structure. The size parameter
   * is the initial size of the list.
   */
  publicAPI.insertNextPoint = numLinks => {
    model.array.push({
      ncells: numLinks,
      cells: Array(numLinks)
    });
    ++model.maxId;
  };

  /**
   * Insert a cell id into the list of cells (at the end) using the cell id
   * provided. (Make sure to extend the link list (if necessary) using the
   * method resizeCellList().)
   */
  publicAPI.insertNextCellReference = (ptId, cellId) => {
    model.array[ptId].cells[model.array[ptId].ncells++] = cellId;
  };

  /**
   * Delete point (and storage) by destroying links to using cells.
   */
  publicAPI.deletePoint = ptId => {
    model.array[ptId].ncells = 0;
    model.array[ptId].cells = null;
  };

  /**
   * Delete the reference to the cell (cellId) from the point (ptId). This
   * removes the reference to the cellId from the cell list, but does not
   * resize the list (recover memory with resizeCellList(), if necessary).
   */
  publicAPI.removeCellReference = (cellId, ptId) => {
    model.array[ptId].cells = model.array[ptId].cells.filter(cell => cell !== cellId);
    model.array[ptId].ncells = model.array[ptId].cells.length;
  };

  /**
   * Add the reference to the cell (cellId) from the point (ptId). This
   * adds a reference to the cellId from the cell list, but does not resize
   * the list (extend memory with resizeCellList(), if necessary).
   */
  publicAPI.addCellReference = (cellId, ptId) => {
    model.array[ptId].cells[model.array[ptId].ncells++] = cellId;
  };

  /**
   * Change the length of a point's link list (i.e., list of cells using a
   * point) by the size specified.
   */
  publicAPI.resizeCellList = (ptId, size) => {
    model.array[ptId].cells.length = size;
  };

  /**
   * Reclaim any unused memory.
   */
  publicAPI.squeeze = () => {
    resize(model, model.maxId + 1);
  };

  /**
   * Reset to a state of no entries without freeing the memory.
   */
  publicAPI.reset = () => {
    model.maxId = -1;
  };

  /**
   * Standard DeepCopy method.  Since this object contains no reference
   * to other objects, there is no ShallowCopy.
   */
  publicAPI.deepCopy = src => {
    model.array = [...src.array];
    model.extend = src.extend;
    model.maxId = src.maxId;
  };

  /**
   * Increment the count of the number of cells using the point.
   */
  publicAPI.incrementLinkCount = ptId => {
    ++model.array[ptId].ncells;
  };
  publicAPI.allocateLinks = n => {
    for (let i = 0; i < n; ++i) {
      model.array[i].cells = new Array(model.array[i].ncells);
    }
  };

  /**
   * Insert a cell id into the list of cells using the point.
   */
  publicAPI.insertCellReference = (ptId, pos, cellId) => {
    model.array[ptId].cells[pos] = cellId;
  };
}

// ----------------------------------------------------------------------------
// Object factory
// ----------------------------------------------------------------------------

const DEFAULT_VALUES = {
  array: null,
  // pointer to data
  maxId: 0,
  // maximum index inserted thus far
  extend: 0 // grow array by this point
};

// ----------------------------------------------------------------------------

function extend(publicAPI, model) {
  let initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
  Object.assign(model, DEFAULT_VALUES, initialValues);
  macro.obj(publicAPI, model);
  vtkCellLinks(publicAPI, model);
}

// ----------------------------------------------------------------------------

const newInstance = macro.newInstance(extend, 'vtkCellLinks');

// ----------------------------------------------------------------------------

var vtkCellLinks$1 = {
  newInstance,
  extend
};

export { InitLink, vtkCellLinks$1 as default, extend, newInstance };
