/*
 * crc.c: CRC con un generador pequeño, mostrando la división paso a paso.
 *
 *   crc <datos> <generador>           calcula el CRC y la palabra a transmitir
 *   crc -c <palabra> <generador>      revisa una palabra recibida
 *
 * Los dos argumentos son cadenas de ceros y unos, por ejemplo: crc 1101011 1001
 * Es una herramienta para entender el cálculo, bit por bit; una tarjeta de red hace lo
 * mismo con un registro de desplazamiento.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_BITS 256

/* Devuelve 1 si la cadena no está vacía y solo tiene ceros y unos. */
static int is_bit_string(const char *s)
{
    return s[0] != '\0' && strspn(s, "01") == strlen(s);
}

/*
 * División módulo 2 de buf (largo len) entre gen (largo gen_len). Deja el residuo en los
 * últimos gen_len - 1 bits de buf. Si trace es distinto de cero, imprime cada resta.
 */
static void mod2_divide(char *buf, size_t len, const char *gen, size_t gen_len, int trace)
{
    for (size_t i = 0; i + gen_len <= len; i++) {
        if (buf[i] == '0') {
            continue;
        }
        if (trace) {
            printf("  %s\n", buf);
            printf("  %*s%s  (XOR en la posición %zu)\n", (int)i, "", gen, i);
        }
        for (size_t j = 0; j < gen_len; j++) {
            buf[i + j] = (buf[i + j] == gen[j]) ? '0' : '1';
        }
    }
    if (trace) {
        printf("  %s\n", buf);
    }
}

static void usage(const char *prog)
{
    fprintf(stderr, "uso: %s <datos> <generador>\n", prog);
    fprintf(stderr, "     %s -c <palabra> <generador>\n", prog);
}

int main(int argc, char *argv[])
{
    int check = argc == 4 && strcmp(argv[1], "-c") == 0;
    if (!check && argc != 3) {
        usage(argv[0]);
        return 2;
    }
    const char *input = argv[argc - 2];
    const char *gen = argv[argc - 1];
    size_t input_len = strlen(input);
    size_t gen_len = strlen(gen);

    if (!is_bit_string(input) || !is_bit_string(gen)) {
        fprintf(stderr, "los argumentos deben ser cadenas de ceros y unos\n");
        return 2;
    }
    /* El generador define r = gen_len - 1 solo si su primer bit es 1. */
    if (gen_len < 2 || gen[0] != '1') {
        fprintf(stderr, "el generador debe tener al menos 2 bits y empezar con 1\n");
        return 2;
    }
    if (input_len + gen_len > MAX_BITS) {
        fprintf(stderr, "máximo %d bits en total\n", MAX_BITS);
        return 2;
    }

    size_t r = gen_len - 1;
    char buf[MAX_BITS + 1];

    if (check) {
        if (input_len < gen_len) {
            fprintf(stderr, "la palabra debe ser más larga que el generador\n");
            return 2;
        }
        memcpy(buf, input, input_len + 1);
        mod2_divide(buf, input_len, gen, gen_len, 1);
        const char *rem = buf + input_len - r;
        int ok = strspn(rem, "0") == r;
        printf("residuo: %s\n", rem);
        printf("%s\n", ok ? "sin errores detectados" : "ERROR detectado");
        return ok ? 0 : 1;
    }

    /* Se agregan r ceros: es multiplicar los datos por 2^r. */
    memcpy(buf, input, input_len);
    memset(buf + input_len, '0', r);
    buf[input_len + r] = '\0';
    mod2_divide(buf, input_len + r, gen, gen_len, 1);
    const char *rem = buf + input_len;
    printf("CRC (r = %zu bits): %s\n", r, rem);
    printf("se transmite: %s%s\n", input, rem);
    return 0;
}
