/*
AUTORE: Manuel Vichi 3^AIN
Algoritmi sort su file
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
void fSelectionSort(const char *nomeFile) {
    FILE *fp = fopen(nomeFile, "r+b");
    if (fp == NULL) {
        perror("Errore nell'apertura del file");
        return;
    }
    fseek(fp, 0, SEEK_END);
    long dimensioneFile = ftell(fp);
    fseek(fp, 0, SEEK_SET);
    size_t n = dimensioneFile / sizeof(int);
    int valoreMin, valoreCorrente;
    size_t indiceMin;
    int temp;
    for (size_t i = 0; i < n - 1; i++) {
        indiceMin = i;
        fseek(fp, i * sizeof(int), SEEK_SET);
        fread(&valoreMin, sizeof(int), 1, fp);
        for (size_t j = i + 1; j < n; j++) {
            fseek(fp, j * sizeof(int), SEEK_SET);
            fread(&valoreCorrente, sizeof(int), 1, fp);
            if (valoreCorrente < valoreMin) {
                valoreMin = valoreCorrente;
                indiceMin = j;
            }
        }
        if (indiceMin != i) {
            fseek(fp, i * sizeof(int), SEEK_SET);
            fread(&temp, sizeof(int), 1, fp);
            int valoreTemp;
            fseek(fp, indiceMin * sizeof(int), SEEK_SET);
            fread(&valoreTemp, sizeof(int), 1, fp);
            fseek(fp, i * sizeof(int), SEEK_SET);
            fwrite(&valoreTemp, sizeof(int), 1, fp);
            fseek(fp, indiceMin * sizeof(int), SEEK_SET);
            fwrite(&temp, sizeof(int), 1, fp);
        }
    }
    fclose(fp);
}
void fBubbleSort(const char *nomeFile) {
    FILE *fp = fopen(nomeFile, "r+b");
    if (fp == NULL) {
        perror("Errore nell'apertura del file");
        return;
    }
    fseek(fp, 0, SEEK_END);
    long dimensioneFile = ftell(fp);
    fseek(fp, 0, SEEK_SET);
    size_t n = dimensioneFile / sizeof(int);
    int valore1, valore2;
    bool scambiato;
    for (size_t i = 0; i < n - 1; i++) {
        scambiato = false;
        for (size_t j = 0; j < n - i - 1; j++) {
            fseek(fp, j * sizeof(int), SEEK_SET);
            if (fread(&valore1, sizeof(int), 1, fp) != 1) {
                perror("Errore di lettura del file");
                fclose(fp);
                return;
            }
            fseek(fp, (j + 1) * sizeof(int), SEEK_SET);
            if (fread(&valore2, sizeof(int), 1, fp) != 1) {
                perror("Errore di lettura del file");
                fclose(fp);
                return;
            }
            if (valore1 > valore2) {
                fseek(fp, j * sizeof(int), SEEK_SET);
                if (fwrite(&valore2, sizeof(int), 1, fp) != 1) {
                    perror("Errore di scrittura nel file");
                    fclose(fp);
                    return;
                }
                fseek(fp, (j + 1) * sizeof(int), SEEK_SET);
                if (fwrite(&valore1, sizeof(int), 1, fp) != 1) {
                    perror("Errore di scrittura nel file");
                    fclose(fp);
                    return;
                }
                scambiato = true;
            }
        }
        if (!scambiato) {
            break;
        }
    }
    fclose(fp);
}
void swap(FILE *fp, int pos1, int pos2, size_t elementSize) {
    int temp1, temp2;
    fseek(fp, pos1 * elementSize, SEEK_SET);
    fread(&temp1, elementSize, 1, fp);
    fseek(fp, pos2 * elementSize, SEEK_SET);
    fread(&temp2, elementSize, 1, fp);
    fseek(fp, pos1 * elementSize, SEEK_SET);
    fwrite(&temp2, elementSize, 1, fp);
    fseek(fp, pos2 * elementSize, SEEK_SET);
    fwrite(&temp1, elementSize, 1, fp);
}
int partition(FILE *fp, int low, int high, size_t elementSize) {
    int pivot;
    fseek(fp, high * elementSize, SEEK_SET);
    fread(&pivot, elementSize, 1, fp);
    int i = low - 1;
    for (int j = low; j < high; j++) {
        int current;
        fseek(fp, j * elementSize, SEEK_SET);
        fread(&current, elementSize, 1, fp);
        if (current < pivot) {
            i++;
            swap(fp, i, j, elementSize);
        }
    }
    swap(fp, i + 1, high, elementSize);
    return i + 1;
}
/*
Funzione per ordinamento in-place con Quick Sort
Parametri:
FILE *fp: Questo parametro è un puntatore a un file aperto in modalità binaria, cioè è il "cassetto" dove sono memorizzati i dati (gli elementi) che vuoi ordinare. Quando chiamiamo le funzioni come fseek, fread o fwrite, usiamo questo puntatore per dire al programma in quale file operare.
int low: Questo parametro rappresenta l'indice (cioè la "posizione") del primo elemento del gruppo (o "sottosequenza") di elementi del file che vogliamo ordinare. Ad esempio, se vuoi ordinare tutto il file, low sarà di solito 0, perché il primo elemento si trova alla posizione 0.
int high: Rappresenta l'indice dell'ultimo elemento del gruppo di elementi che stiamo considerando per l'ordinamento. Se il file contiene 100 elementi e numeriamo gli elementi da 0 a 99, allora per ordinare tutto il file useremo high = 99. In altre parole, fQuickSort ordina gli elementi dalle posizioni low a high.
size_t elementSize: Questo parametro indica la dimensione, in byte, di ogni elemento contenuto nel file. Ad esempio, se il file contiene interi e ogni intero occupa 4 byte, userai sizeof(int) (che di solito vale 4) per questo parametro. È fondamentale perché la funzione deve sapere quanti byte saltare per passare da un elemento all’altro nel file: per raggiungere l’elemento in posizione i, il programma si sposta di i * elementSize byte dal principio del file.
*/
void fQuickSort(FILE *fp, int low, int high, size_t elementSize) {
    if (low < high) {
        int pi = partition(fp, low, high, elementSize);
        fQuickSort(fp, low, pi - 1, elementSize);
        fQuickSort(fp, pi + 1, high, elementSize);
    }
}

// Funzione per ordinare in-place un file binario utilizzando l'Insertion Sort.
// Parametri:
//   fp    - puntatore ad un file aperto in modalità binaria
//   record_size - dimensione in byte di ogni record (ad esempio, sizeof(int))
//   cmp         - funzione per confrontare due record
void fInsertionSort(FILE *fp, size_t record_size, int (*cmp)(const void *, const void *)) {
    fseek(fp, 0, SEEK_END);
    long file_size = ftell(fp);
    size_t num_records = file_size / record_size;
    void *key = malloc(record_size);
    void *temp = malloc(record_size);
    if (!key || !temp) {
        perror("Errore nell'allocazione della memoria");
        free(key);
        free(temp);
        return;
    }
    for (size_t i = 1; i < num_records; i++) {
        fseek(fp, i * record_size, SEEK_SET);
        fread(key, record_size, 1, fp);
        size_t j = i;
        while (j > 0) {
            fseek(fp, (j - 1) * record_size, SEEK_SET);
            fread(temp, record_size, 1, fp);
            if (cmp(temp, key) <= 0) {
                break;
            }
            fseek(fp, j * record_size, SEEK_SET);
            fwrite(temp, record_size, 1, fp);
            j--;
        }
        fseek(fp, j * record_size, SEEK_SET);
        fwrite(key, record_size, 1, fp);
    }
    free(key);
    free(temp);
}