//! [`core::fmt::DebugTuple`] reimplementation with
//! [`DebugTuple::finish_non_exhaustive()`] method.

use ::core;
use core::fmt::{Debug, Formatter, Result, Write};
use core::prelude::v1::*;

/// Same as [`core::fmt::DebugTuple`], but with
/// [`DebugTuple::finish_non_exhaustive()`] method.
#[must_use = "must eventually call `finish()` or `finish_non_exhaustive()` on \
              Debug builders"]
pub struct DebugTuple<'a, 'b: 'a> {
    fmt: &'a mut Formatter<'b>,
    result: Result,
    fields: usize,
    empty_name: bool,
}

/// Creates a new [`DebugTuple`].
pub fn debug_tuple<'a, 'b>(
    fmt: &'a mut Formatter<'b>,
    name: &str,
) -> DebugTuple<'a, 'b> {
    let result = fmt.write_str(name);
    DebugTuple {
        fmt,
        result,
        fields: 0,
        empty_name: name.is_empty(),
    }
}

impl<'a, 'b: 'a> DebugTuple<'a, 'b> {
    /// Adds a new field to the generated tuple struct output.
    ///
    /// # Example
    ///
    /// ```rust
    /// use core::fmt;
    /// use derive_more::__private::debug_tuple;
    ///
    /// struct Foo(i32, String);
    ///
    /// impl fmt::Debug for Foo {
    ///     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
    ///         debug_tuple(fmt, "Foo")
    ///             .field(&self.0) // We add the first field.
    ///             .field(&self.1) // We add the second field.
    ///             .finish() // We're good to go!
    ///     }
    /// }
    ///
    /// assert_eq!(
    ///     format!("{:?}", Foo(10, "Hello World".to_string())),
    ///     "Foo(10, \"Hello World\")",
    /// );
    /// ```
    pub fn field(&mut self, value: &dyn Debug) -> &mut Self {
        self.result = self.result.and_then(|_| {
            if self.is_pretty() {
                if self.fields == 0 {
                    self.fmt.write_str("(\n")?;
                }

                let mut padded_formatter = Padded::new(self.fmt);
                padded_formatter.write_fmt(format_args!("{value:#?}"))?;
                padded_formatter.write_str(",\n")
            } else {
                let prefix = if self.fields == 0 { "(" } else { ", " };
                self.fmt.write_str(prefix)?;
                value.fmt(self.fmt)
            }
        });

        self.fields += 1;
        self
    }

    /// Finishes output and returns any error encountered.
    ///
    /// # Example
    ///
    /// ```
    /// use core::fmt;
    /// use derive_more::__private::debug_tuple;
    ///
    /// struct Foo(i32, String);
    ///
    /// impl fmt::Debug for Foo {
    ///     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
    ///         debug_tuple(fmt, "Foo")
    ///             .field(&self.0)
    ///             .field(&self.1)
    ///             .finish() // You need to call it to "finish" the
    ///                       // tuple formatting.
    ///     }
    /// }
    ///
    /// assert_eq!(
    ///     format!("{:?}", Foo(10, "Hello World".to_string())),
    ///     "Foo(10, \"Hello World\")",
    /// );
    /// ```
    pub fn finish(&mut self) -> Result {
        if self.fields > 0 {
            self.result = self.result.and_then(|_| {
                if self.fields == 1 && self.empty_name && !self.is_pretty() {
                    self.fmt.write_str(",")?;
                }
                self.fmt.write_str(")")
            });
        }
        self.result
    }

    /// Marks the struct as non-exhaustive, indicating to the reader that there are some other
    /// fields that are not shown in the debug representation, and finishes output, returning any
    /// error encountered.
    ///
    /// # Example
    ///
    /// ```rust
    /// use core::fmt;
    /// use derive_more::__private::debug_tuple;
    ///
    /// struct Bar(i32, f32);
    ///
    /// impl fmt::Debug for Bar {
    ///     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
    ///         debug_tuple(fmt, "Bar")
    ///             .field(&self.0)
    ///             .finish_non_exhaustive() // Show that some other field(s) exist.
    ///     }
    /// }
    ///
    /// assert_eq!(format!("{:?}", Bar(10, 1.0)), "Bar(10, ..)");
    /// ```
    pub fn finish_non_exhaustive(&mut self) -> Result {
        self.result = self.result.and_then(|_| {
            if self.fields > 0 {
                if self.is_pretty() {
                    let mut padded_formatter = Padded::new(self.fmt);
                    padded_formatter.write_str("..\n")?;
                    self.fmt.write_str(")")
                } else {
                    self.fmt.write_str(", ..)")
                }
            } else {
                self.fmt.write_str("(..)")
            }
        });
        self.result
    }

    fn is_pretty(&self) -> bool {
        self.fmt.alternate()
    }
}

/// Wrapper for a [`Formatter`] adding 4 spaces on newlines for inner pretty
/// printed [`Debug`] values.
struct Padded<'a, 'b> {
    formatter: &'a mut Formatter<'b>,
    on_newline: bool,
}

impl<'a, 'b> Padded<'a, 'b> {
    fn new(formatter: &'a mut Formatter<'b>) -> Self {
        Self {
            formatter,
            on_newline: true,
        }
    }
}

impl Write for Padded<'_, '_> {
    fn write_str(&mut self, s: &str) -> Result {
        for s in s.split_inclusive('\n') {
            if self.on_newline {
                self.formatter.write_str("    ")?;
            }

            self.on_newline = s.ends_with('\n');
            self.formatter.write_str(s)?;
        }

        Ok(())
    }
}
