/*-
 * Scram-rs - a SCRAM authentification authorization library
 * 
 * Copyright (C) 2021  Aleksandr Morozov
 * Copyright (C) 2025 Aleksandr Morozov
 * 
 * The syslog-rs crate can be redistributed and/or modified
 * under the terms of either of the following licenses:
 *
 *   1. the Mozilla Public License Version 2.0 (the “MPL”) OR
 *
 *   2. The MIT License (MIT)
 *                     
 *   3. EUROPEAN UNION PUBLIC LICENCE v. 1.2 EUPL © the European Union 2007, 2016
 */



#[cfg(feature = "std")]
use std::{mem, ffi::{c_char, CStr}};
#[cfg(not(feature = "std"))]
use core::{mem, ffi::{c_char, CStr}};

#[cfg(not(feature = "std"))]
use alloc::boxed::Box;

use crate::{capi_scram_err, capi_scram_err_map, scram_ierror_map, ScramErrorCode, ScramNonce, ScramServerError};

use super::{common, error::{CApiScramResult, CApiScramRuntimeError}};

#[repr(C)]
pub struct CApiNonce(ScramNonce);


impl CApiNonce
{
    pub unsafe 
    fn take(&mut self) -> CApiScramResult<ScramNonce>
    {
        let res = mem::replace(&mut self.0, ScramNonce::empty());

        if res == ScramNonce::empty()
        {
            capi_scram_err!(ScramErrorCode::InternalError, ScramServerError::None, "ScramNonce was consumed! Reinit.")
        }
        else
        {
            return Ok(res);
        }
    }
}

#[unsafe(no_mangle)]
pub unsafe extern "C" 
fn capi_scram_nonce_free(nonce: *mut CApiNonce)
{
    if nonce.is_null() == true
    {
        return;
    }

    drop(unsafe { Box::from_raw(nonce) });

    return;
}

#[unsafe(no_mangle)]
pub unsafe extern "C" 
fn capi_scram_nonce_none(nonce: *mut *mut CApiNonce, error: *mut *mut CApiScramRuntimeError) -> i32
{
    if nonce.is_null() == true
    {
        return -1;
    }
    
    match ScramNonce::none()
    {
        Ok(non) => 
        {
            unsafe { *nonce.as_mut().unwrap() = Box::into_raw(Box::new(CApiNonce(non))) }; 
            
            return 0;
        },
        Err(e) =>
        {
            if error.is_null() == false
            {
                unsafe { *error.as_mut().unwrap() = capi_scram_err_map!(e).into_capi() };
            }

            return 1;
        }
    }
}

#[unsafe(no_mangle)]
pub unsafe extern "C" 
fn capi_scram_nonce_plain(nonce: *mut *mut CApiNonce, plain_nonce: *const u8, plain_nonce_len: usize, 
    error_out: *mut *mut CApiScramRuntimeError) -> i32
{
    if nonce.is_null() == true
    {
        return -1;
    }
    else if plain_nonce.is_null() == true
    {
        return -1;
    }
    
    let plain = 
        match 
            common::carr_to_slice(plain_nonce, plain_nonce_len, "plain_nonce invalid")
        {
            Ok(r) => r,
            Err(e) => 
            {
                unsafe 
                { 
                    if let Some(err) = error_out.as_mut()
                    {
                        *err = e.into_capi();
                    }
                }

                return 1;
            }
        };
    
    match ScramNonce::plain(plain)
    {
        Ok(non) => 
        {
            unsafe { *nonce.as_mut().unwrap() = Box::into_raw(Box::new(CApiNonce(non))) };
            
            return 0;
        },
        Err(e) =>
        {
            unsafe 
            { 
                if let Some(err) = error_out.as_mut()
                {
                    *err = capi_scram_err_map!(e).into_capi();
                }
            }

            return 1;
        }
    }
}

#[unsafe(no_mangle)]
pub unsafe extern "C" 
fn capi_scram_nonce_base64(nonce: *mut *mut CApiNonce, base64_nonce: *const c_char, 
    error: *mut *mut CApiScramRuntimeError) -> i32
{
    if nonce.is_null() == true
    {
        return -1;
    }
    else if base64_nonce.is_null() == true
    {
        return -1;
    }
    
    let plain = 
        unsafe 
        { 
            match 
                CStr::from_ptr(base64_nonce)
                    .to_str()
                    .map_err(|e|
                        scram_ierror_map!(ScramErrorCode::InternalError, "base64 nonce UTF-8 error: '{}", e)
                    )
            {
                Ok(r) => r,
                Err(e) =>
                {
                    if error.is_null() == false
                    {
                        *error.as_mut().unwrap() = capi_scram_err_map!(e).into_capi();
                    }

                    return 1;
                }
            }
        };
    
    
    match ScramNonce::base64(plain)
    {
        Ok(non) => 
        {
            unsafe { *nonce.as_mut().unwrap() = Box::into_raw(Box::new(CApiNonce(non))) };
            
            return 0;
        },
        Err(e) =>
        {
            if error.is_null() == false
            {
                unsafe { *error.as_mut().unwrap() = capi_scram_err_map!(e).into_capi() };
            }

            return 1;
        }
    }
}
