Documentation
// This is free and unencumbered software released into the public domain.

use crate::{ParseDecimalError, ParseError};
use core::str::FromStr;

#[cfg(feature = "rust_decimal")]
use rust_decimal::prelude::{FromPrimitive, ToPrimitive};

/// Rust type for representing values of the `xsd:integer` datatype.
pub type Integer = i128;

/// Rust type for representing values of the `xsd:long` datatype.
pub type Long = i64;

/// Rust type for representing values of the `xsd:int` datatype.
pub type Int = i32;

/// Rust type for representing values of the `xsd:short` datatype.
pub type Short = i16;

/// Rust type for representing values of the `xsd:byte` datatype.
pub type Byte = i8;

/// Rust type for representing values of the `xsd:decimal` datatype.
#[cfg(feature = "rust_decimal")]
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(
    feature = "borsh",
    derive(borsh::BorshSerialize, borsh::BorshDeserialize)
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Decimal(
    #[cfg_attr(feature = "serde", serde(with = "rust_decimal::serde::str"))] rust_decimal::Decimal,
);

/// Rust type for representing values of the `xsd:decimal` datatype.
#[cfg(not(feature = "rust_decimal"))]
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Decimal(crate::primitive::Double);

impl Decimal {
    pub fn is_integer(&self) -> bool {
        #[cfg(feature = "rust_decimal")]
        return self.0.is_integer();
        #[cfg(not(feature = "rust_decimal"))]
        return self.0.as_f64().fract() == 0.0;
    }

    pub fn as_f64(&self) -> f64 {
        return self.0.as_f64();
    }

    pub fn to_f64(&self) -> Option<f64> {
        Some(self.as_f64())
    }

    pub fn to_i128(&self) -> Option<i128> {
        if !self.is_integer() {
            return None;
        }
        #[cfg(feature = "rust_decimal")]
        return self.0.to_i128();
        #[cfg(not(feature = "rust_decimal"))]
        return Some(self.as_f64() as i128);
    }

    #[cfg(feature = "serde")]
    pub fn to_json(&self) -> Option<serde_json::Value> {
        Some(self.clone().into_json())
    }

    #[cfg(feature = "serde")]
    pub fn into_json(self) -> serde_json::Value {
        use alloc::string::ToString;
        serde_json::Value::String(self.to_string())
    }
}

impl core::fmt::Display for Decimal {
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
        self.0.fmt(f)
    }
}

impl From<i8> for Decimal {
    fn from(input: i8) -> Self {
        Self(input.into())
    }
}

impl From<i16> for Decimal {
    fn from(input: i16) -> Self {
        Self(input.into())
    }
}

impl From<i32> for Decimal {
    fn from(input: i32) -> Self {
        Self(input.into())
    }
}

impl From<i64> for Decimal {
    fn from(input: i64) -> Self {
        Self(input.into())
    }
}

impl From<i128> for Decimal {
    fn from(input: i128) -> Self {
        Self(input.into())
    }
}

impl From<isize> for Decimal {
    fn from(input: isize) -> Self {
        Self(input.into())
    }
}

#[cfg(feature = "rust_decimal")]
impl From<rust_decimal::Decimal> for Decimal {
    fn from(input: rust_decimal::Decimal) -> Self {
        Self(input)
    }
}

#[cfg(feature = "rust_decimal")]
impl From<&rust_decimal::Decimal> for Decimal {
    fn from(input: &rust_decimal::Decimal) -> Self {
        Self(input.clone())
    }
}

impl FromStr for Decimal {
    type Err = ParseDecimalError;

    #[cfg(feature = "rust_decimal")]
    fn from_str(input: &str) -> Result<Self, Self::Err> {
        Ok(Self(input.parse::<rust_decimal::Decimal>()?))
    }

    #[cfg(not(feature = "rust_decimal"))]
    fn from_str(input: &str) -> Result<Self, Self::Err> {
        input
            .parse::<crate::primitive::Double>()
            .map(Self)
            .map_err(|_| ParseDecimalError)
    }
}

impl TryFrom<f32> for Decimal {
    type Error = ParseDecimalError;

    #[cfg(feature = "rust_decimal")]
    fn try_from(input: f32) -> Result<Self, Self::Error> {
        Ok(Self(rust_decimal::Decimal::try_from(input)?))
    }

    #[cfg(not(feature = "rust_decimal"))]
    fn try_from(input: f32) -> Result<Self, Self::Error> {
        Ok(Self(input.into()))
    }
}

impl TryFrom<f64> for Decimal {
    type Error = ParseDecimalError;

    #[cfg(feature = "rust_decimal")]
    fn try_from(input: f64) -> Result<Self, Self::Error> {
        Ok(Self(rust_decimal::Decimal::try_from(input)?))
    }

    #[cfg(not(feature = "rust_decimal"))]
    fn try_from(input: f64) -> Result<Self, Self::Error> {
        Ok(Self(input.into()))
    }
}

impl TryFrom<Decimal> for i128 {
    type Error = ();

    fn try_from(input: Decimal) -> Result<Self, Self::Error> {
        Self::try_from(&input)
    }
}

impl TryFrom<&Decimal> for i128 {
    type Error = ();

    fn try_from(input: &Decimal) -> Result<Self, Self::Error> {
        input.to_i128().ok_or(())
    }
}