use std::io;
use std::fs::File;
use std::io::Read;

use crate::genji;
use super::timer;
use super::util;

fn rsa_sign_verify(_api: &genji::Api, idx:i16, msg:&str, dng_nr:i16)->bool {

    let msg_bytes  = msg.as_bytes().to_vec();
    let mut sig   = [0u8; 129];
    let mut sig_len=129;

    let mut timer = timer::GenjiTimer {  mode:0,  api: _api, started:false, counter: 0};

    timer.start(dng_nr);
    let mut ret = _api.sign_datarsa( idx, &msg_bytes.to_vec(), msg_bytes.len() as i16, &mut sig, &mut sig_len, dng_nr );
    timer.stop(dng_nr);

    if 1 != ret {
        println!("     Error in signing");
        return false
    }

    println!("\n    ... Signature signed by the key at index {}  Processing Time: {} msec", idx, timer.counter);
    let sub: Vec<_> = sig.iter().take(sig_len as usize).collect();
    println!("    {:x?}", sub);
    util::pause();

    //verify
    timer.start(dng_nr);
    ret = _api.verify_sigrsa( idx, &msg_bytes.to_vec(), msg_bytes.len() as i16, &sig, sig_len, dng_nr );
    timer.stop(dng_nr);

    if 1 != ret {
        println!("     Error in verifying signature");
        return false
    }

    println!("\n     ---> Ok, verified by the key at index {} Processing Time: {} msec", idx, timer.counter);
    util::pause();
    
    true
}


fn rsa_encdec(_api: &genji::Api, idx:i16, plain:&str, dng_nr:i16)->bool {

    let plain_bytes  = plain.as_bytes().to_vec();
    let mut cipher   = [0u8; 258];
    let mut cipher_len=258;

    let mut timer = timer::GenjiTimer {  mode:0,  api: _api, started:false, counter: 0};

    timer.start(dng_nr);
    let mut ret = _api.encrypt_datarsa( idx, &plain_bytes.to_vec(), plain_bytes.len() as i16, &mut cipher, &mut cipher_len, dng_nr );
    timer.stop(dng_nr);

    if 1 != ret {
        println!("     Error in encrypting");
        return false
    }

    println!("\n    ... Encryption:  successfully encrypted the given text using the key at index {}  Processing time: {} msec", idx, timer.counter);
    let sub: Vec<_> = cipher.iter().take(cipher_len as usize).collect();
    println!("    {:x?}", sub);
    util::pause();

    let mut plain1_bytes    = [0u8; 258];
    let mut plain1_bytes_len=258;

    timer.start(dng_nr);
    ret = _api.decrypt_datarsa( idx, &cipher[..], cipher_len, &mut plain1_bytes, &mut plain1_bytes_len, dng_nr );
    timer.stop(dng_nr);

    if 1 != ret {
        println!("     Error in decrypting");
        return false
    }

    println!("\n     ---> Decryption: successfully decrypted the cipher data using the key at index {} Processing Time:{} msec", idx, timer.counter);

    //convert bytes to string and display
    println!("    {}",String::from_utf8(plain1_bytes[..plain1_bytes_len as usize].to_vec()).unwrap() );
    util::pause();

    true
}

fn create_keypair( _api: &genji::Api, idx: i16, dng_nr:i16)->bool {

    println!("\n   ... Creating a key pair at index {}",idx);

    let mut timer = timer::GenjiTimer {  mode:0,  api: _api, started:false, counter: 0};

    timer.start(dng_nr);
    let ret = _api.create_keypair( idx, dng_nr);
    timer.stop(dng_nr);

    if ret != 1 {
        println!( "   xxx> Failed to create a keypair at index 1");
        return false;
    }
 
    println!( "     ---> Successfully created the key pair at index {}  Processing Time: {} msec\n", idx, timer.counter);
    true
}

fn import_keypair(_api: &genji::Api, idx: i16,  file_name:&str, is_privkey: isize, dng_nr:i16)->bool
{
    let f = File::open( file_name);
    let mut f1;

     match f {
        Ok(file) => { f1 = file },
        Err(_error) => {
            return false;
        },
    };

    let mut buf = Vec::new();
    let r = f1.read_to_end(&mut buf);
    match r {
        Ok( _size) => {},
        Err(_error) => {
            return false
        },
    };

    let ret = _api.write_rsakey( idx, is_privkey, buf.len() as i16, &buf[..], dng_nr);
    if 1 != ret {
        println!( "   Failed to write the key from the file {} ", file_name);
        return false;
    }
    println!( "     --> Successful. The key from the file {} has been written at key index {}\n", file_name, idx);
    true
}

pub fn rsa_encdec_test(_api: &genji::Api, dng_nr:i16)->bool {

    println!("   ******* RSA Encrypt/Decrypt Test ********");

    // set RSA key pair at RSA key index 1
    if false == create_keypair( &_api, 1, dng_nr) {
        return false;
    }
    util::pause();

    // load the file "genji.privkey" to Genji's RSA key index 2
    println!( "   ... Trying to import 'genji.privkey'");
    if false == import_keypair(&_api, 2,  "genji.privkey", 1, dng_nr) {
        return false;
    }
    util::pause();

    // load the file "genji.pubkey" to Genji's RSA key index 2
    println!( "   ... Trying to import 'genji.pubkey'");
    if false == import_keypair(&_api, 2,  "genji.pubkey", 0, dng_nr) {
        return false;
    }
    util::pause();

    // Encrypt using the key pairs at key index 1 and 2 in turn
    println!("\n   Enter text to encrypt( max. 117bytes):");

    let mut input = String::new();

    // read from stdin
    io::stdin()
        .read_line(&mut input)
        .expect("failed to read from stdin");

    if input.trim().len() == 0 {
        println!("   Skipping RSA encrypt/decrypt test");
        return true;
    }

    //encrypt/decrypt test using Key Index 1
    if rsa_encdec(&_api, 1, input.trim(), dng_nr) == false {
        return false;
    }

    //encrypt/decrypt test using Key Index 2
    if rsa_encdec(&_api, 2, input.trim(), dng_nr) == false {
        return false;
    }

    println!("\n    Since you have a public key in 'genji.pubkey', it is possible to encrypt externally using it and let the private key at Genji's key index 2 decrypt" );
    println!("    Let's try" );
    util::pause();
    util::pause();

    // read "genji.pubkey"
    let f = File::open( "genji.pubkey" );
    let mut f1;

     match f {
        Ok(file) => { f1 = file },
        Err(error) => {
            println!("  xxx file open error xxx {}", error);
            return false;
        },
    };

    let mut buf = Vec::new();
    let r = f1.read_to_end(&mut buf);
    match r {
        Ok( _size) => {},
        Err(error) => {
            println!("  xxx file read error xxx {}", error);
            return false
        },
    };
    println!("    -->Read from 'genji.pubkey");
    util::pause();

    //encrypt using the public key in "buf"
    let plain = "encrypt externally and decrypt internally";
    let plain_bytes  = plain.as_bytes().to_vec();
    let mut cipher   = [0u8; 258];
    let mut cipher_len=258;

    if 1 != _api.encrypt_by_cos_rsapubkey( &buf[..], buf.len() as i16, &plain_bytes[..], plain_bytes.len() as i16, &mut cipher, &mut cipher_len){
        println!( "    cos_rsapubkey encrypt error");
        return false;
    }

    println!("    -->Successfully encrypted by the public key from the file");
    util::pause();

    //decrypt internally by the private key at key index 2
    let mut plain1_bytes    = [0u8; 258];
    let mut plain1_bytes_len=258;

    let ret = _api.decrypt_datarsa( 2, &cipher[..], cipher_len, &mut plain1_bytes, &mut plain1_bytes_len, dng_nr );

    if 1 != ret {
        println!("     Error in decrypting");
        return false
    }

    println!("\n     ---> Successfully decrypted by the private key at index 2");

    //convert bytes to string and display
    println!("    {}",String::from_utf8(plain1_bytes[..plain1_bytes_len as usize].to_vec()).unwrap() );
    util::pause();



    true
}

pub fn rsa_sig_verify_test(_api: &genji::Api, dng_nr:i16)->bool {

    println!("\n   ******* RSA Sign/Verify Test ********");

    println!("\n   Enter text to sign:");

    let mut input = String::new();

    // read from stdin
    io::stdin()
        .read_line(&mut input)
        .expect("failed to read from stdin");

    if input.trim().len() == 0 {
        println!("   Skipping Sign/Verify test");
        return true;
    }

    //sing/verify test using Key Index 1
    if false == rsa_sign_verify(&_api, 1, input.trim(), dng_nr){
        return false;
    }

    //sing/verify test using Key Index 2
    if false == rsa_sign_verify(&_api, 2, input.trim(), dng_nr){
        return false;
    }

    true

}

