//! Validate buffers and other information.

use util::*;
use super::traits::*;

// HELPERS

// Determine if the integer component is empty.
perftools_inline!{
fn is_integer_empty<'a, Data>(data: &Data)
    -> bool
    where Data: FastDataInterface<'a>
{
    data.integer_iter().next().is_none()
}}

// Determine if the fraction component is empty.
perftools_inline!{
fn is_fraction_empty<'a, Data>(data: &Data)
    -> bool
    where Data: FastDataInterface<'a>
{
    data.fraction_iter().next().is_none()
}}

// Determine if the fraction component exists.
perftools_inline!{
#[cfg(feature = "format")]
fn has_fraction<'a, Data>(data: &Data)
    -> bool
    where Data: FastDataInterface<'a>
{
    data.fraction().is_some()
}}

// Determine if the exponent component exists.
perftools_inline!{
fn has_exponent<'a, Data>(data: &Data)
    -> bool
    where Data: FastDataInterface<'a>
{
    data.exponent().is_some()
}}

// Unwrap option to get the pointer.
perftools_inline!{
fn option_as_ptr(option: Option<&[u8]>) -> *const u8
{
    option.unwrap().as_ptr()
}}

// MANTISSA

// Validate the extracted integer has no leading zeros.
perftools_inline!{
#[cfg(feature = "format")]
pub(super) fn validate_no_leading_zeros<'a, Data>(data: &Data)
    -> ParseResult<()>
    where Data: FastDataInterface<'a>
{
    // Check if the next character is a sign symbol.
    let mut iter = data.integer_iter();
    match iter.next() {
        Some(&b'0')     => (),
        _               => return Ok(())
    };

    // Only here if we have a leading 0 symbol.
    match iter.next() {
        Some(_) => Err((ErrorCode::InvalidLeadingZeros, data.integer().as_ptr())),
        None    => Ok(())
    }
}}

// Validate the extracted mantissa float components.
//      1. Validate non-empty significant digits (integer or fraction).
perftools_inline!{
pub(super) fn validate_permissive_mantissa<'a, Data>(data: &Data)
    -> ParseResult<()>
    where Data: FastDataInterface<'a>
{
    let integer_empty = is_integer_empty(data);
    let fraction_empty = is_fraction_empty(data);
    if integer_empty && fraction_empty {
        // Invalid floating-point number, no integer or fraction components.
        Err((ErrorCode::EmptyMantissa, data.integer().as_ptr()))
    } else {
        Ok(())
    }
}}

// Validate the extracted mantissa float components.
//      1. Validate integer component is non-empty.
perftools_inline!{
#[cfg(feature = "format")]
pub(super) fn validate_required_integer<'a, Data>(data: &Data)
    -> ParseResult<()>
    where Data: FastDataInterface<'a>
{
    if is_integer_empty(data) {
        // Invalid floating-point number, no integer component.
        Err((ErrorCode::EmptyInteger, data.integer().as_ptr()))
    } else {
        Ok(())
    }
}}

// Validate the extracted mantissa float components.
//      1. Validate fraction component is non-empty if present.
perftools_inline!{
#[cfg(feature = "format")]
pub(super) fn validate_required_fraction<'a, Data>(data: &Data)
    -> ParseResult<()>
    where Data: FastDataInterface<'a>
{
    if has_fraction(data) && is_fraction_empty(data) {
        // Invalid floating-point number, no fraction component.
        Err((ErrorCode::EmptyFraction, option_as_ptr(data.fraction())))
    } else {
        Ok(())
    }
}}

// Validate the extracted mantissa float components.
//      1. Validate integer component is non-empty.
//      2. Validate fraction component is non-empty if present.
perftools_inline!{
#[cfg(feature = "format")]
pub(super) fn validate_required_digits<'a, Data>(data: &Data)
    -> ParseResult<()>
    where Data: FastDataInterface<'a>
{
    if is_integer_empty(data) {
        // Invalid floating-point number, no integer component.
        Err((ErrorCode::EmptyInteger, data.integer().as_ptr()))
    } else if has_fraction(data) && is_fraction_empty(data) {
        // Invalid floating-point number, no fraction component.
        Err((ErrorCode::EmptyFraction, option_as_ptr(data.fraction())))
    } else {
        Ok(())
    }
}}

// Validate mantissa depending on float format.
perftools_inline!{
#[cfg(feature = "format")]
pub(super) fn validate_mantissa<'a, Data>(data: &Data, format: NumberFormat)
    -> ParseResult<()>
    where Data: FastDataInterface<'a>
{
    // Check no leading zeros.
    if format.no_float_leading_zeros() {
        validate_no_leading_zeros(data)?;
    }

    // Check required digits.
    let required_integer = format.required_integer_digits();
    let required_fraction = format.required_fraction_digits();
    match (required_integer, required_fraction) {
        (true, true)    => validate_required_digits(data),
        (false, true)   => validate_required_fraction(data),
        (true, false)   => validate_required_integer(data),
        (false, false)  => validate_permissive_mantissa(data)
    }
}}

// EXPONENT

// Validate the required exponent component.
//      1). If the exponent has been defined, ensure at least 1 digit follows it.
perftools_inline!{
pub(super) fn validate_required_exponent<'a, Data>(data: &Data)
    -> ParseResult<()>
    where Data: FastDataInterface<'a>
{
    // If we don't have an exponent stored, we're fine.
    if !has_exponent(data) {
        return Ok(())
    }

    // Check if the next character is a sign symbol.
    let mut iter = data.exponent_iter();
    match iter.next() {
        Some(&b'+') | Some(&b'-')   => (),
        Some(_)                     => return Ok(()),
        None                        => return Err((ErrorCode::EmptyExponent, option_as_ptr(data.exponent())))
    };

    // Only here if we have a sign symbol.
    match iter.next() {
        Some(_) => Ok(()),
        None    => Err((ErrorCode::EmptyExponent, option_as_ptr(data.exponent())))
    }
}}

// Validate optional exponent component.
//      A no-op, since the data is optional.
perftools_inline!{
#[cfg(feature = "format")]
pub(super) fn validate_optional_exponent<'a, Data>(_: &Data)
    -> ParseResult<()>
    where Data: FastDataInterface<'a>
{
    Ok(())
}}

// Validate invalid exponent component.
perftools_inline!{
#[cfg(feature = "format")]
pub(super) fn validate_invalid_exponent<'a, Data>(data: &Data)
    -> ParseResult<()>
    where Data: FastDataInterface<'a>
{
    match has_exponent(data) {
        true  => return Err((ErrorCode::InvalidExponent, option_as_ptr(data.exponent()))),
        false => Ok(())
    }
}}

// Validate exponent depending on float format.
perftools_inline!{
#[cfg(feature = "format")]
pub(super) fn validate_exponent<'a, Data>(data: &Data, format: NumberFormat)
    -> ParseResult<()>
    where Data: FastDataInterface<'a>
{
    let required = format.required_exponent_digits();
    let invalid = format.no_exponent_notation();
    match (required, invalid) {
        (true, _)       => validate_required_exponent(data),
        (_, true)       => validate_invalid_exponent(data),
        (false, false)  => validate_optional_exponent(data)
    }
}}

// EXPONENT SIGN

// Validate optional exponent sign.
//      A no-op, since the data is optional.
perftools_inline!{
pub(super) fn validate_optional_exponent_sign<'a, Data>(_: &Data)
    -> ParseResult<()>
    where Data: FastDataInterface<'a>
{
    Ok(())
}}

// Validate a required exponent sign.
perftools_inline!{
#[cfg(feature = "format")]
pub(super) fn validate_required_exponent_sign<'a, Data>(data: &Data)
    -> ParseResult<()>
    where Data: FastDataInterface<'a>
{
    // Check if the next character is a sign symbol.
    let mut iter = data.exponent_iter();
    match iter.next() {
        Some(&b'+') | Some(&b'-')   => Ok(()),
        _ if has_exponent(data)     => Err((ErrorCode::MissingExponentSign, option_as_ptr(data.exponent()))),
        _                           => Ok(())
    }
}}

// Validate a required exponent sign.
perftools_inline!{
#[cfg(feature = "format")]
pub(super) fn validate_no_positive_exponent_sign<'a, Data>(data: &Data)
    -> ParseResult<()>
    where Data: FastDataInterface<'a>
{
    // Check if the next character is a sign symbol.
    let mut iter = data.exponent_iter();
    match iter.next() {
        Some(&b'+') => Err((ErrorCode::InvalidPositiveExponentSign, option_as_ptr(data.exponent()))),
        _           => Ok(())
    }
}}

// Validate exponent sign depending on float format.
perftools_inline!{
#[cfg(feature = "format")]
pub(super) fn validate_exponent_sign<'a, Data>(data: &Data, format: NumberFormat)
    -> ParseResult<()>
    where Data: FastDataInterface<'a>
{
    let required = format.required_exponent_sign();
    let no_positive = format.no_positive_exponent_sign();
    match (required, no_positive) {
        (true, _)       => validate_required_exponent_sign(data),
        (_, true)       => validate_no_positive_exponent_sign(data),
        (false, false)  => validate_optional_exponent_sign(data)
    }
}}

// EXPONENT FRACTION

// Validate an exponent may occur with or without a fraction.
perftools_inline!{
pub(super) fn validate_exponent_optional_fraction<'a, Data>(_: &Data)
    -> ParseResult<()>
    where Data: FastDataInterface<'a>
{
    Ok(())
}}

// Validate an exponent requires a fraction component.
perftools_inline!{
#[cfg(feature = "format")]
pub(super) fn validate_exponent_required_fraction<'a, Data>(data: &Data)
    -> ParseResult<()>
    where Data: FastDataInterface<'a>
{
    match has_exponent(data) && !has_fraction(data) {
        true  => Err((ErrorCode::ExponentWithoutFraction, option_as_ptr(data.exponent()))),
        false => Ok(())
    }
}}

// Validate exponent fraction depending on float format.
perftools_inline!{
#[cfg(feature = "format")]
pub(super) fn validate_exponent_fraction<'a, Data>(data: &Data, format: NumberFormat)
    -> ParseResult<()>
    where Data: FastDataInterface<'a>
{
    match format.no_exponent_without_fraction() {
        true  => validate_exponent_required_fraction(data),
        false => validate_exponent_optional_fraction(data)
    }
}}

// TESTS
// -----

#[cfg(test)]
mod tests {
    use super::*;
    use super::super::standard::*;

    #[test]
    #[cfg(feature = "format")]
    fn validate_no_leading_zeros_test() {
        type Data<'a> = StandardFastDataInterface<'a>;
        let data: Data = (b!("01"), Some(b!("23450")), None, 0).into();
        assert!(validate_no_leading_zeros(&data).is_err());

        let data: Data = (b!("1"), Some(b!("23450")), None, 0).into();
        assert!(validate_no_leading_zeros(&data).is_ok());

        let data: Data = (b!("0"), Some(b!("23450")), None, 0).into();
        assert!(validate_no_leading_zeros(&data).is_ok());

        let data: Data = (b!(""), Some(b!("23450")), None, 0).into();
        assert!(validate_no_leading_zeros(&data).is_ok());
    }

    #[test]
    fn validate_permissive_mantissa_test() {
        type Data<'a> = StandardFastDataInterface<'a>;
        let data: Data = (b!("01"), Some(b!("23450")), None, 0).into();
        assert!(validate_permissive_mantissa(&data).is_ok());

        let data: Data = (b!("0"), Some(b!("")), Some(b!("")), 0).into();
        assert!(validate_permissive_mantissa(&data).is_ok());

        let data: Data = (b!("0"), Some(b!("")), Some(b!("")), 0).into();
        assert!(validate_permissive_mantissa(&data).is_ok());

        let data: Data = (b!(""), Some(b!("")), Some(b!("+")), 0).into();
        assert!(validate_permissive_mantissa(&data).is_err());
    }

    #[test]
    #[cfg(feature = "format")]
    fn validate_required_integer_test() {
        type Data<'a> = StandardFastDataInterface<'a>;
        let data: Data = (b!("01"), Some(b!("23450")), None, 0).into();
        assert!(validate_required_integer(&data).is_ok());

        let data: Data = (b!("0"), Some(b!("")), Some(b!("")), 0).into();
        assert!(validate_required_integer(&data).is_ok());

        let data: Data = (b!(""), Some(b!("0")), Some(b!("")), 0).into();
        assert!(validate_required_integer(&data).is_err());

        let data: Data = (b!(""), Some(b!("")), Some(b!("")), 0).into();
        assert!(validate_required_integer(&data).is_err());
    }

    #[test]
    #[cfg(feature = "format")]
    fn validate_required_fraction_test() {
        type Data<'a> = StandardFastDataInterface<'a>;
        let data: Data = (b!("01"), Some(b!("23450")), None, 0).into();
        assert!(validate_required_fraction(&data).is_ok());

        let data: Data = (b!("0"), Some(b!("")), Some(b!("")), 0).into();
        assert!(validate_required_fraction(&data).is_err());

        let data: Data = (b!(""), Some(b!("0")), Some(b!("")), 0).into();
        assert!(validate_required_fraction(&data).is_ok());

        let data: Data = (b!(""), Some(b!("")), Some(b!("")), 0).into();
        assert!(validate_required_fraction(&data).is_err());
    }

    #[test]
    #[cfg(feature = "format")]
    fn validate_required_digits_test() {
        type Data<'a> = StandardFastDataInterface<'a>;
        let data: Data = (b!("01"), Some(b!("23450")), None, 0).into();
        assert!(validate_required_digits(&data).is_ok());

        let data: Data = (b!("0"), Some(b!("")), Some(b!("")), 0).into();
        assert!(validate_required_digits(&data).is_err());

        let data: Data = (b!(""), Some(b!("0")), Some(b!("")), 0).into();
        assert!(validate_required_digits(&data).is_err());

        let data: Data = (b!(""), Some(b!("")), Some(b!("")), 0).into();
        assert!(validate_required_digits(&data).is_err());
    }

    #[test]
    fn validate_required_exponent_test() {
        type Data<'a> = StandardFastDataInterface<'a>;
        let data: Data = (b!("01"), Some(b!("23450")), None, 0).into();
        assert!(validate_required_exponent(&data).is_ok());

        let data: Data = (b!("0"), Some(b!("")), Some(b!("")), 0).into();
        assert!(validate_required_exponent(&data).is_err());

        let data: Data = (b!(""), Some(b!("")), Some(b!("+")), 0).into();
        assert!(validate_required_exponent(&data).is_err());

        let data: Data = (b!(""), Some(b!("")), Some(b!("2")), 0).into();
        assert!(validate_required_exponent(&data).is_ok());

        let data: Data = (b!(""), Some(b!("")), Some(b!("+2")), 0).into();
        assert!(validate_required_exponent(&data).is_ok());
    }

    #[test]
    #[cfg(feature = "format")]
    fn validate_optional_exponent_test() {
        type Data<'a> = StandardFastDataInterface<'a>;
        let data: Data = (b!("0"), Some(b!("")), None, 0).into();
        assert!(validate_optional_exponent(&data).is_ok());

        let data: Data = (b!("0"), Some(b!("")), Some(b!("")), 0).into();
        assert!(validate_optional_exponent(&data).is_ok());

        let data: Data = (b!("0"), Some(b!("")), Some(b!("+")), 0).into();
        assert!(validate_optional_exponent(&data).is_ok());

        let data: Data = (b!("0"), Some(b!("")), Some(b!("2")), 0).into();
        assert!(validate_optional_exponent(&data).is_ok());

        let data: Data = (b!("0"), Some(b!("")), Some(b!("+2")), 0).into();
        assert!(validate_optional_exponent(&data).is_ok());
    }

    #[test]
    #[cfg(feature = "format")]
    fn validate_invalid_exponent_test() {
        type Data<'a> = StandardFastDataInterface<'a>;
        let data: Data = (b!("0"), Some(b!("")), None, 0).into();
        assert!(validate_invalid_exponent(&data).is_ok());

        let data: Data = (b!("0"), Some(b!("")), Some(b!("")), 0).into();
        assert!(validate_invalid_exponent(&data).is_err());

        let data: Data = (b!("0"), Some(b!("")), Some(b!("+")), 0).into();
        assert!(validate_invalid_exponent(&data).is_err());

        let data: Data = (b!("0"), Some(b!("")), Some(b!("2")), 0).into();
        assert!(validate_invalid_exponent(&data).is_err());

        let data: Data = (b!("0"), Some(b!("")), Some(b!("+2")), 0).into();
        assert!(validate_invalid_exponent(&data).is_err());
    }

    #[test]
    fn validate_optional_exponent_sign_test() {
        type Data<'a> = StandardFastDataInterface<'a>;
        let data: Data = (b!("0"), Some(b!("")), None, 0).into();
        assert!(validate_optional_exponent_sign(&data).is_ok());

        let data: Data = (b!("0"), Some(b!("")), Some(b!("")), 0).into();
        assert!(validate_optional_exponent_sign(&data).is_ok());

        let data: Data = (b!("0"), Some(b!("")), Some(b!("+")), 0).into();
        assert!(validate_optional_exponent_sign(&data).is_ok());

        let data: Data = (b!("0"), Some(b!("")), Some(b!("2")), 0).into();
        assert!(validate_optional_exponent_sign(&data).is_ok());

        let data: Data = (b!("0"), Some(b!("")), Some(b!("+2")), 0).into();
        assert!(validate_optional_exponent_sign(&data).is_ok());
    }

    #[test]
    #[cfg(feature = "format")]
    fn validate_required_exponent_sign_test() {
        type Data<'a> = StandardFastDataInterface<'a>;
        let data: Data = (b!("0"), Some(b!("")), None, 0).into();
        assert!(validate_required_exponent_sign(&data).is_ok());

        let data: Data = (b!("0"), Some(b!("")), Some(b!("")), 0).into();
        assert!(validate_required_exponent_sign(&data).is_err());

        let data: Data = (b!("0"), Some(b!("")), Some(b!("+")), 0).into();
        assert!(validate_required_exponent_sign(&data).is_ok());

        let data: Data = (b!("0"), Some(b!("")), Some(b!("2")), 0).into();
        assert!(validate_required_exponent_sign(&data).is_err());

        let data: Data = (b!("0"), Some(b!("")), Some(b!("+2")), 0).into();
        assert!(validate_required_exponent_sign(&data).is_ok());
    }

    #[test]
    #[cfg(feature = "format")]
    fn validate_no_positive_exponent_sign_test() {
        type Data<'a> = StandardFastDataInterface<'a>;
        let data: Data = (b!("0"), Some(b!("")), None, 0).into();
        assert!(validate_no_positive_exponent_sign(&data).is_ok());

        let data: Data = (b!("0"), Some(b!("")), Some(b!("")), 0).into();
        assert!(validate_no_positive_exponent_sign(&data).is_ok());

        let data: Data = (b!("0"), Some(b!("")), Some(b!("+")), 0).into();
        assert!(validate_no_positive_exponent_sign(&data).is_err());

        let data: Data = (b!("0"), Some(b!("")), Some(b!("2")), 0).into();
        assert!(validate_no_positive_exponent_sign(&data).is_ok());

        let data: Data = (b!("0"), Some(b!("")), Some(b!("+2")), 0).into();
        assert!(validate_no_positive_exponent_sign(&data).is_err());

        let data: Data = (b!("0"), Some(b!("")), Some(b!("-2")), 0).into();
        assert!(validate_no_positive_exponent_sign(&data).is_ok());
    }

    #[test]
    fn validate_exponent_optional_fraction_test() {
        type Data<'a> = StandardFastDataInterface<'a>;
        let data: Data = (b!("0"), Some(b!("")), None, 0).into();
        assert!(validate_exponent_optional_fraction(&data).is_ok());

        let data: Data = (b!(""), Some(b!("0")), None, 0).into();
        assert!(validate_exponent_optional_fraction(&data).is_ok());

        let data: Data = (b!("0"), Some(b!("")), Some(b!("")), 0).into();
        assert!(validate_exponent_optional_fraction(&data).is_ok());

        let data: Data = (b!(""), Some(b!("0")), Some(b!("+")), 0).into();
        assert!(validate_exponent_optional_fraction(&data).is_ok());
    }

    #[test]
    #[cfg(feature = "format")]
    fn validate_exponent_required_fraction_test() {
        type Data<'a> = StandardFastDataInterface<'a>;
        let data: Data = (b!("0"), Some(b!("")), None, 0).into();
        assert!(validate_exponent_required_fraction(&data).is_ok());

        let data: Data = (b!("0"), Some(b!("")), Some(b!("")), 0).into();
        assert!(validate_exponent_required_fraction(&data).is_ok());

        let data: Data = (b!("0"), None, Some(b!("")), 0).into();
        assert!(validate_exponent_required_fraction(&data).is_err());

        let data: Data = (b!(""), Some(b!("0")), Some(b!("+")), 0).into();
        assert!(validate_exponent_required_fraction(&data).is_ok());
    }
}
