#include "phash.h"

#include <cmath>
#include <cstring>

int phash_hamming(const char* a, const char* b) {
  if (!a || !b) {
    return -1;
  }
  int distance = 0;
  const int len_a = static_cast<int>(strlen(a));
  const int len_b = static_cast<int>(strlen(b));
  const int min_len = len_a < len_b ? len_a : len_b;

  for (int i = 0; i < min_len; i++) {
    if (a[i] != b[i]) {
      distance++;
    }
  }
  distance += std::abs(len_a - len_b);
  return distance;
}

int phash_topn(const char* query_hash, const phash_entry* library, int count,
               phash_match* out, int n) {
  if (!query_hash || !library || !out || count <= 0 || n <= 0) {
    return -1;
  }

  struct Scored {
    const char* id;
    int distance;
  };

  Scored* scored = new Scored[count];
  for (int i = 0; i < count; i++) {
    scored[i].id = library[i].id;
    scored[i].distance = phash_hamming(query_hash, library[i].hash);
  }

  const int result_count = n < count ? n : count;
  for (int i = 0; i < result_count; i++) {
    int best = i;
    for (int j = i + 1; j < count; j++) {
      if (scored[j].distance < scored[best].distance) {
        best = j;
      }
    }
    if (best != i) {
      Scored tmp = scored[i];
      scored[i] = scored[best];
      scored[best] = tmp;
    }
    out[i].id = scored[i].id;
    out[i].distance = scored[i].distance;
  }

  delete[] scored;
  return result_count;
}
