#[allow(non_snake_case)]

use std::ffi::c_uchar;
use std::ffi::c_short;
use std::ffi::c_uint;
use cty;

// the struct must have a C-compatible memory layout
#[repr(C)]
pub struct tm {
    pub tm_sec: cty::c_int,   // seconds after the minute - [0, 60] including leap second
    pub tm_min: cty::c_int,   // minutes after the hour - [0, 59]
    pub tm_hour: cty::c_int,  // hours since midnight - [0, 23]
    pub tm_mday: cty::c_int,  // day of the month - [1, 31]
    pub tm_mon: cty::c_int,   // months since January - [0, 11]
    pub tm_year: cty::c_int,  // years since 1900
    pub tm_wday: cty::c_int,  // days since Sunday - [0, 6]
    pub tm_yday: cty::c_int,  // days since January 1 - [0, 365]
    pub tm_isdst: cty::c_int, // daylight savings time flag
}


#[link(name = "genji")]
unsafe extern "C"{ 
    unsafe fn Init_MatrixAPI()->i16; 
    unsafe fn Release_MatrixAPI()->i16;
    unsafe fn Dongle_Count(DngPort:u16)->i16;
    unsafe fn Dongle_ReadSerNr( UserCode:i32, DngNr:i16, DngPort:i16)->i32;

    unsafe fn Dongle_ReadGUSN( UserCode:i32, GUSN: *const c_uchar, DngNr:i16, DngPort:i16)->i16;
    unsafe fn Dongle_SetLED( mode:i16, dNr:i16, DngPort:i16 )->i16;
    unsafe fn Dongle_GetTime(UserCode:i32, pCurTime: *mut tm, dNr:i16, PortNr:i16)->i16;
    unsafe fn Dongle_GetRand( UserCode:i32, siz:i16, pRand: *const c_uchar, dNr:i16, PortNr:i16 )->i16;

    unsafe fn Dongle_MemSize2( dNr:i16, PortNr:i16)->i16;
    unsafe fn Dongle_WriteData2(UserCode:i32, data: *const c_uchar, pos: i16, len:i16, dNr:i16, PortNr:i16)->i16;
    unsafe fn Dongle_ReadData2(UserCode:i32,  data: *const c_uchar, pos: i16, len:i16, dNr:i16, PortNr:i16)->i16;
    unsafe fn Dongle_LockData2(UserCode:i32,  bLock:i32, lockKey:u32, dNr:i16, PortNr:i16)->i16;
    unsafe fn Dongle_LockData(UserCode:i32,  bLock:i32, lockKey:u32, dNr:i16, PortNr:i16)->i16;

    unsafe fn Dongle_CreateRSAKeyPair( UserCode:i32, idx:i16,  dNr:i16,  PortNr:i16 )->i16;
    unsafe fn Dongle_WriteRSAKey( UserCode:i32, idx:i16, bPrivKey:isize, keySize:i16, pKeyBuf: *const c_uchar, dNr:i16, PortNr:i16)->i16;
    unsafe fn Dongle_LockRSAKeyPair( UserCode:i32, idx:i16, bLock:isize, lockKey:u32, dNr:i16, PortNr:i16 )->i16;
    unsafe fn Dongle_GetRSAPubKey( UserCode:i32, idx:i16, pubKey: *const c_uchar, pubKeyLen: *mut  i16, dNr: i16, PortNr: i16 )->i16;
    unsafe fn EncryptByCOSRsaPubKey( pubKey:*const c_uchar, pubKey_len:i16, plainText:*const c_uchar, plainTextLen:i16, cipher:*const c_uchar, cipherLen:*mut i16)->i16;
    unsafe fn Dongle_EncryptDataRSA( UserCode:i32, idx:i16, plainText:*const c_uchar, plainTextLen:i16, cipher:*const c_uchar, cipherLen:*mut i16, dNr:i16, PortNr:i16 )->i16;
    unsafe fn Dongle_DecryptDataRSA( UserCode:i32, idx:i16, cipher:*const c_uchar, cipherLen:i16, plainText:*const c_uchar, plainTextLen:*mut i16, dNr:i16, PortNr:i16 )->i16;
    unsafe fn Dongle_SignDataRSA( UserCode:i32, idx:i16, msg:*const c_uchar, msgLen:i16, sigData:*const c_uchar, sigLen:*mut i16, dNr:i16, PortNr:i16 )->i16;
    unsafe fn Dongle_VerifySigRSA(UserCode:i32, idx:i16, msg:*const c_uchar, msgLen:i16, sigData:*const c_uchar, sigLen:i16, dNr:i16, PortNr:i16)->i16;

    unsafe fn  Dongle_SetTimer( UserCode:i32,  mode:i16,  dNr:i16, PortNr:i16)->i16;
    unsafe fn  Dongle_StartTimer( UserCode:i32, dNr:i16, PortNr:i16)->i16;
    unsafe fn  Dongle_GetTimer( UserCode:i32,  dwCount:*mut u32,  dNr:i16, PortNr:i16)->i16;
    unsafe fn  Dongle_StopTimer(UserCode:i32, dNr:i16, PortNr:i16)->i16;

    unsafe fn  Dongle_WriteKeyHMACSHA1(UserCode:i32, key:*const c_uchar, dNr:i16, PortNr:i16 )->i16;
    unsafe fn  Dongle_HMACSHA1(UserCode:i32, plainText:*const c_uchar, plainLen:i16, hash:*const c_uchar, hashLen:*mut i16, dNr:i16, PortNr:i16 )->i16;

    unsafe fn  RegisterGenjiCallbackPort( port:i32 );

    unsafe fn  Dongle_WriteData( UserCode:i32,  data:*const u32, count:i16, dNr:i16, PortNr:i16)->i16;
    unsafe fn  Dongle_WriteDataEx(UserCode:i32, data:*const u32, fpos:i16, count:i16, dNr:i16, PortNr:i16)->i16;
    unsafe fn  Dongle_ReadData(UserCode:i32, data:*const u32, count:i16, dNr:i16, PortNr:i16)->i16;
    unsafe fn  Dongle_ReadDataEx(UserCode:i32, data:*const u32, fpos:i16, count:i16, dNr:i16, PortNr:i16)->i16;
    unsafe fn  Dongle_WriteKey(UserCode:i32, key:*const u32,dNr:i16, PortNr:i16)->i16;

    unsafe fn Dongle_EncryptData(UserCode:i32, DataBlockdata:*const u32, dNr:i16, PortNr:i16)->i16;
    unsafe fn Dongle_DecryptData(UserCode:i32, DataBlockdata:*const u32, dNr:i16, PortNr:i16)->i16;

}

pub struct Api{
    user_code:i32,
}

impl Api{
    pub fn new( uc: i32) -> Self{
        Self
         { 
            user_code : uc,
         }
    }
   pub fn init(&self) -> i16{
       unsafe{ Init_MatrixAPI() }
    }

   pub fn release(&self) -> i16{
       unsafe{ Release_MatrixAPI() }
    }

    pub fn count(&self) -> i16{
       unsafe{ Dongle_Count(85) }
    }

    pub fn sernr(&self, dng_nr:i16) -> i32{
       unsafe{ Dongle_ReadSerNr(self.user_code, dng_nr,85) }
    }

    pub fn gusn(&self, gusn: &mut String, dng_nr:i16) -> i16{
       let buffer = vec![0u8; 16];
       let ret :i16;
       unsafe{ ret = Dongle_ReadGUSN(self.user_code, buffer.as_ptr(), dng_nr,85) }
       *gusn = String::from_utf8(buffer).unwrap();
       ret
    }

    pub fn set_led( &self, mode:i16, dng_nr:i16 )->i16{
       unsafe { Dongle_SetLED( mode, dng_nr, 85 ) }
    }

    pub fn get_time(&self, cur_time: *mut tm, dng_nr:i16)->i16{
        unsafe { Dongle_GetTime(self.user_code, cur_time, dng_nr, 85) }
    }

    pub fn get_rand( &self, siz:i16, rand: &mut [u8], dng_nr:i16 )->i16{
       unsafe { Dongle_GetRand( self.user_code, siz, rand.as_ptr(), dng_nr, 85 ) }
    }

    pub fn memsize2( &self, dng_nr:i16)->i16{
        unsafe { Dongle_MemSize2( dng_nr, 86) }
    }

    pub fn lock_data2( &self, lock_flag:i32, lock_key:u32, dng_nr:i16)->i16{
        unsafe { Dongle_LockData2(self.user_code, lock_flag, lock_key, dng_nr, 85) }
    }

    pub fn lock_data( &self, lock_flag:i32, lock_key:u32, dng_nr:i16)->i16{
        unsafe { Dongle_LockData(self.user_code, lock_flag, lock_key, dng_nr, 85) }
    }

    pub fn write_data2(&self, data:&[u8], pos:i16, len:i16, dng_nr:i16)->i16{
        unsafe { Dongle_WriteData2( self.user_code, data.as_ptr(), pos, len, dng_nr, 85) }
    }

    pub fn read_data2( &self, data:&mut[u8], pos:i16, len:i16, dng_nr:i16)->i16{
        unsafe { Dongle_ReadData2(self.user_code, data.as_ptr(), pos, len, dng_nr, 85) }
    }

    pub fn create_keypair( &self, idx:i16, dng_nr:i16)->i16{
        unsafe { Dongle_CreateRSAKeyPair( self.user_code, idx,  dng_nr,  85 ) }
    }

    pub fn write_rsakey( &self, idx:i16, is_privkey:isize, key_size:i16, key_buf:&[u8], dng_nr:i16 )->i16{
        unsafe { Dongle_WriteRSAKey( self.user_code, idx, is_privkey, key_size, key_buf.as_ptr(), dng_nr, 85) }
    }

    pub fn lock_rsakeypair( &self, idx:i16, is_lock:isize, lock_key:u32, dng_nr:i16)->i16 {
        unsafe { Dongle_LockRSAKeyPair( self.user_code, idx, is_lock, lock_key, dng_nr, 85 ) }
    }

    pub fn get_rsapubkey( &self, idx:i16, pub_key:&mut [u8], key_len:&mut i16, dng_nr:i16)->i16{
         // get a mutable raw pointer to the integer
        let int_ptr: *mut c_short = key_len as *mut c_short;
        unsafe { Dongle_GetRSAPubKey( self.user_code, idx, pub_key.as_ptr(), int_ptr, dng_nr, 85 ) }
    }
    
    pub fn encrypt_by_cos_rsapubkey( &self,pub_key:&[u8], pub_len:i16, plain_text:&[u8], plain_len:i16, cipher:&mut [u8], cipher_len:&mut i16)->i16{
         // get a mutable raw pointer to the integer
        let int_ptr: *mut c_short = cipher_len as *mut c_short;
        unsafe { EncryptByCOSRsaPubKey( pub_key.as_ptr(), pub_len, plain_text.as_ptr(), plain_len, cipher.as_ptr(), int_ptr) }
    }

    pub fn encrypt_datarsa( &self, idx:i16, plain_text:&[u8], plain_len:i16, cipher:&mut [u8], cipher_len:&mut i16, dng_nr:i16 )->i16{
        // get a mutable raw pointer to the integer
        let int_ptr: *mut c_short = cipher_len as *mut c_short;
        unsafe { Dongle_EncryptDataRSA( self.user_code, idx, plain_text.as_ptr(), plain_len, cipher.as_ptr(), int_ptr, dng_nr, 85 ) }
    }

    pub fn decrypt_datarsa( &self, idx:i16, cipher:&[u8], cipher_len:i16, plain:&mut [u8], plain_len:&mut i16, dng_nr:i16 )->i16{
        // get a mutable raw pointer to the integer
        let int_ptr: *mut c_short = plain_len as *mut c_short;
        unsafe { Dongle_DecryptDataRSA( self.user_code, idx, cipher.as_ptr(), cipher_len, plain.as_ptr(), int_ptr, dng_nr, 85 ) }
    }

    pub fn sign_datarsa( &self, idx:i16, msg:&[u8], msg_len:i16, sig:&mut [u8], sig_len:&mut i16, dng_nr:i16 )->i16{
        // get a mutable raw pointer to the integer
        let int_ptr: *mut c_short = sig_len as *mut c_short;
        unsafe { Dongle_SignDataRSA( self.user_code, idx, msg.as_ptr(), msg_len, sig.as_ptr(), int_ptr, dng_nr, 85) }
    }

    pub fn verify_sigrsa( &self, idx:i16, msg:&[u8], msg_len:i16, sig:&[u8], sig_len:i16, dng_nr:i16 )->i16{
        unsafe { Dongle_VerifySigRSA( self.user_code, idx, msg.as_ptr(), msg_len, sig.as_ptr(), sig_len, dng_nr, 85) }
    }

    pub fn set_timer( &self, mode:i16, dng_nr:i16)->i16 {
        unsafe { Dongle_SetTimer( self.user_code,  mode,  dng_nr, 85) }
    }

    pub fn start_timer( &self, dng_nr:i16)->i16 {
        unsafe { Dongle_StartTimer( self.user_code, dng_nr, 85) }
    }

    pub fn get_timer( &self, count:&mut u32, dng_nr:i16)->i16 {
        // get a mutable raw pointer to the integer
        let int_ptr: *mut c_uint = count as *mut c_uint;
        unsafe {  Dongle_GetTimer( self.user_code,  int_ptr,  dng_nr, 85) }
    }
    pub fn stop_timer( &self, dng_nr:i16)->i16 {
        unsafe { Dongle_StopTimer(self.user_code, dng_nr, 85) }
    }

    // key must be at least 25bytes long
    pub fn write_key_hmac_sha1( &self, key:&[u8],  dng_nr:i16 )->i16{
        unsafe {  Dongle_WriteKeyHMACSHA1(self.user_code, key.as_ptr(), dng_nr, 85 ) }
    }

    pub fn hmac_sha1( &self, msg:&[u8], msg_len:i16, hash:&mut [u8], hash_len:&mut i16, dng_nr:i16 )->i16{
        // get a mutable raw pointer to the integer
        let int_ptr: *mut c_short = hash_len as *mut c_short;
        unsafe { Dongle_HMACSHA1(self.user_code, msg.as_ptr(), msg_len, hash.as_ptr(), int_ptr, dng_nr, 85 ) }
    }

    pub fn register_genji_callback_port(&self, port:i32){
      unsafe { RegisterGenjiCallbackPort( port ) }
    }

    pub fn write_data(&self, data:&[u32], count:i16, dng_nr:i16)->i16{
        unsafe {  Dongle_WriteData( self.user_code,  data.as_ptr(), count, dng_nr, 85) }
    }

    pub fn write_dataex(&self, data:&[u32], fpos:i16, count:i16, dng_nr:i16)->i16{
        unsafe { Dongle_WriteDataEx(self.user_code, data.as_ptr(), fpos, count, dng_nr, 85) }
    }

    pub fn read_data(&self, data:&[u32], count:i16, dng_nr:i16)->i16{
        unsafe { Dongle_ReadData(self.user_code, data.as_ptr(), count, dng_nr, 85) }
    }

    pub fn read_dataex(&self, data:&[u32], fpos:i16, count:i16, dng_nr:i16)->i16{
        unsafe { Dongle_ReadDataEx(self.user_code, data.as_ptr(), fpos, count, dng_nr, 85 ) }
    }

    pub fn write_key(&self, key:&[u32;4], dng_nr:i16)->i16{
        unsafe { Dongle_WriteKey(self.user_code, key.as_ptr(), dng_nr, 85) }
    }

    pub fn encrypt_data(&self, data:&[u32;2], dng_nr:i16)->i16{
        unsafe { Dongle_EncryptData(self.user_code, data.as_ptr(), dng_nr, 85) }
    }

    pub fn decrypt_data(&self, data:&[u32;2], dng_nr:i16)->i16{
        unsafe { Dongle_DecryptData(self.user_code, data.as_ptr(), dng_nr, 85) }
    }
}



