// License: see LICENSE file at root directory of `master` branch

//! # Number writer

#![cfg(feature="std")]
#![cfg(test)]

use {
    alloc::string::ToString,
    core::fmt::{self, Display, Formatter},
    std::{
        io::{self, Write},
        time::Instant,
    },

    crate::{IoResult, Number},

    super::super::Inner,
};

enum Sample {
    Format,
    ToString,
    NumberWriter,
}

impl Display for Sample {

    fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
        f.write_str(match self {
            Sample::Format => "Format",
            Sample::ToString => "ToString",
            Sample::NumberWriter => "NumberWriter",
        })
    }

}

impl Sample {

    fn write<W>(&self, n: &Number, stream: &mut W) -> IoResult<()> where W: Write {
        match self {
            Sample::Format => match n.inner {
                Inner::I32(i) => write!(stream, "{}", i),
                Inner::I64(i) => write!(stream, "{}", i),
                Inner::I128(i) => write!(stream, "{}", i),
                Inner::ISize(i) => write!(stream, "{}", i),

                Inner::U32(u) => write!(stream, "{}", u),
                Inner::U64(u) => write!(stream, "{}", u),
                Inner::U128(u) => write!(stream, "{}", u),
                Inner::USize(u) => write!(stream, "{}", u),

                Inner::F32(f) => write!(stream, "{}", f),
                Inner::F64(f) => write!(stream, "{}", f),
            },
            Sample::ToString => match n.inner {
                Inner::I32(i) => stream.write_all(i.to_string().as_bytes()),
                Inner::I64(i) => stream.write_all(i.to_string().as_bytes()),
                Inner::I128(i) => stream.write_all(i.to_string().as_bytes()),
                Inner::ISize(i) => stream.write_all(i.to_string().as_bytes()),

                Inner::U32(u) => stream.write_all(u.to_string().as_bytes()),
                Inner::U64(u) => stream.write_all(u.to_string().as_bytes()),
                Inner::U128(u) => stream.write_all(u.to_string().as_bytes()),
                Inner::USize(u) => stream.write_all(u.to_string().as_bytes()),

                Inner::F32(f) => stream.write_all(f.to_string().as_bytes()),
                Inner::F64(f) => stream.write_all(f.to_string().as_bytes()),
            },
            Sample::NumberWriter => n.write(stream),
        }
    }

}

/// # Tests formatting time
///
/// This test requires you to watch for results from stdout.
#[test]
#[ignore]
fn formatting_time() -> IoResult<()> {
    use core::convert::TryFrom;

    const COUNT: u16 = 15_000;

    fn test(s: Sample) -> IoResult<()> {
        let mut sink = io::sink();

        let start = Instant::now();
        for i in 0..COUNT {
            s.write(&Number::from(u16::max_value() - i), &mut sink)?;
            s.write(&Number::from(i16::min_value() + i16::try_from(i).unwrap()), &mut sink)?;
            s.write(&Number::from(u32::max_value() - u32::from(i)), &mut sink)?;
            s.write(&Number::from(i32::min_value() + i32::from(i)), &mut sink)?;
            s.write(&Number::from(u64::max_value() - u64::from(i)), &mut sink)?;
            s.write(&Number::from(i64::min_value() + i64::from(i)), &mut sink)?;

            #[cfg(target_pointer_width = "128")] {
                s.write(&Number::from(u128::max_value() - u128::from(i)), &mut nw, &mut sink)?;
                s.write(&Number::from(i128::min_value() + i128::from(i)), &mut nw, &mut sink)?;
            }
        }

        std::println!("{s:>20}: {time:>15}", s=s.to_string(), time=format!("{:?}", Instant::now().duration_since(start)));
        Ok(())
    }

    test(Sample::Format)?;
    test(Sample::ToString)?;
    test(Sample::NumberWriter)?;

    Ok(())
}
