use core::panic::PanicMessage;
use std::sync::mpsc::Receiver;

use crate::channel::Channel;
use crate::code::{self, LdpcCode};
use crate::construction::random::{generate_random_h, generate_random_h_for_k};
use crate::decoder::bit_flip::BitFlipDecoder;
use crate::encoder::dense::DenseEncoder;
use crate::utils;
use rand::{Rng, RngExt};

pub fn run_simulation(text: &str, wc: usize, wr: usize, error_prob: f64) {
    let mut message_bits = utils::string_to_bits(text);
    let k = message_bits.len();
    println!(
        "\nSimulation LDPC : k = {} bits, wc = {}, wr = {}, p = {:.2}, text : {}",
        k, wc, wr, error_prob, text
    );

    println!("Construction");

    let h_matrix = generate_random_h_for_k(k, wc, wr);
    let ldpc = LdpcCode::new(h_matrix);

    let encoder = DenseEncoder::new(&ldpc);

    let k = encoder.k;
    // if encoder.k != k {
    //     println!("Erreur: la matrice donne k = {} != {}", encoder.k, k);
    // }

    println!(
        "  -> Code généré : n = {}, k = {}, k/n = {:.2}",
        encoder.n,
        encoder.k,
        (encoder.k as f64 / encoder.n as f64)
    );

    println!("Encodage");

    let mut rng = rand::rng();
    let message: Vec<u8> = (0..k).map(|_| rng.random_range(0..2)).collect();

    let codeword = encoder.encode(&message);

    // println!("  -> Message  u ({:02} bits) : {:?}", k, message);
    // println!("  -> Codeword s ({:02} bits) : {:?}", encoder.n, codeword);

    println!("\nCanal bruité (p = {})", error_prob);

    let channel = Channel::new(error_prob);
    let received = channel.add_noise(&codeword);

    let nb_errors = Channel::count_errors(&codeword, &received);
    if nb_errors == 0 {
        println!("   -> Aucune erreur.")
    } else {
        println!("  -> {} erreurs !", nb_errors);
        // println!("  -> Recu r ({:02} bits) : {:?}", received.len(), received);
    }

    println!("\n Décodage (bit-flipping)");

    if nb_errors == 0 {
        println!("  -> Pas besoin de correction");
        return;
    }

    let decoder = BitFlipDecoder::new(&ldpc);
    let max_iter = 100;

    match decoder.decode(&received, max_iter) {
        Some(decoded) => {
            let decoded_message = &decoded[0..encoder.k];
            let res_text = utils::bits_to_string(decoded_message);
            if decoded == codeword {
                println!("  -> Réussite");
            } else {
                println!("  -> Echec : le decoder a convergé vers un mauvais codeword");
            }
            println!("  -> Texte recu : {}", res_text);
        }
        None => {
            let failed_text = utils::bits_to_string(&received[0..encoder.k]);
            println!(
                "  -> Echec : impossible decorriger après {} itérations",
                max_iter
            );
            println!("  -> Texte recu : {}", failed_text);
        }
    }
}
