extern crate dotenv;
/**
 * High level API for the SSV library,
 * based on the low level bindings in libssv.rs.
 *
 */
extern crate libc;

use libc::{c_void, size_t};
use std::ffi::CString;
use std::path::PathBuf;
use std::ptr;

use std::env;

use crate::libssv::{SSV_InitLIB2, SSV_LireCartePS, SSV_LireConfig};

fn ssv_init_lib_2() {
    let ini_str = env::var("SESAM_INI_PATH").expect("SESAM_INI_PATH must be set");
    let ini = CString::new(ini_str).expect("CString::new failed");
    unsafe {
        let result = SSV_InitLIB2(ini.as_ptr());
        println!("SSV_InitLIB2 result: {}", result);
    }
}

/*
1. CB = Caractères Binaires »
2. CE = Caractères « Etendus » (ISO 8859-1)
3. CA = Caractères Alphanumériques (ASCII?)
4. CN = Caractères Numériques
*/

#[derive(Debug)]
struct CartePS {
    titulaire: TitulairePS,
    situations: Vec<SituationPS>,
}
#[derive(Debug)]
struct TitulairePS {
    type_de_carte_ps: String,                         // CN
    type_d_identification_nationale: String,          // CN
    numero_d_identification_nationale: String,        // CE - 8 -> 30
    cle_du_numero_d_identification_nationale: String, // CN
    code_civilite: String,                            // CN
    nom_du_ps: String,                                // CE - 27
    prenom_du_ps: String,                             // CE - 27
    categorie_carte: char,                            // CA
}

#[derive(Debug)]
struct SituationPS {
    numero_logique_de_la_situation_de_facturation_du_ps: u8,
    mode_d_exercice: String,
    statut_d_exercice: String,
    secteur_d_activite: String,
    type_d_identification_structure: String,
    numero_d_identification_structure: String,
    cle_du_numero_d_identification_structure: String,
    raison_sociale_structure: String,
    numero_d_identification_de_facturation_du_ps: String,
    cle_du_numero_d_identification_de_facturation_du_ps: String,
    numero_d_identification_du_ps_remplaçant: String,
    cle_du_numero_d_identification_du_ps_remplaçant: String,
    code_conventionnel: String,
    code_specialite: String,
    code_zone_tarifaire: String,
    code_zone_ik: String,
    code_agrement_1: String,
    code_agrement_2: String,
    code_agrement_3: String,
    habilitation_à_signer_une_facture: String,
    habilitation_à_signer_un_lot: String,
}

impl Default for SituationPS {
    fn default() -> Self {
        Self {
            numero_logique_de_la_situation_de_facturation_du_ps: 0,
            mode_d_exercice: String::new(),
            statut_d_exercice: String::new(),
            secteur_d_activite: String::new(),
            type_d_identification_structure: String::new(),
            numero_d_identification_structure: String::new(),
            cle_du_numero_d_identification_structure: String::new(),
            raison_sociale_structure: String::new(),
            numero_d_identification_de_facturation_du_ps: String::new(),
            cle_du_numero_d_identification_de_facturation_du_ps: String::new(),
            numero_d_identification_du_ps_remplaçant: String::new(),
            cle_du_numero_d_identification_du_ps_remplaçant: String::new(),
            code_conventionnel: String::new(),
            code_specialite: String::new(),
            code_zone_tarifaire: String::new(),
            code_zone_ik: String::new(),
            code_agrement_1: String::new(),
            code_agrement_2: String::new(),
            code_agrement_3: String::new(),
            habilitation_à_signer_une_facture: String::new(),
            habilitation_à_signer_un_lot: String::new(),
        }
    }
}

fn ssv_lire_carte_ps() {
    let resource_ps =
        CString::new("HID Global OMNIKEY 3x21 Smart Card Reader 0").expect("CString::new failed");
    let resource_reader = CString::new("").expect("CString::new failed");
    let card_number = CString::new("1234").expect("CString::new failed");

    let mut buffer: *mut c_void = ptr::null_mut();
    let mut size: size_t = 0;
    let mut hex_values: &[u8] = &[];
    unsafe {
        let result = SSV_LireCartePS(
            resource_ps.as_ptr(),
            resource_reader.as_ptr(),
            card_number.as_ptr(),
            &mut buffer,
            &mut size,
        );
        println!("SSV_LireCartePS result: {}", result);

        if !buffer.is_null() {
            hex_values = std::slice::from_raw_parts(buffer as *const u8, size);
            libc::free(buffer);
        }
    }
    decode_zone_memoire(hex_values);
}

pub fn decode_zone_memoire(bytes: &[u8]) {
    // Maintenant, vous pouvez accéder aux octets individuels dans `donnees`
    let mut current_pos_general = 0;
    let mut current_groupe = 0;
    let mut num_champ = 0;

    let mut carte_ps = CartePS {
        titulaire: TitulairePS {
            type_de_carte_ps: String::new(),
            type_d_identification_nationale: String::new(),
            numero_d_identification_nationale: String::new(),
            cle_du_numero_d_identification_nationale: String::new(),
            code_civilite: String::new(),
            nom_du_ps: String::new(),
            prenom_du_ps: String::new(),
            categorie_carte: ' ',
        },
        situations: Vec::new(),
    };

    while current_pos_general < bytes.len() - 1 {
        num_champ = 0;
        current_groupe =
            256 * bytes[current_pos_general] as i32 + bytes[current_pos_general + 1] as i32;
        current_pos_general += 2;

        let longueur_groupe: usize;
        if bytes[current_pos_general] < 128 {
            longueur_groupe = bytes[current_pos_general] as usize;
        } else {
            let nbre_octets_longueur = bytes[current_pos_general] - 128;
            longueur_groupe = (0..nbre_octets_longueur).fold(0, |acc, i| {
                current_pos_general += 1;
                acc + (256_i32.pow(i as u32) * bytes[current_pos_general] as i32) as usize
            });
        }
        current_pos_general += 1;

        let mut current_pos_groupe = 0;
        while current_pos_groupe < longueur_groupe {
            num_champ += 1;
            let longueur_champs: usize;
            let nbre_octets_longueur: usize;
            if bytes[current_pos_general] < 128 {
                longueur_champs = bytes[current_pos_general] as usize;
                nbre_octets_longueur = 1;
            } else {
                nbre_octets_longueur = bytes[current_pos_general] as usize - 128;
                longueur_champs = (0..nbre_octets_longueur).fold(0, |acc, i| {
                    current_pos_general += 1;
                    current_pos_groupe += 1;
                    acc + (256_i32.pow(i as u32) * bytes[current_pos_general] as i32) as usize
                });
            }

            let mut bytes_champ = vec![0; longueur_champs];
            if longueur_champs > 0 {
                bytes_champ.copy_from_slice(
                    &bytes[current_pos_general + nbre_octets_longueur
                        ..current_pos_general + nbre_octets_longueur + longueur_champs],
                );

                match current_groupe {
                    1 => match num_champ {
                        1 => {
                            carte_ps.titulaire.type_de_carte_ps =
                                String::from_utf8_lossy(&bytes_champ).to_string();
                        }
                        2 => {
                            carte_ps.titulaire.type_d_identification_nationale =
                                String::from_utf8_lossy(&bytes_champ).to_string();
                        }
                        3 => {
                            carte_ps.titulaire.numero_d_identification_nationale =
                                String::from_utf8_lossy(&bytes_champ).to_string();
                        }
                        4 => {
                            carte_ps.titulaire.cle_du_numero_d_identification_nationale =
                                String::from_utf8_lossy(&bytes_champ).to_string();
                        }
                        5 => {
                            carte_ps.titulaire.code_civilite =
                                String::from_utf8_lossy(&bytes_champ).to_string();
                        }
                        6 => {
                            carte_ps.titulaire.nom_du_ps =
                                String::from_utf8_lossy(&bytes_champ).to_string();
                        }
                        7 => {
                            carte_ps.titulaire.prenom_du_ps =
                                String::from_utf8_lossy(&bytes_champ).to_string();
                        }
                        8 => {
                            carte_ps.titulaire.categorie_carte = bytes_champ[0] as char;
                        }
                        _ => (),
                    },
                    2 => match num_champ {
                        1 => {
                            carte_ps.situations.push(SituationPS::default());
                            carte_ps
                                .situations
                                .last_mut()
                                .unwrap()
                                .numero_logique_de_la_situation_de_facturation_du_ps =
                                bytes_champ[0];
                        }
                        2 => {
                            carte_ps.situations.last_mut().unwrap().mode_d_exercice =
                                String::from_utf8_lossy(&bytes_champ).to_string();
                        }
                        3 => {
                            carte_ps.situations.last_mut().unwrap().statut_d_exercice =
                                String::from_utf8_lossy(&bytes_champ).to_string();
                        }
                        4 => {
                            carte_ps.situations.last_mut().unwrap().secteur_d_activite =
                                String::from_utf8_lossy(&bytes_champ).to_string();
                        }
                        5 => {
                            carte_ps
                                .situations
                                .last_mut()
                                .unwrap()
                                .type_d_identification_structure =
                                String::from_utf8_lossy(&bytes_champ).to_string();
                        }
                        6 => {
                            carte_ps
                                .situations
                                .last_mut()
                                .unwrap()
                                .numero_d_identification_structure =
                                String::from_utf8_lossy(&bytes_champ).to_string();
                        }
                        7 => {
                            carte_ps
                                .situations
                                .last_mut()
                                .unwrap()
                                .cle_du_numero_d_identification_structure =
                                String::from_utf8_lossy(&bytes_champ).to_string();
                        }
                        8 => {
                            carte_ps
                                .situations
                                .last_mut()
                                .unwrap()
                                .raison_sociale_structure =
                                String::from_utf8_lossy(&bytes_champ).to_string();
                        }
                        9 => {
                            carte_ps
                                .situations
                                .last_mut()
                                .unwrap()
                                .numero_d_identification_de_facturation_du_ps =
                                String::from_utf8_lossy(&bytes_champ).to_string();
                        }
                        10 => {
                            carte_ps
                                .situations
                                .last_mut()
                                .unwrap()
                                .cle_du_numero_d_identification_de_facturation_du_ps =
                                String::from_utf8_lossy(&bytes_champ).to_string();
                        }
                        11 => {
                            carte_ps
                                .situations
                                .last_mut()
                                .unwrap()
                                .numero_d_identification_du_ps_remplaçant =
                                String::from_utf8_lossy(&bytes_champ).to_string();
                        }
                        12 => {
                            carte_ps
                                .situations
                                .last_mut()
                                .unwrap()
                                .cle_du_numero_d_identification_du_ps_remplaçant =
                                String::from_utf8_lossy(&bytes_champ).to_string();
                        }
                        13 => {
                            carte_ps.situations.last_mut().unwrap().code_conventionnel =
                                String::from_utf8_lossy(&bytes_champ).to_string();
                        }
                        14 => {
                            carte_ps.situations.last_mut().unwrap().code_specialite =
                                String::from_utf8_lossy(&bytes_champ).to_string();
                        }
                        15 => {
                            carte_ps.situations.last_mut().unwrap().code_zone_tarifaire =
                                String::from_utf8_lossy(&bytes_champ).to_string();
                        }
                        16 => {
                            carte_ps.situations.last_mut().unwrap().code_zone_ik =
                                String::from_utf8_lossy(&bytes_champ).to_string();
                        }
                        17 => {
                            carte_ps.situations.last_mut().unwrap().code_agrement_1 =
                                String::from_utf8_lossy(&bytes_champ).to_string();
                        }
                        18 => {
                            carte_ps.situations.last_mut().unwrap().code_agrement_2 =
                                String::from_utf8_lossy(&bytes_champ).to_string();
                        }
                        19 => {
                            carte_ps.situations.last_mut().unwrap().code_agrement_3 =
                                String::from_utf8_lossy(&bytes_champ).to_string();
                        }
                        20 => {
                            carte_ps.situations.last_mut().unwrap().habilitation_à_signer_une_facture =
                                String::from_utf8_lossy(&bytes_champ).to_string();
                        }
                        21 => {
                            carte_ps.situations.last_mut().unwrap().habilitation_à_signer_un_lot =
                                String::from_utf8_lossy(&bytes_champ).to_string();
                        }
                        _ => (),
                    },
                    _ => (),
                }
            }

            current_pos_general += longueur_champs + nbre_octets_longueur;
            current_pos_groupe += longueur_champs + nbre_octets_longueur;
        }
    }

    println!("CartePS: {:#?}", carte_ps);
}

fn ssv_lire_config() {
    let mut buffer: *mut c_void = ptr::null_mut();
    let mut size: size_t = 0;
    unsafe {
        let result = SSV_LireConfig(&mut buffer, &mut size);
        println!("SSV_LireConfig result: {}", result);

        if !buffer.is_null() {
            let hex_values = std::slice::from_raw_parts(buffer as *const u8, size);
            for &byte in hex_values {
                print!("{:02X} ", byte);
            }
            println!();

            libc::free(buffer);
        }
    }
}

pub fn demo() {
    /*
     * TODO : this is probably not working on release, because I'm not sure it exists a CARGO_MANIFEST_DIR and so it can find the `.env`
     * Maybe we could use a system standard config path to store a config file
     */
    let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
    let manifest_path = PathBuf::from(manifest_dir);
    dotenv::from_path(manifest_path.join(".env")).ok();

    println!("------- Demo for the SSV library --------");

    ssv_init_lib_2();
    ssv_lire_carte_ps();
    // ssv_lire_config();

    println!("-----------------------------------------");
}
