use crate::Printing;
use std::fmt::Write;
pub const RD: &str = "\x1B[1;31m";
pub const GR: &str = "\x1B[1;32m";
pub const YL: &str = "\x1B[1;33m";
pub const BL: &str = "\x1B[1;34m";
pub const MG: &str = "\x1B[1;35m";
pub const CY: &str = "\x1B[1;36m";
pub const UN: &str = "\x1B[0m";
impl<T> Printing<T> for T
where
    T: std::fmt::Display,
{
    fn to_str(self) -> String {
        self.to_string()
    }
    fn to_plainstr(self) -> String {
        self.to_string()
    }
}
impl<T> Printing<T> for &[T]
where
    T: std::fmt::Display,
{
    fn to_str(self) -> String {
        match self.len() {
            0 => "[]".to_string(),
            1 => format!("[{}]", self[0]),
            _ => {
                self.iter()
                    .skip(1)
                    .fold(format!("[{}", self[0]), |mut s, item| {
                        write!(s, " {}", item).ok();
                        s
                    })
                    + "]"
            }
        }
    }
    fn to_plainstr(self) -> String {
        match self.len() {
            0 => "".to_string(),
            1 => format!("{}", self[0]),
            _ => self
                .iter()
                .skip(1)
                .fold(format!("{}", self[0]), |mut s, item| {
                    write!(s, " {}", item).ok();
                    s
                }),
        }
    }
}
impl<T,U> Printing<T> for &(T,U)
where
    T: std::fmt::Display,
    U: std::fmt::Display
{
    fn to_str(self) -> String {
        format!("({},{})", self.0, self.1)   
    }
    fn to_plainstr(self) -> String {
        format!("{} {}", self.0, self.1)   
    }
}
impl<T,U,V> Printing<T> for &(T,U,V)
where
    T: std::fmt::Display,
    U: std::fmt::Display,
    V: std::fmt::Display
{
    fn to_str(self) -> String {
        format!("({},{},{})", self.0, self.1, self.2)   
    }
    fn to_plainstr(self) -> String {
        format!("{} {} {}", self.0, self.1, self.2)   
    }
}
impl<T,U,V,W> Printing<T> for &(T,U,V,W)
where
    T: std::fmt::Display,
    U: std::fmt::Display,
    V: std::fmt::Display,
    W: std::fmt::Display
{
    fn to_str(self) -> String {
        format!("({},{},{},{})", self.0, self.1, self.2, self.3)   
    }
    fn to_plainstr(self) -> String {
        format!("{} {} {} {}", self.0, self.1, self.2, self.3)   
    }
}
impl<T> Printing<T> for &[&[T]]
where
    T: std::fmt::Display,
{
    fn to_str(self) -> String {
        if self.is_empty() {
            return "[]".to_string();
        };
        self.iter().fold("[\n".to_string(), |mut s, &item| {
            writeln!(s, " {}", item.to_str()).ok();
            s
        }) + "]"
    }
    fn to_plainstr(self) -> String {
        if self.is_empty() {
            return "".to_string();
        };
        self.iter().fold("\n".to_string(), |mut s, &item| {
            writeln!(s, " {}", item.to_str()).ok();
            s
        })
    }
}
impl<T> Printing<T> for &[Vec<T>]
where
    T: std::fmt::Display,
{
    fn to_str(self) -> String {
        if self.is_empty() {
            return "[]".to_string();
        };
        self.iter().fold("[\n".to_string(), |mut s, item| {
            writeln!(s, " {}", item.to_str()).ok();
            s
        }) + "]"
    }
    fn to_plainstr(self) -> String {
        if self.is_empty() {
            return "".to_string();
        };
        self.iter().fold("\n".to_string(), |mut s, item| {
            writeln!(s, " {}", item.to_str()).ok();
            s
        })
    }
}