// BSD 3-Clause License; see https://github.com/scikit-hep/awkward-1.0/blob/main/LICENSE

#define FILENAME(line) FILENAME_FOR_EXCEPTIONS_C("src/cpu-kernels/awkward_ListOffsetArray_argsort_strings.cpp", line)

#include <algorithm>
#include <cstring>
#include <numeric>
#include <vector>

#include "awkward/kernels.h"

template <bool is_stable, bool is_ascending, bool is_local>
ERROR awkward_ListOffsetArray_argsort_strings_impl(
  int64_t* tocarry,
  const int64_t* fromparents,
  int64_t length,
  const char* stringdata,
  const int64_t* stringstarts,
  const int64_t* stringstops) {

  auto sorter =
        [&stringdata, &stringstarts, &stringstops](int left, int right) -> bool {
          size_t left_n = stringstops[left] - stringstarts[left];
          size_t right_n = stringstops[right] - stringstarts[right];
          const char* left_str = &stringdata[stringstarts[left]];
          const char* right_str = &stringdata[stringstarts[right]];
          int cmp = strncmp(left_str, right_str, std::min(left_n, right_n));
          bool out;
          if (cmp == 0) {
            out = left_n < right_n;
          }
          else {
            out = cmp < 0;
          }
          if (is_ascending) {
            return out;
          }
          else {
            return !out;
          }
        };

  int64_t firstindex = 0;
  int64_t lastparent = -1;
  int64_t k = 0;
  std::vector<int64_t> index;
  for (int64_t i = 0;  i < length + 1;  i++) {
    if (i == length  ||  fromparents[i] != lastparent) {
      if (is_stable) {
        std::stable_sort(index.begin(), index.end(), sorter);
      }
      else {
        std::sort(index.begin(), index.end(), sorter);
      }
      for (int64_t j = 0;  j < (int64_t)index.size();  j++) {
        if (is_local) {
          tocarry[firstindex + j] = index[j] - firstindex;
        }
        else {
          tocarry[firstindex + j] = index[j];
        }
      }
      index.clear();
    }

    if (i != length) {
      if (index.empty()) {
        firstindex = i;
      }
      index.push_back(i);
      lastparent = fromparents[i];
    }
  }

  return success();
}

ERROR awkward_ListOffsetArray_argsort_strings(
  int64_t* tocarry,
  const int64_t* fromparents,
  int64_t length,
  const uint8_t* stringdata,
  const int64_t* stringstarts,
  const int64_t* stringstops,
  bool is_stable,
  bool is_ascending,
  bool is_local) {
  if (is_stable) {
    if (is_ascending) {
      if (is_local) {
        return awkward_ListOffsetArray_argsort_strings_impl<true, true, true>(
          tocarry, fromparents, length, (char*)stringdata, stringstarts, stringstops);
      }
      else {
        return awkward_ListOffsetArray_argsort_strings_impl<true, true, false>(
          tocarry, fromparents, length, (char*)stringdata, stringstarts, stringstops);
      }
    }
    else {
      if (is_local) {
        return awkward_ListOffsetArray_argsort_strings_impl<true, false, true>(
          tocarry, fromparents, length, (char*)stringdata, stringstarts, stringstops);
      }
      else {
        return awkward_ListOffsetArray_argsort_strings_impl<true, false, false>(
          tocarry, fromparents, length, (char*)stringdata, stringstarts, stringstops);
      }
    }
  }
  else {
    if (is_ascending) {
      if (is_local) {
        return awkward_ListOffsetArray_argsort_strings_impl<false, true, true>(
          tocarry, fromparents, length, (char*)stringdata, stringstarts, stringstops);
      }
      else {
        return awkward_ListOffsetArray_argsort_strings_impl<false, true, false>(
          tocarry, fromparents, length, (char*)stringdata, stringstarts, stringstops);
      }
    }
    else {
      if (is_local) {
        return awkward_ListOffsetArray_argsort_strings_impl<false, false, true>(
          tocarry, fromparents, length, (char*)stringdata, stringstarts, stringstops);
      }
      else {
        return awkward_ListOffsetArray_argsort_strings_impl<false, false, false>(
          tocarry, fromparents, length, (char*)stringdata, stringstarts, stringstops);
      }
    }
  }
}
