/**
 * Copyright (c) 2021 Dominic Masters
 * 
 * This software is released under the MIT License.
 * https://opensource.org/licenses/MIT
 */

#include "array.h"

void * arrayGet(size_t size, void *array, int32_t index) {
  return (char *)array + (index * (size/sizeof(char)));
}

void arraySet(size_t size, void *array, int32_t index, void *data) {
  memcpy(arrayGet(size, array, index), data, size);
}

void arrayShuffle(size_t size, void *array, int32_t arrayLength) {
  int32_t x, y;
  void *itemDest, *itemSource;
  void *temporary = malloc(size);

  for(x = 0; x < arrayLength-1; x++) {
    // Select random element from remaining elements.
    y = randUint8Range(x, arrayLength);

    itemDest = arrayGet(size, array, y);
    itemSource = arrayGet(size, array, x);

    // Copy the data into temporary variable
    memcpy(temporary, itemDest, size);

    // Take the item at this current index, and store from where we just took
    memcpy(itemDest, itemSource, size);

    // Store the thing from where we just took into here.
    memcpy(itemSource, temporary, size);
  }

  // Cleanup
  free(temporary);
}

int32_t arrayFind(size_t size, void *array, int32_t length, void *value) {
  int32_t i;
  void *item;

  for(i = 0; i < length; i++) {
    // Get the item and compare.
    item = arrayGet(size, array, i);
    if(memcmp(item, value, size) == 0) return i;
  }

  // No find.
  return -1;
}

bool arrayContains(size_t size, void *array, int32_t length, void *value) {
  int32_t i = arrayFind(size, array, length, value);
  return i != -1;
}

void arrayCopy(size_t size, void *source, int32_t length, void *dest) {
  memcpy(dest, source, size * length);
}

void arraySort(size_t size, void *array, int32_t length, arraysort_t *sort) {
  qsort(array, length, size, sort);
}

// Common Sorters:

void arraySortInt32(int32_t *array, int32_t length) {
  arraySort(sizeof(int32_t), array, length, &_arraySorterInt32);
}
int32_t _arraySorterInt32(const void* left, const void* right) {
  return *((int32_t *)left) - *((int32_t *)right);
}

void arraySortUint8(uint8_t *array, int32_t length) {
  arraySort(sizeof(uint8_t), array, length, &_arraySorterUint8);
}
int32_t _arraySorterUint8(const void* left, const void* right) {
  return *((uint8_t *)left) - *((uint8_t *)right);
}

int32_t arrayFindString(char **array, int32_t arrayLength, char *value) {
  int32_t i;
  for(i = 0; i < arrayLength; i++) {
    if(strcmp(array[i], value) == 0) return i;
  }
  return -1;
}

void arrayRewind(size_t size, void *source, int32_t length, int32_t start,
  int32_t count
) {
  if(count == -1) count = length - start;

  // Create a temporary new array to house the data.
  void *temporary = malloc(size * count);
  
  // Copy the data from the source to the temporary array.
  arrayCopy(size, arrayGet(size, source, start), count, temporary);

  // Now copy the data back to the source array.
  arrayCopy(size, temporary, count, source);

  // Cleanup the temporary array.
  free(temporary);
}

void arraySplice(
  size_t size, void *array, int32_t start, int32_t count, int32_t length
) {
  // Take the end of the array...
  int32_t takeStart = start + count;
  int32_t takeLength = length - takeStart;

  void *temporary = malloc(size * takeLength);
  arrayCopy(size, arrayGet(size, array, takeStart), takeLength, temporary);

  // Now copy it back into the original array
  arrayCopy(size, temporary, takeLength, arrayGet(size, array, start));
  free(temporary);
}


int32_t arraySum(
  size_t size, void *array, int32_t length, arraysumcallback_t *callback
) {
  int32_t i, count;
  count = 0;
  for(i = 0; i < length; i++) {
    if(!callback(arrayGet(size, array, i))) continue;
    count++;
  }
  return count;
}