import type { DataXY, DoubleMatrix } from 'cheminfo-types';

import type { XHistogramOptions } from '../x/index.ts';
import { xHistogram } from '../x/index.ts';

import { matrixMinMaxAbsoluteZ } from './matrixMinMaxAbsoluteZ.ts';
import { matrixMinMaxZ } from './matrixMinMaxZ.ts';

export interface MatrixHistogramOptions {
  /**
   * Center the X value. We will enlarge the first and
   * @default true
   */
  centerX?: boolean;

  /**
   * histogram
   */
  histogram?: DataXY;

  /**
   * Number of slots
   * @default 256
   */
  nbSlots?: number;

  /**
   * We can first apply a log on the x-axis.
   */
  logBaseX?: number;

  /**
   * We can apply a log on the resulting histogram
   */
  logBaseY?: number;

  /**
   * Take the absolute value
   */
  absolute?: boolean;

  /**
   * Maximal value to calculate used to calculate slot size
   * @default maxValue
   */
  max?: number;

  /**
   * Minimum value to calculate used to calculate slot size
   * @default minValue
   */
  min?: number;
}

/**
 * Calculates a histogram of defined number of slots.
 * @param matrix - matrix [rows][cols].
 * @param options - options
 * @returns result of the histogram.
 */
export function matrixHistogram(
  matrix: DoubleMatrix,
  options: MatrixHistogramOptions = {},
): DataXY {
  let { min, max } = options;
  const { absolute, logBaseX, logBaseY, histogram, nbSlots, centerX } = options;

  if (matrix.length === 0 || matrix[0].length === 0) {
    throw new Error('matrix must have at least one column and one row');
  }

  if (min === undefined || max === undefined) {
    const minMax = absolute
      ? matrixMinMaxAbsoluteZ(matrix)
      : matrixMinMaxZ(matrix);
    if (min === undefined) {
      min =
        logBaseX && minMax.min
          ? Math.log(minMax.min) / Math.log(logBaseX)
          : minMax.min;
    }
    if (max === undefined) {
      max =
        logBaseX && minMax.max
          ? Math.log(minMax.max) / Math.log(logBaseX)
          : minMax.max;
    }
  }

  const xHistogramOptions: XHistogramOptions = {
    histogram,
    nbSlots,
    centerX,
    absolute,
    logBaseX,
    // Skip logBaseY as it's handled below.
    min,
    max,
  };
  const finalHistogram = xHistogram(matrix[0], xHistogramOptions);
  xHistogramOptions.histogram = finalHistogram;

  const nbRows = matrix.length;
  for (let row = 1; row < nbRows; row++) {
    xHistogram(matrix[row], xHistogramOptions);
  }

  const y = finalHistogram.y;
  if (logBaseY) {
    const logOfBase = Math.log10(logBaseY);
    for (let i = 0; i < y.length; i++) {
      y[i] = Math.log10(y[i] + 1) / logOfBase;
    }
  }

  return finalHistogram;
}
