use crate::matrix::MatrixGF2;
use rand::RngExt;

// H doit respecter les contraine des poids w_c et w_r
pub fn generate_random_h(rows: usize, cols: usize, wc: usize, wr: usize) -> MatrixGF2 {
    assert_eq!(rows * wr, cols * wc, "Erreur");
    loop {
        let mut data = vec![0; rows * cols];
        let mut row_w = vec![0; rows];
        let mut rng = rand::rng();
        let mut success = true;

        for c in 0..cols {
            let mut ones_placed = 0;
            let mut avail_rows: Vec<usize> = (0..rows)
                .filter(|&r| row_w[r] < wr && !creates_cycle_4(rows, cols, &data, r, c))
                .collect();

            if avail_rows.len() < wc {
                // Les problèmes...
                success = false;
                break;
            }

            while ones_placed < wc {
                let id = rng.random_range(0..avail_rows.len());
                let r = avail_rows.remove(id);
                data[r * cols + c] = 1;
                row_w[r] += 1;
                ones_placed += 1;
            }
        }
        if success {
            return MatrixGF2::new(rows, cols, data);
        }
    }
}

pub fn generate_random_h_for_k(k: usize, wc: usize, wr: usize) -> MatrixGF2 {
    assert!(wr > wc, "wr < wc ...");
    assert!((k * wr) % (wr - wc) == 0, "Colonnes non entieres");

    let n = (k * wr) / (wr - wc);
    let m = n - k;

    let max_attemps = 50;
    let mut best_h = generate_random_h(m, n, wc, wr);
    let mut max_rank = 0;

    // On garde que les matrice de rang max
    for _ in 0..max_attemps {
        let h_cand = generate_random_h(m, n, wc, wr);
        let mut h_test = h_cand.clone();
        let (rank, _) = h_test.gauss_jordan_swap_cols();
        if rank == m {
            return h_cand;
        }
        if rank > max_rank {
            max_rank = rank;
            best_h = h_cand;
        }
    }
    best_h
}

fn creates_cycle_4(rows: usize, cols: usize, data: &[u8], row: usize, col: usize) -> bool {
    for r2 in 0..rows {
        if r2 != row && data[r2 * cols + col] == 1 {
            for c2 in 0..col {
                if data[row * cols + c2] == 1 && data[r2 * cols + c2] == 1 {
                    return true;
                }
            }
        }
    }
    false
}
