#![allow(unused_macros)]

macro_rules! platform {
    { unix => { $($unix:tt)* }, windows => { $($windows:tt)* }, } => {
        if cfg!(unix) {
            #[cfg(unix)] { $($unix)* }
            #[cfg(not(unix))] { unreachable!() }
        } else if cfg!(windows) {
            #[cfg(windows)] { $($windows)* }
            #[cfg(not(windows))] { unreachable!() }
        } else {
            #[cfg(not(any(unix, windows)))] compile_error!("Unsupported platform");
            unreachable!()
        }
    }
}

/// Returns the name of the current function.
macro_rules! func {
    () => {{
        fn f() {}
        fn type_name_of<T>(_: T) -> &'static str {
            std::any::type_name::<T>()
        }
        // This gets the name of f, which is
        // `...::current_function::f`.  By stripping off the ::f, we
        // get the current function's name!
        let func_name = type_name_of(f);
        if let Some(stripped) = func_name.strip_suffix("::f") {
            stripped
        } else {
            // Um... the output changed.  Don't panic for something
            // that is only for debugging.  Just return the string as
            // is.
            func_name
        }
    }}
}

/// Like `writeln!`, but nicely wraps lines.
///
/// Unlike `writeln` and like `eprintln` panics if it can't write to
/// the specified stream.
macro_rules! wwriteln {
    {
        stream=$stream: expr
    } => {{
        let stream: &mut dyn std::io::Write = $stream;
        if let Err(err) = writeln!(stream) {
            panic!("Error writing to output stream: {}", err);
        }
    }};

    {
        stream=$stream: expr,
        indent=$i: expr,
        $($arg: expr),*
    } => {{
        let stream: &mut dyn std::io::Write = $stream;
        let i = $i;
        crate::output::wrapping::iwwriteln(
            stream,
            i.as_ref(), i.as_ref(),
            format_args!($($arg),*))
    }};

    {
        stream=$stream: expr,
        initial_indent=$ii: expr,
        subsequent_indent=$si: expr,
        $($arg: expr),*
    } => {{
        let stream: &mut dyn std::io::Write = $stream;
        let ii = $ii;
        let si = $si;
        crate::output::wrapping::iwwriteln(
            stream,
            ii.as_ref(), si.as_ref(),
            format_args!($($arg),*))
    }};

    {
        stream=$stream: expr,
        initial_indent=$ii: expr,
        $($arg: expr),*
    } => {{
        let stream: &mut dyn std::io::Write = $stream;
        let ii = $ii;
        let si = format!("{:1$}", "", ii.len());
        crate::output::wrapping::iwwriteln(
            stream, ii.as_ref(), si.as_ref(),
            format_args!($($arg),*))
    }};

    {
        stream=$stream: expr,
        $($arg: expr),*
    } => {{
        let stream: &mut dyn std::io::Write = $stream;
        crate::output::wrapping::wwriteln(
            stream, format_args!($($arg),*))
    }};

    {
        $stream: expr,
        $($arg: expr),*
    } => {
        wwriteln!(stream=$stream, $($arg),*)
    };

    {
        $stream: expr
    } => {
        wwriteln!(stream=$stream)
    };
}

/// Like eprintln, but nicely wraps lines.
macro_rules! weprintln {
    { } => {
        wwriteln!(stream=&mut std::io::stderr())
    };

    {
        indent=$i: expr,
        $($arg: expr),*
    } => {
        wwriteln!(stream=&mut std::io::stderr(),
                  indent=$i,
                  $($arg),*)
    };

    {
        initial_indent=$ii: expr,
        subsequent_indent=$si: expr,
        $($arg: expr),*
    } => {
        wwriteln!(stream=&mut std::io::stderr(),
                  initial_indent=$ii,
                  subsequent_indent=$si,
                  $($arg),*)
    };

    {
        initial_indent=$ii: expr,
        $($arg: expr),*
    } => {
        wwriteln!(stream=&mut std::io::stderr(),
                  initial_indent=$ii,
                  $($arg),*)
    };

    {
        $($arg: expr),*
    } => {
        wwriteln!(stream=&mut std::io::stderr(), $($arg),*)
    };
}

/// Like weprintln, but doesn't emit anything in quiet mode.
macro_rules! make_qprintln {
    ($stream: expr, $quiet: expr) => {
        macro_rules! qprintln {
            // XXX: Lot's of repetition due to the fact that nested
            // macros still cannot have repetitions:
            // https://github.com/rust-lang/rust/issues/83527

            // First, with `indent`.
            { indent=$i: expr } => {
                if ! $quiet {
                    wwriteln!(stream=$stream, indent=$i);
                }
            };
            { indent=$i: expr, $a0: expr } => {
                if ! $quiet {
                    wwriteln!(stream=$stream, indent=$i, $a0);
                }
            };
            { indent=$i: expr, $a0: expr, $a1: expr } => {
                if ! $quiet {
                    wwriteln!(stream=$stream, indent=$i, $a0, $a1);
                }
            };
            { indent=$i: expr, $a0: expr, $a1: expr, $a2: expr } => {
                if ! $quiet {
                    wwriteln!(stream=$stream, indent=$i, $a0, $a1, $a2);
                }
            };
            { indent=$i: expr, $a0: expr, $a1: expr, $a2: expr, $a3: expr } => {
                if ! $quiet {
                    wwriteln!(stream=$stream, indent=$i, $a0, $a1, $a2, $a3);
                }
            };
            { indent=$i: expr, $a0: expr, $a1: expr, $a2: expr, $a3: expr, $a4: expr } => {
                if ! $quiet {
                    wwriteln!(stream=$stream, indent=$i, $a0, $a1, $a2, $a3, $a4);
                }
            };
            { indent=$i: expr, $a0: expr, $a1: expr, $a2: expr, $a3: expr, $a4: expr, $a5: expr } => {
                if ! $quiet {
                    wwriteln!(stream=$stream, indent=$i, $a0, $a1, $a2, $a3, $a4, $a5);
                }
            };
            { indent=$i: expr, $a0: expr, $a1: expr, $a2: expr, $a3: expr, $a4: expr, $a5: expr, $a6: expr } => {
                if ! $quiet {
                    wwriteln!(stream=$stream, indent=$i, $a0, $a1, $a2, $a3, $a4, $a5, $a6);
                }
            };
            // Again, with `initial_indent` and `subsequent_indent`.
            { initial_indent=$ii: expr, subsequent_indent=$si: expr } => {
                if ! $quiet {
                    wwriteln!(stream=$stream, initial_indent=$ii, subsequent_indent=$si);
                }
            };
            { initial_indent=$ii: expr, subsequent_indent=$si: expr, $a0: expr } => {
                if ! $quiet {
                    wwriteln!(stream=$stream, initial_indent=$ii, subsequent_indent=$si, $a0);
                }
            };
            { initial_indent=$ii: expr, subsequent_indent=$si: expr, $a0: expr, $a1: expr } => {
                if ! $quiet {
                    wwriteln!(stream=$stream, initial_indent=$ii, subsequent_indent=$si, $a0, $a1);
                }
            };
            { initial_indent=$ii: expr, subsequent_indent=$si: expr, $a0: expr, $a1: expr, $a2: expr } => {
                if ! $quiet {
                    wwriteln!(stream=$stream, initial_indent=$ii, subsequent_indent=$si, $a0, $a1, $a2);
                }
            };
            { initial_indent=$ii: expr, subsequent_indent=$si: expr, $a0: expr, $a1: expr, $a2: expr, $a3: expr } => {
                if ! $quiet {
                    wwriteln!(stream=$stream, initial_indent=$ii, subsequent_indent=$si, $a0, $a1, $a2, $a3);
                }
            };
            { initial_indent=$ii: expr, subsequent_indent=$si: expr, $a0: expr, $a1: expr, $a2: expr, $a3: expr, $a4: expr } => {
                if ! $quiet {
                    wwriteln!(stream=$stream, initial_indent=$ii, subsequent_indent=$si, $a0, $a1, $a2, $a3, $a4);
                }
            };
            { initial_indent=$ii: expr, subsequent_indent=$si: expr, $a0: expr, $a1: expr, $a2: expr, $a3: expr, $a4: expr, $a5: expr } => {
                if ! $quiet {
                    wwriteln!(stream=$stream, initial_indent=$ii, subsequent_indent=$si, $a0, $a1, $a2, $a3, $a4, $a5);
                }
            };
            { initial_indent=$ii: expr, subsequent_indent=$si: expr, $a0: expr, $a1: expr, $a2: expr, $a3: expr, $a4: expr, $a5: expr, $a6: expr } => {
                if ! $quiet {
                    wwriteln!(stream=$stream, initial_indent=$ii, subsequent_indent=$si, $a0, $a1, $a2, $a3, $a4, $a5, $a6);
                }
            };

            // Again, with `initial_indent`.
            { initial_indent=$ii: expr } => {
                if ! $quiet {
                    wwriteln!(stream=$stream, initial_indent=$ii);
                }
            };
            { initial_indent=$ii: expr, $a0: expr } => {
                if ! $quiet {
                    wwriteln!(stream=$stream, initial_indent=$ii, $a0);
                }
            };
            { initial_indent=$ii: expr, $a0: expr, $a1: expr } => {
                if ! $quiet {
                    wwriteln!(stream=$stream, initial_indent=$ii, $a0, $a1);
                }
            };
            { initial_indent=$ii: expr, $a0: expr, $a1: expr, $a2: expr } => {
                if ! $quiet {
                    wwriteln!(stream=$stream, initial_indent=$ii, $a0, $a1, $a2);
                }
            };
            { initial_indent=$ii: expr, $a0: expr, $a1: expr, $a2: expr, $a3: expr } => {
                if ! $quiet {
                    wwriteln!(stream=$stream, initial_indent=$ii, $a0, $a1, $a2, $a3);
                }
            };
            { initial_indent=$ii: expr, $a0: expr, $a1: expr, $a2: expr, $a3: expr, $a4: expr } => {
                if ! $quiet {
                    wwriteln!(stream=$stream, initial_indent=$ii, $a0, $a1, $a2, $a3, $a4);
                }
            };
            { initial_indent=$ii: expr, $a0: expr, $a1: expr, $a2: expr, $a3: expr, $a4: expr, $a5: expr } => {
                if ! $quiet {
                    wwriteln!(stream=$stream, initial_indent=$ii, $a0, $a1, $a2, $a3, $a4, $a5);
                }
            };
            { initial_indent=$ii: expr, $a0: expr, $a1: expr, $a2: expr, $a3: expr, $a4: expr, $a5: expr, $a6: expr } => {
                if ! $quiet {
                    wwriteln!(stream=$stream, initial_indent=$ii, $a0, $a1, $a2, $a3, $a4, $a5, $a6);
                }
            };

            // Again, without any indent.
            {} => {
                if ! $quiet {
                    wwriteln!(stream=$stream);
                }
            };
            { $a0: expr } => {
                if ! $quiet {
                    wwriteln!(stream=$stream, $a0);
                }
            };
            { $a0: expr, $a1: expr } => {
                if ! $quiet {
                    wwriteln!(stream=$stream, $a0, $a1);
                }
            };
            { $a0: expr, $a1: expr, $a2: expr } => {
                if ! $quiet {
                    wwriteln!(stream=$stream, $a0, $a1, $a2);
                }
            };
            { $a0: expr, $a1: expr, $a2: expr, $a3: expr } => {
                if ! $quiet {
                    wwriteln!(stream=$stream, $a0, $a1, $a2, $a3);
                }
            };
            { $a0: expr, $a1: expr, $a2: expr, $a3: expr, $a4: expr } => {
                if ! $quiet {
                    wwriteln!(stream=$stream, $a0, $a1, $a2, $a3, $a4);
                }
            };
            { $a0: expr, $a1: expr, $a2: expr, $a3: expr, $a4: expr, $a5: expr } => {
                if ! $quiet {
                    wwriteln!(stream=$stream, $a0, $a1, $a2, $a3, $a4, $a5);
                }
            };
            { $a0: expr, $a1: expr, $a2: expr, $a3: expr, $a4: expr, $a5: expr, $a6: expr } => {
                if ! $quiet {
                    wwriteln!(stream=$stream, $a0, $a1, $a2, $a3, $a4, $a5, $a6);
                }
            };
        }
    };
    ($quiet: expr) => {
        make_qprintln!(&mut std::io::stderr(), $quiet)
    }
}
