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);
    }
}

fn ssv_lire_carte_ps() {
    let resource_ps = CString::new("PS").expect("CString::new failed");
    let resource_reader = CString::new("TRANSPA1").expect("CString::new failed");
    let card_number = CString::new("1234567890").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);
            for &byte in hex_values {
                print!("{:02X} ", byte);
            }
            println!();

            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`
    for &octet in bytes {
        println!("Octet: {}", octet);
    }

    let mut current_pos_general = 0;
    let mut current_groupe = 0;
    let mut num_champ = 0;

    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 => println!("Groupe1.Champ1: {}", String::from_utf8_lossy(&bytes_champ)),
                        2 => println!("Groupe1.Champ2: {}", String::from_utf8_lossy(&bytes_champ)),
                        3 => println!("Groupe1.Champ3: {}", String::from_utf8_lossy(&bytes_champ)),
                        4 => println!("Groupe1.Champ4: {}", String::from_utf8_lossy(&bytes_champ)),
                        5 => println!("Groupe1.Champ5: {}", String::from_utf8_lossy(&bytes_champ)),
                        6 => println!("Groupe1.Champ6: {}", String::from_utf8_lossy(&bytes_champ)),
                        7 => println!("Groupe1.Champ7: {}", String::from_utf8_lossy(&bytes_champ)),
                        8 => println!("Groupe1.Champ8: {}", String::from_utf8_lossy(&bytes_champ)),
                        _ => (),
                    },
                    2 => match num_champ {
                        1 => {
                            // Ajoutez votre logique pour Groupe2 ici
                        }
                        2 => {
                            // Ajoutez votre logique pour Groupe2 ici
                        }
                        3 => {
                            // Ajoutez votre logique pour Groupe2 ici
                        }
                        _ => (),
                    },
                    _ => (),
                }
            }

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

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!("-----------------------------------------");
}