#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "data10.h"
#include "data100.h"
#include "data1000.h"

// struct to store swaps and comps during sorting
typedef struct {
    size_t swaps;
    size_t comparisions;
} Status;

// Common Sorting Algorithim Function Signiture/Pointer
typedef void (*SortAlg)(unsigned char *, size_t, Status *);

// print first <head> and last <tail> elements of <arr>
void printArr(unsigned char *arr, size_t len, size_t head, size_t tail) {
    for (size_t i = 0; i < len; i++) {
        if (i >= len - tail || i < head) {
            printf("%d ", arr[i]);
        } else {
            if (i % 50 == 0)
                printf(". ");
        }
    }
    putchar('\n');
}

// swap values
void swap(unsigned char *a, unsigned char *b) {
    unsigned char t = *a;
    *a = *b;
    *b = t;
}

void bubble_sort(unsigned char *arr, size_t len, Status *stat) {
    for (size_t i = 0; i < len - 1; i++) {
        for (size_t j = 0; j < len - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                swap(&arr[j], &arr[j + 1]);
                stat->swaps++;
            }
            stat->comparisions++;
        }
    }
}

void selection_sort(unsigned char *arr, size_t len, Status *stat) {
    for (size_t i = 0; i < len; i++) {
        size_t min_idx = i;
        for (size_t j = i; j < len; j++) {
            if (arr[j] < arr[min_idx]) {
                min_idx = j;
            }
            stat->comparisions++;
        }
        swap(&arr[i], &arr[min_idx]);
        stat->swaps++;
    }
}

void insertion_sort(unsigned char *arr, size_t len, Status *stat) {
    for (size_t i = 1; i < len; i++) {
        for (size_t j = 0; j < i; j++) {
            if (arr[j] > arr[i]) {
                swap(&arr[j], &arr[i]);
                stat->swaps++;
            }
            stat->comparisions++;
        }
    }
}

int main(void) {
    unsigned char *datasets[3] = {data10, data100, data1000};
    size_t datasetLengths[3] = {data10_len, data100_len, data1000_len};
    char *labels[3] = {"bubble", "selection", "insertion"};
    SortAlg algs[3] = {bubble_sort, selection_sort, insertion_sort};
    unsigned char *buffer =
        (unsigned char *)(malloc(sizeof(unsigned char) * 1000));
    Status stats[3][3] = {{{0}}};

    // For each 3 datasets, perform 3 different sorting algorithims on copied
    // array
    for (size_t n = 0; n < 3; n++) {
        for (size_t m = 0; m < 3; m++) {
            printf("%s #%ld\n", labels[m], n + 1);
            memcpy(buffer, datasets[n], datasetLengths[n]);
            printf("Before: ");
            printArr(buffer, datasetLengths[n], 10, 10);
            (*algs[m])(buffer, datasetLengths[n], &stats[n][m]);
            printf("After: ");
            printArr(buffer, datasetLengths[n], 10, 10);
        }
        putchar('\n');
    }

    for (size_t i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            printf("%s %ld: swaps: %ld, comps: %ld\n", labels[j],
                   datasetLengths[i], stats[i][j].swaps,
                   stats[i][j].comparisions);
        }
    }

    return 0;
}
