/*
 * * see http://rsb.info.nih.gov/ij/developer/source/ij/process/AutoThresholder.java.html.
 * Huang: Implements Huang's fuzzy thresholding method: Huang, L-K & Wang, M-J J (1995),
 * "Image thresholding by minimizing the measure of fuzziness", Pattern Recognition 28(1): 41-51
 */

/**
 * Return a threshold for a histogram using Huang algorithm.
 * @param histogram - Image histogram.
 * @returns The threshold.
 */
export default function huang(histogram: Uint32Array): number {
  /* Determine the first non-zero bin */
  let firstBin = 0;
  for (let ih = 0; ih < histogram.length; ih++) {
    if (histogram[ih] !== 0) {
      firstBin = ih;
      break;
    }
  }

  /* Determine the last non-zero bin */
  let lastBin = histogram.length - 1;
  for (let ih = histogram.length - 1; ih >= firstBin; ih--) {
    if (histogram[ih] !== 0) {
      lastBin = ih;
      break;
    }
  }

  const term = 1 / (lastBin - firstBin);
  const mu0 = new Array(histogram.length);
  let sumPix = 0;
  let numPix = 0;
  for (let ih = firstBin; ih < histogram.length; ih++) {
    sumPix += ih * histogram[ih];
    numPix += histogram[ih];
    mu0[ih] = sumPix / numPix;
  }

  const mu1 = new Array(histogram.length);
  sumPix = 0;
  numPix = 0;
  for (let ih = lastBin; ih > 0; ih--) {
    sumPix += ih * histogram[ih];
    numPix += histogram[ih];
    mu1[ih - 1] = sumPix / numPix;
  }

  /* Determine the threshold that minimizes the fuzzy entropy*/
  let threshold = -1;
  let minEnt = Number.MAX_VALUE;
  for (let it = 0; it < histogram.length; it++) {
    let ent = 0;
    let muX;
    for (let ih = 0; ih <= it; ih++) {
      /* Equation (4) in Ref. 1 */
      muX = 1 / (1 + term * Math.abs(ih - mu0[it]));
      if (!(muX < 1e-6 || muX > 0.999999)) {
        /* Equation (6) & (8) in Ref. 1 */
        ent +=
          histogram[ih] *
          (-muX * Math.log(muX) - (1 - muX) * Math.log(1 - muX));
      }
    }

    for (let ih = it + 1; ih < histogram.length; ih++) {
      /* Equation (4) in Ref. 1 */
      muX = 1 / (1 + term * Math.abs(ih - mu1[it]));
      if (!(muX < 1e-6 || muX > 0.999999)) {
        /* Equation (6) & (8) in Ref. 1 */
        ent +=
          histogram[ih] *
          (-muX * Math.log(muX) - (1 - muX) * Math.log(1 - muX));
      }
    }

    if (ent < minEnt) {
      minEnt = ent;
      threshold = it;
    }
  }
  return threshold;
}
