pub mod hamming_code{

    use std::{borrow::Borrow, collections::HashMap};

    use crate::{
        model::model::model_module::*,
        controller::view_utils::input_utils::input_utils_module::*,
    };

    /// **Синдромы**
    ///
    /// ошибочная позиция 1 false true true.
    ///
    /// ошибочная позиция 2 false false true.
    ///
    /// ошибочная позиция 3 true false true.
    ///
    /// ошибочная позиция 4 false true false.
    ///
    /// ошибочная позиция 5 true true false.
    ///
    /// ошибочная позиция 6 true false false.
    ///
    /// ошибочная позиция 7 false false false.

    pub fn hamming(raw_input: String, mode: HammingMode) -> Result<String, String>{

        let length_of_code : usize = mode.clone() as usize;

        let prepared_input : String = processing_input(&raw_input);

        let (fc, sc) = check_correct_input(&raw_input, &prepared_input, length_of_code);

        if !fc || !sc {

            Err("Ошибка. Проверьте корректность ввода.".to_string())

        } else {

            let mut data : String = String::new();

            let prepared_data: Vec<u8> = from_string_to_vec_bits(prepared_input);

            match mode {
                HammingMode::Encrypt => hamming_encrypt_data(&prepared_data, &mut data, length_of_code),
                HammingMode::Decrypt => hamming_decrypt_data(&prepared_data, &mut data, length_of_code),
            }

            return Ok(data);

        }

    }

    pub fn hamming_encrypt_data(
        data: &Vec<u8>,
        result_string: &mut String,
        length_of_code: usize
    ) {
        let mut i : usize = length_of_code;

        while i <= data.len(){

            let data_bits = &data[i - length_of_code..i];
            let (check_bit_1, check_bit_2, check_bit_3) = (
                data_bits[0] ^ data_bits[1] ^ data_bits[3],
                data_bits[0] ^ data_bits[2] ^ data_bits[3],
                data_bits[1] ^ data_bits[2] ^ data_bits[3]
            );
            result_string.push_str(&*format!("{check_bit_1}{}{check_bit_2}{}{check_bit_3}{}{} ",
                                             data_bits[0],
                                             data_bits[1],
                                             data_bits[2],
                                             data_bits[3]));
            i += length_of_code;

        }
    }

    pub fn hamming_decrypt_data(
        data: &Vec<u8>,
        result_string: &mut String,
        length_of_code: usize
    ) {

        let mut i : usize = length_of_code;

        let syndromes : HashMap<usize, (bool, bool, bool)> = HashMap::from(
            [
                (1, (false, true, true)),
                (2, (false, false, true)),
                (3, (true, false, true)),
                (4, (false, true, false)),
                (5, (true, true, false)),
                (6, (true, false, false)),
                (7, (false, false, false)),
            ]
        );

        let mut errors : String = String::new();

        while i <= data.len(){

            let mut data_bits = &data[i - length_of_code..i];

            let checked_bits : (bool, bool, bool) =
                (
                    (data_bits[1] ^ data_bits[3] ^ data_bits[6]) == data_bits[0],
                    (data_bits[1] ^ data_bits[5] ^ data_bits[6]) == data_bits[2],
                    (data_bits[3] ^ data_bits[5] ^ data_bits[6]) == data_bits[4]
                );

            match checked_bits {
                (true, true, true) => {
                    i += length_of_code;
                    continue;
                },
                _ => {

                    let error_position = syndromes
                        .iter()
                        .find(move |&(&error_position, &error)| error == checked_bits).
                        unwrap().0;

                    let correctly_code : Vec<u8> = data_bits
                        .iter()
                        .enumerate()
                        .map(|(index, bit)| {
                        if index == error_position - 1 {
                            if *bit == 1u8 { 0u8 } else { 1u8 }
                        } else {
                            *bit
                        }
                    }).collect();

                    let error = format!("Ошибка в коде {} {:?}, позиция ошибки {}, корректный код: {:?}; \n",
                                    i / 7,
                                    &data_bits,
                                    error_position,
                                    correctly_code
                    );

                    errors.push_str(error.as_str());

                    i += length_of_code;

                }
            }
        }

        if errors.len() == 0 {
            result_string.push_str("Все коды корректны.");
        } else {
            result_string.push_str(errors.as_str())
        }

    }

}