import { m as macro } from '../../macros2.js';
import { i as isNan, h as hsv2rgb } from './Math/index.js';
import vtkScalarsToColors from './ScalarsToColors.js';
import { ScalarMappingTarget } from './ScalarsToColors/Constants.js';
import { VtkDataTypes } from './DataArray/Constants.js';

const {
  vtkErrorMacro
} = macro;

// ----------------------------------------------------------------------------
// Global methods
// ----------------------------------------------------------------------------

// Add module-level functions or api that you want to expose statically via
// the next section...

// ----------------------------------------------------------------------------
// Static API
// ----------------------------------------------------------------------------

const BELOW_RANGE_COLOR_INDEX = 0;
const ABOVE_RANGE_COLOR_INDEX = 1;
const NAN_COLOR_INDEX = 2;

// ----------------------------------------------------------------------------
// vtkMyClass methods
// ----------------------------------------------------------------------------

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

  //----------------------------------------------------------------------------
  // Description:
  // Return true if all of the values defining the mapping have an opacity
  // equal to 1. Default implementation return true.
  publicAPI.isOpaque = () => {
    if (model.opaqueFlagBuildTime.getMTime() < publicAPI.getMTime()) {
      let opaque = true;
      if (model.nanColor[3] < 1.0) {
        opaque = 0;
      }
      if (model.useBelowRangeColor && model.belowRangeColor[3] < 1.0) {
        opaque = 0;
      }
      if (model.useAboveRangeColor && model.aboveRangeColor[3] < 1.0) {
        opaque = 0;
      }
      for (let i = 3; i < model.table.length && opaque; i += 4) {
        if (model.table[i] < 255) {
          opaque = false;
        }
      }
      model.opaqueFlag = opaque;
      model.opaqueFlagBuildTime.modified();
    }
    return model.opaqueFlag;
  };
  publicAPI.usingLogScale = () => false;

  //----------------------------------------------------------------------------
  // Don't count special colors (min, max, NaN) as available colors
  publicAPI.getNumberOfAvailableColors = () => model.table.length / 4 - 3;

  //----------------------------------------------------------------------------
  // Apply shift/scale to the scalar value v and return the index.
  publicAPI.linearIndexLookup = (v, p) => {
    let dIndex = 0;
    const nv = Number(v);
    if (nv < p.range[0]) {
      dIndex = p.maxIndex + BELOW_RANGE_COLOR_INDEX + 1.5;
    } else if (nv > p.range[1]) {
      dIndex = p.maxIndex + ABOVE_RANGE_COLOR_INDEX + 1.5;
    } else {
      dIndex = (nv + p.shift) * p.scale;

      // This conditional is needed because when v is very close to
      // p.Range[1], it may map above p.MaxIndex in the linear mapping
      // above.
      dIndex = dIndex < p.maxIndex ? dIndex : p.maxIndex;
    }
    return Math.floor(dIndex);
  };
  publicAPI.linearLookup = (v, table, p) => {
    let index = 0;
    if (isNan(v)) {
      index = Math.floor(p.maxIndex + 1.5 + NAN_COLOR_INDEX);
    } else {
      index = publicAPI.linearIndexLookup(v, p);
    }
    const offset = 4 * index;
    return table.slice(offset, offset + 4);
  };
  publicAPI.indexedLookupFunction = (v, table, p) => {
    let index = publicAPI.getAnnotatedValueIndexInternal(v);
    if (index === -1) {
      index = model.numberOfColors + NAN_COLOR_INDEX;
    }
    const offset = 4 * index;
    return [table[offset], table[offset + 1], table[offset + 2], table[offset + 3]];
  };

  //----------------------------------------------------------------------------
  publicAPI.lookupShiftAndScale = (range, p) => {
    p.shift = -range[0];
    p.scale = Number.MAX_VALUE;
    if (range[1] > range[0]) {
      p.scale = (p.maxIndex + 1) / (range[1] - range[0]);
    }
  };

  // Public API methods
  publicAPI.mapScalarsThroughTable = (input, output, outFormat, inputOffset) => {
    let lookupFunc = publicAPI.linearLookup;
    if (model.indexedLookup) {
      lookupFunc = publicAPI.indexedLookupFunction;
    }
    const trange = publicAPI.getMappingRange();
    const p = {
      maxIndex: publicAPI.getNumberOfColors() - 1,
      range: trange,
      shift: 0.0,
      scale: 0.0
    };
    publicAPI.lookupShiftAndScale(trange, p);
    const alpha = publicAPI.getAlpha();
    const length = input.getNumberOfTuples();
    const inIncr = input.getNumberOfComponents();
    const outputV = output.getData();
    const inputV = input.getData();
    if (alpha >= 1.0) {
      if (outFormat === ScalarMappingTarget.RGBA) {
        for (let i = 0; i < length; i++) {
          const cptr = lookupFunc(inputV[i * inIncr + inputOffset], model.table, p);
          outputV[i * 4] = cptr[0];
          outputV[i * 4 + 1] = cptr[1];
          outputV[i * 4 + 2] = cptr[2];
          outputV[i * 4 + 3] = cptr[3];
        }
      }
    } else {
      /* eslint-disable no-lonely-if */
      if (outFormat === ScalarMappingTarget.RGBA) {
        for (let i = 0; i < length; i++) {
          const cptr = lookupFunc(inputV[i * inIncr + inputOffset], model.table, p);
          outputV[i * 4] = cptr[0];
          outputV[i * 4 + 1] = cptr[1];
          outputV[i * 4 + 2] = cptr[2];
          outputV[i * 4 + 3] = Math.floor(cptr[3] * alpha + 0.5);
        }
      }
    } // alpha blending
  };

  publicAPI.forceBuild = () => {
    let hinc = 0.0;
    let sinc = 0.0;
    let vinc = 0.0;
    let ainc = 0.0;
    const maxIndex = model.numberOfColors - 1;
    if (maxIndex) {
      hinc = (model.hueRange[1] - model.hueRange[0]) / maxIndex;
      sinc = (model.saturationRange[1] - model.saturationRange[0]) / maxIndex;
      vinc = (model.valueRange[1] - model.valueRange[0]) / maxIndex;
      ainc = (model.alphaRange[1] - model.alphaRange[0]) / maxIndex;
    }
    model.table.length = 4 * maxIndex + 16;
    const hsv = [];
    const rgba = [];
    for (let i = 0; i <= maxIndex; i++) {
      hsv[0] = model.hueRange[0] + i * hinc;
      hsv[1] = model.saturationRange[0] + i * sinc;
      hsv[2] = model.valueRange[0] + i * vinc;
      hsv2rgb(hsv, rgba);
      rgba[3] = model.alphaRange[0] + i * ainc;

      //  case VTK_RAMP_LINEAR:
      model.table[i * 4] = rgba[0] * 255.0 + 0.5;
      model.table[i * 4 + 1] = rgba[1] * 255.0 + 0.5;
      model.table[i * 4 + 2] = rgba[2] * 255.0 + 0.5;
      model.table[i * 4 + 3] = rgba[3] * 255.0 + 0.5;
    }
    publicAPI.buildSpecialColors();
    model.buildTime.modified();
  };
  publicAPI.setTable = table => {
    // Handle JS array (assume 2D array)
    if (Array.isArray(table)) {
      const nbComponents = table[0].length;
      model.numberOfColors = table.length;
      const colorOffset = 4 - nbComponents;
      let offset = 0;
      // fill table
      for (let i = 0; i < model.numberOfColors; i++) {
        model.table[i * 4] = 255;
        model.table[i * 4 + 1] = 255;
        model.table[i * 4 + 2] = 255;
        model.table[i * 4 + 3] = 255;
      }
      // extract colors
      for (let i = 0; i < table.length; i++) {
        const color = table[i];
        for (let j = 0; j < nbComponents; j++) {
          model.table[offset++] = color[j];
        }
        offset += colorOffset;
      }
      publicAPI.buildSpecialColors();
      model.insertTime.modified();
      publicAPI.modified();
      return true;
    }
    if (table.getNumberOfComponents() !== 4) {
      vtkErrorMacro('Expected 4 components for RGBA colors');
      return false;
    }
    if (table.getDataType() !== VtkDataTypes.UNSIGNED_CHAR) {
      vtkErrorMacro('Expected unsigned char values for RGBA colors');
      return false;
    }
    model.numberOfColors = table.getNumberOfTuples();
    const data = table.getData();
    model.table.length = data.length;
    for (let i = 0; i < data.length; i++) {
      model.table[i] = data[i];
    }
    publicAPI.buildSpecialColors();
    model.insertTime.modified();
    publicAPI.modified();
    return true;
  };
  publicAPI.buildSpecialColors = () => {
    // Add "special" colors (NaN, below range, above range) to table here.
    const {
      numberOfColors
    } = model;
    const tptr = model.table;
    let base = (numberOfColors + BELOW_RANGE_COLOR_INDEX) * 4;

    // Below range color
    if (model.useBelowRangeColor || numberOfColors === 0) {
      tptr[base] = model.belowRangeColor[0] * 255.0 + 0.5;
      tptr[base + 1] = model.belowRangeColor[1] * 255.0 + 0.5;
      tptr[base + 2] = model.belowRangeColor[2] * 255.0 + 0.5;
      tptr[base + 3] = model.belowRangeColor[3] * 255.0 + 0.5;
    } else {
      // Duplicate the first color in the table.
      tptr[base] = tptr[0];
      tptr[base + 1] = tptr[1];
      tptr[base + 2] = tptr[2];
      tptr[base + 3] = tptr[3];
    }

    // Above range color
    base = (numberOfColors + ABOVE_RANGE_COLOR_INDEX) * 4;
    if (model.useAboveRangeColor || numberOfColors === 0) {
      tptr[base] = model.aboveRangeColor[0] * 255.0 + 0.5;
      tptr[base + 1] = model.aboveRangeColor[1] * 255.0 + 0.5;
      tptr[base + 2] = model.aboveRangeColor[2] * 255.0 + 0.5;
      tptr[base + 3] = model.aboveRangeColor[3] * 255.0 + 0.5;
    } else {
      // Duplicate the last color in the table.
      tptr[base] = tptr[4 * (numberOfColors - 1) + 0];
      tptr[base + 1] = tptr[4 * (numberOfColors - 1) + 1];
      tptr[base + 2] = tptr[4 * (numberOfColors - 1) + 2];
      tptr[base + 3] = tptr[4 * (numberOfColors - 1) + 3];
    }

    // Always use NanColor
    base = (numberOfColors + NAN_COLOR_INDEX) * 4;
    tptr[base] = model.nanColor[0] * 255.0 + 0.5;
    tptr[base + 1] = model.nanColor[1] * 255.0 + 0.5;
    tptr[base + 2] = model.nanColor[2] * 255.0 + 0.5;
    tptr[base + 3] = model.nanColor[3] * 255.0 + 0.5;
  };
  publicAPI.build = () => {
    if (model.table.length < 1 || publicAPI.getMTime() > model.buildTime.getMTime() && model.insertTime.getMTime() <= model.buildTime.getMTime()) {
      publicAPI.forceBuild();
    }
  };
  if (model.table.length > 0) {
    // Ensure that special colors are properly included in the table
    publicAPI.buildSpecialColors();

    // ensure insertTime is more recently modified than buildTime if
    // a table is provided via the constructor
    model.insertTime.modified();
  }
}

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

const DEFAULT_VALUES = {
  numberOfColors: 256,
  // table: null,

  hueRange: [0.0, 0.66667],
  saturationRange: [1.0, 1.0],
  valueRange: [1.0, 1.0],
  alphaRange: [1.0, 1.0],
  nanColor: [0.5, 0.0, 0.0, 1.0],
  belowRangeColor: [0.0, 0.0, 0.0, 1.0],
  aboveRangeColor: [1.0, 1.0, 1.0, 1.0],
  useAboveRangeColor: false,
  useBelowRangeColor: false,
  alpha: 1.0
  // buildTime: null,
  // opaqueFlagBuildTime: null,
  // insertTime: null,
};

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

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

  // Inheritance
  vtkScalarsToColors.extend(publicAPI, model, initialValues);

  // Internal objects initialization
  if (!model.table) {
    model.table = [];
  }
  model.buildTime = {};
  macro.obj(model.buildTime);
  model.opaqueFlagBuildTime = {};
  macro.obj(model.opaqueFlagBuildTime, {
    mtime: 0
  });
  model.insertTime = {};
  macro.obj(model.insertTime, {
    mtime: 0
  });

  // Create get-only macros
  macro.get(publicAPI, model, ['buildTime']);

  // Create get-set macros
  macro.setGet(publicAPI, model, ['numberOfColors', 'useAboveRangeColor', 'useBelowRangeColor']);

  // Create set macros for array (needs to know size)
  macro.setArray(publicAPI, model, ['alphaRange', 'hueRange', 'saturationRange', 'valueRange'], 2);
  macro.setArray(publicAPI, model, ['nanColor', 'belowRangeColor', 'aboveRangeColor'], 4);

  // Create get macros for array
  macro.getArray(publicAPI, model, ['hueRange', 'saturationRange', 'valueRange', 'alphaRange', 'nanColor', 'belowRangeColor', 'aboveRangeColor']);

  // For more macro methods, see "Sources/macros.js"

  // Object specific methods
  vtkLookupTable(publicAPI, model);
}

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

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

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

var vtkLookupTable$1 = {
  newInstance,
  extend
};

export { vtkLookupTable$1 as default, extend, newInstance };
