import type { BriefDescriptor } from '../descriptors/getBriefDescriptors.js';

import { getHammingDistance } from './getHammingDistance.js';

// todo: implement bruteForceManyMatches -> N best matches for each source descriptor

export interface BruteForceMatchOptions {
  /**
   * Whether to sort the matches from best to worst.
   * @default `false`
   */
  sort?: boolean;
  /**
   * Number of best matches to return.
   * @default `source.length`
   */
  nbBestMatches?: number;
}

export interface Match {
  /**
   * Index of the source keypoint.
   */
  sourceIndex: number;
  /**
   * Index of the destination keypoint.
   */
  destinationIndex: number;
  /**
   * Distance from source to destination keypoints.
   */
  distance: number;
}

/**
 * Find the best match for each of the source descriptors using brute force matching.
 * @param source - Source descriptors.
 * @param destination - Destination descriptors.
 * @param options - Brute force amtch options.
 * @returns The best match for each source descriptor.
 */
export function bruteForceOneMatch(
  source: BriefDescriptor[],
  destination: BriefDescriptor[],
  options: BruteForceMatchOptions = {},
): Match[] {
  const { sort = !!options.nbBestMatches, nbBestMatches = source.length } =
    options;

  const matches: Match[] = [];
  for (let sourceIndex = 0; sourceIndex < source.length; sourceIndex++) {
    let minDistance = Number.POSITIVE_INFINITY;
    let index = 0;
    for (
      let destinationIndex = 0;
      destinationIndex < destination.length;
      destinationIndex++
    ) {
      const distance = getHammingDistance(
        source[sourceIndex],
        destination[destinationIndex],
      );

      if (distance < minDistance) {
        minDistance = distance;
        index = destinationIndex;
      }
    }
    matches.push({
      sourceIndex,
      destinationIndex: index,
      distance: minDistance,
    });
  }
  if (sort) {
    matches.sort((a, b) => a.distance - b.distance);
  }
  return matches.slice(0, nbBestMatches);
}
