use std::vec::Vec;

#[cfg(feature = "color")]
use colored::Color;
#[cfg(feature = "color")]
use colored::ColoredString;
#[cfg(feature = "color")]
use colored::Colorize;

pub struct Config {
    width: u32,
    height: u32,
    offset: u32,
    caption: String,
    #[cfg(feature = "color")]
    caption_color: Color,
    #[cfg(feature = "color")]
    axis_color: Color,
    #[cfg(feature = "color")]
    label_color: Color,
    #[cfg(feature = "color")]
    series_colors: Vec<Color>,
    #[cfg(feature = "color")]
    series_legends: Vec<String>,
}

impl Default for Config {
    fn default() -> Self {
        Config {
            width: 0,
            height: 0,
            offset: 0,
            caption: String::new(),
            #[cfg(feature = "color")]
            caption_color: Color::White,
            #[cfg(feature = "color")]
            axis_color: Color::White,
            #[cfg(feature = "color")]
            label_color: Color::White,
            #[cfg(feature = "color")]
            series_colors: vec![],
            #[cfg(feature = "color")]
            series_legends: Vec::new(),
        }
    }
}

impl Config {
    pub fn with_caption(mut self, caption: String) -> Self {
        self.caption = caption;
        self
    }

    pub fn with_height(mut self, height: u32) -> Self {
        self.height = height;
        self
    }

    pub fn with_width(mut self, width: u32) -> Self {
        self.width = width;
        self
    }

    pub fn with_offset(mut self, offset: u32) -> Self {
        self.offset = offset;
        self
    }

    #[cfg(feature = "color")]
    pub fn with_caption_color(mut self, color: Color) -> Self {
        self.caption_color = color;
        self
    }

    #[cfg(feature = "color")]
    pub fn with_axis_color(mut self, color: Color) -> Self {
        self.axis_color = color;
        self
    }

    #[cfg(feature = "color")]
    pub fn with_label_color(mut self, color: Color) -> Self {
        self.label_color = color;
        self
    }

    #[cfg(feature = "color")]
    pub fn with_series_colors(mut self, colors: Vec<Color>) -> Self {
        self.series_colors = colors;
        self
    }

    #[cfg(feature = "color")]
    pub fn with_series_legends(mut self, legends: Vec<String>) -> Self {
        self.series_legends = legends;
        self
    }
}

pub fn plot(series: Vec<f64>, config: Config) -> String {
    plot_many(vec![series], config)
}

pub fn plot_many(mut series: Vec<Vec<f64>>, mut config: Config) -> String {
    let mut len_max = series.iter().map(|s| s.len()).max().unwrap_or(0);
    if config.width > 0 {
        series.iter_mut().for_each(|s| {
            if s.len() < len_max {
                s.extend(vec![f64::NAN].repeat(len_max - s.len()))
            }
            *s = interpolate(s, config.width);
        });
        len_max = config.width as usize;
    }

    let mut min = f64::MAX;
    let mut max = f64::MIN;

    (min, max) = series.iter().map(|s| min_max(s)).fold(
        (min, max),
        |(current_min, current_max), (next_min, next_max)| {
            (
                f64::min(next_min, current_min),
                f64::max(next_max, current_max),
            )
        },
    );

    let interval = (max - min).abs();
    if config.height == 0 {
        if interval == 0f64 {
            config.height = 3;
        } else if interval <= 1f64 {
            config.height =
                (interval * f64::from(10i32.pow((-interval.log10()).ceil() as u32))) as u32;
        } else {
            config.height = interval as u32;
        }
    }

    if config.offset == 0 {
        config.offset = 3;
    }

    let ratio = if interval != 0f64 {
        f64::from(config.height) / interval
    } else {
        1f64
    };

    let min2 = (min * ratio).round();
    let max2 = (max * ratio).round();

    let int_min2 = min2 as i32;
    let int_max2 = max2 as i32;

    let rows = f64::from(int_max2 - int_min2).abs() as i32;
    let width = len_max as u32 + config.offset;

    let mut plot: Vec<Vec<String>> = Vec::new();

    for _i in 0..=rows {
        let mut line = Vec::<String>::new();
        for _j in 0..width {
            line.push(" ".to_string());
        }
        plot.push(line);
    }

    let mut precision = 2;
    let log_maximum = if min == 0f64 && max == 0f64 {
        -1f64
    } else {
        f64::max(max.abs(), min.abs()).log10()
    };

    if log_maximum < 0f64 {
        if log_maximum % 1f64 != 0f64 {
            precision += log_maximum.abs() as i32;
        } else {
            precision += (log_maximum.abs() - 1f64) as i32;
        }
    } else if log_maximum > 2f64 {
        precision = 0;
    }

    let max_number_label_length = format!("{:.*}", precision as usize, max).len();
    let min_number_label_length = format!("{:.*}", precision as usize, min).len();

    let max_label_width = usize::max(max_number_label_length, min_number_label_length);

    for y in int_min2..=int_max2 {
        let magnitude = if rows > 0 {
            max - f64::from(y - int_min2) * interval / f64::from(rows)
        } else {
            f64::from(y)
        };
        let label = format!(
            "{number:LW$.PREC$}",
            LW = max_label_width + 1,
            PREC = precision as usize,
            number = magnitude
        );
        let w = (y - int_min2) as usize;
        let h = f64::max(f64::from(config.offset) - label.len() as f64, 0f64) as usize;
        plot[w][h] = label;
        plot[w][(config.offset - 1) as usize] = "┤".to_string();
    }

    for series_inner in series {
        let mut y0;
        let mut y1;
        if !series_inner[0].is_nan() {
            y0 = ((series_inner[0] * ratio).round() - min2) as i32;
            plot[(rows - y0) as usize][(config.offset - 1) as usize] = "┼".to_string();
        }

        for x in 0..series_inner.len() - 1 {
            if series_inner[x].is_nan() && series_inner[x + 1].is_nan() {
                continue;
            }
            if series_inner[x + 1].is_nan() && !series_inner[x].is_nan() {
                y0 = ((series_inner[x] * ratio).round() - f64::from(int_min2)) as i32;
                plot[(rows - y0) as usize][(x as u32 + config.offset) as usize] = "─".to_string();
                continue;
            }
            if series_inner[x].is_nan() && !series_inner[x + 1].is_nan() {
                y1 = ((series_inner[x + 1] * ratio).round() - f64::from(int_min2)) as i32;
                plot[(rows - y1) as usize][(x as u32 + config.offset) as usize] = "─".to_string();
                continue;
            }
            y0 = ((series_inner[x] * ratio).round() - f64::from(int_min2)) as i32;
            y1 = ((series_inner[x + 1] * ratio).round() - f64::from(int_min2)) as i32;

            if y0 == y1 {
                plot[(rows - y0) as usize][(x as u32 + config.offset) as usize] = "─".to_string();
            } else {
                if y0 > y1 {
                    plot[(rows - y1) as usize][(x as u32 + config.offset) as usize] =
                        "╰".to_string();
                    plot[(rows - y0) as usize][(x as u32 + config.offset) as usize] =
                        "╮".to_string();
                } else {
                    plot[(rows - y1) as usize][(x as u32 + config.offset) as usize] =
                        "╭".to_string();
                    plot[(rows - y0) as usize][(x as u32 + config.offset) as usize] =
                        "╯".to_string();
                }

                let start = f64::min(f64::from(y0), f64::from(y1)) as i32 + 1;
                let end = f64::max(f64::from(y0), f64::from(y1)) as i32;

                for y in start..end {
                    plot[(rows - y) as usize][(x as u32 + config.offset) as usize] =
                        "│".to_string();
                }
            }
        }
    }

    let mut res: String = plot
        .into_iter()
        .map(|line| line.join(""))
        .collect::<Vec<String>>()
        .join("\n");
    res.pop();
    if !config.caption.is_empty() {
        res.push('\n');
        res.push_str(
            std::iter::repeat(" ")
                .take(config.offset as usize + max_label_width as usize)
                .collect::<String>()
                .as_ref(),
        );
        if config.caption.len() < len_max {
            res.push_str(
                std::iter::repeat(" ")
                    .take((len_max - config.caption.len()) / 2)
                    .collect::<String>()
                    .as_ref(),
            );
        }
        res.push_str(config.caption.as_ref());
    }
    res
}

#[cfg(feature = "color")]
fn create_legend_item(text: &str, color: colored::Color) -> (String, usize) {
    use colored::Colorize;

    let colored_box = "■".color(color).to_string(); // Colored box
    let default_color = "".normal().to_string(); // Reset to default color
    let legend_item = format!("{}{} {}", colored_box, default_color, text);

    // Calculate the length of the legend item (accounting for the box and space)
    let legend_length = text.chars().count() + 2; // 2 for the box and space

    (legend_item, legend_length)
}

#[cfg(feature = "color")]
fn add_legends(lines: &mut String, config: &Config, len_max: usize, left_pad: usize) {
    use std::iter;

    lines.push_str("\n\n");
    lines.push_str(&iter::repeat(" ").take(left_pad).collect::<String>());

    let mut legends_text = String::new();
    let mut legends_text_len = 0;
    let right_pad = 3;

    for (i, text) in config.series_legends.iter().enumerate() {
        let (item, item_len) = create_legend_item(text, config.series_colors[i]);
        legends_text.push_str(&item);
        legends_text_len += item_len;

        if i < config.series_legends.len() - 1 {
            legends_text.push_str(&iter::repeat(" ").take(right_pad).collect::<String>());
            legends_text_len += right_pad;
        }
    }

    if legends_text_len < len_max {
        lines.push_str(
            &iter::repeat(" ")
                .take((len_max - legends_text_len) / 2)
                .collect::<String>(),
        );
    }

    lines.push_str(&legends_text);
}

#[cfg(feature = "color")]
pub fn plot_colored(series: Vec<f64>, config: Config) -> ColoredString {
    plot_many_colored(vec![series], config)
}

#[cfg(feature = "color")]
pub fn plot_many_colored(mut series: Vec<Vec<f64>>, mut config: Config) -> ColoredString {
    let mut len_max = series.iter().map(|s| s.len()).max().unwrap_or(0);
    if config.width > 0 {
        series.iter_mut().for_each(|s| {
            if s.len() < len_max {
                s.extend(vec![f64::NAN].repeat(len_max - s.len()))
            }
            *s = interpolate(s, config.width);
        });
        len_max = config.width as usize;
    }

    let mut min = f64::MAX;
    let mut max = f64::MIN;

    (min, max) = series.iter().map(|s| min_max(s)).fold(
        (min, max),
        |(current_min, current_max), (next_min, next_max)| {
            (
                f64::min(next_min, current_min),
                f64::max(next_max, current_max),
            )
        },
    );

    let interval = (max - min).abs();
    if config.height == 0 {
        if interval == 0f64 {
            config.height = 3;
        } else if interval <= 1f64 {
            config.height =
                (interval * f64::from(10i32.pow((-interval.log10()).ceil() as u32))) as u32;
        } else {
            config.height = interval as u32;
        }
    }

    if config.offset == 0 {
        config.offset = 3;
    }

    let ratio = if interval != 0f64 {
        f64::from(config.height) / interval
    } else {
        1f64
    };

    let min2 = (min * ratio).round();
    let max2 = (max * ratio).round();

    let int_min2 = min2 as i32;
    let int_max2 = max2 as i32;

    let rows = f64::from(int_max2 - int_min2).abs() as i32;
    let width = len_max as u32 + config.offset;

    let mut plot: Vec<Vec<ColoredString>> = Vec::new();

    for _i in 0..=rows {
        let mut line = Vec::<ColoredString>::new();
        for _j in 0..width {
            line.push(" ".to_string().into());
        }
        plot.push(line);
    }

    let mut precision = 2;
    let log_maximum = if min == 0f64 && max == 0f64 {
        -1f64
    } else {
        f64::max(max.abs(), min.abs()).log10()
    };

    if log_maximum < 0f64 {
        if log_maximum % 1f64 != 0f64 {
            precision += log_maximum.abs() as i32;
        } else {
            precision += (log_maximum.abs() - 1f64) as i32;
        }
    } else if log_maximum > 2f64 {
        precision = 0;
    }

    let max_number_label_length = format!("{:.*}", precision as usize, max).len();
    let min_number_label_length = format!("{:.*}", precision as usize, min).len();

    let max_label_width = usize::max(max_number_label_length, min_number_label_length);

    for y in int_min2..=int_max2 {
        let magnitude = if rows > 0 {
            max - f64::from(y - int_min2) * interval / f64::from(rows)
        } else {
            f64::from(y)
        };
        let label = format!(
            "{number:LW$.PREC$}",
            LW = max_label_width + 1,
            PREC = precision as usize,
            number = magnitude
        );
        let w = (y - int_min2) as usize;
        let h = f64::max(f64::from(config.offset) - label.len() as f64, 0f64) as usize;
        plot[w][h] = label.color(config.axis_color);
        plot[w][(config.offset - 1) as usize] = "┤".to_string().color(config.axis_color);
    }

    for (i, series_inner) in series.iter().enumerate() {
        let mut y0;
        let mut y1;
        if !series_inner[0].is_nan() {
            y0 = ((series_inner[0] * ratio).round() - min2) as i32;
            plot[(rows - y0) as usize][(config.offset - 1) as usize] =
                "┼".to_string().color(config.axis_color);
        }

        for x in 0..series_inner.len() - 1 {
            if series_inner[x].is_nan() && series_inner[x + 1].is_nan() {
                continue;
            }
            if series_inner[x + 1].is_nan() && !series_inner[x].is_nan() {
                y0 = ((series_inner[x] * ratio).round() - f64::from(int_min2)) as i32;
                plot[(rows - y0) as usize][(x as u32 + config.offset) as usize] =
                    "─".to_string().color(config.series_colors[i]);
                continue;
            }
            if series_inner[x].is_nan() && !series_inner[x + 1].is_nan() {
                y1 = ((series_inner[x + 1] * ratio).round() - f64::from(int_min2)) as i32;
                plot[(rows - y1) as usize][(x as u32 + config.offset) as usize] =
                    "─".to_string().color(config.series_colors[i]);
                continue;
            }
            y0 = ((series_inner[x] * ratio).round() - f64::from(int_min2)) as i32;
            y1 = ((series_inner[x + 1] * ratio).round() - f64::from(int_min2)) as i32;

            if y0 == y1 {
                plot[(rows - y0) as usize][(x as u32 + config.offset) as usize] =
                    "─".to_string().color(config.series_colors[i]);
            } else {
                if y0 > y1 {
                    plot[(rows - y1) as usize][(x as u32 + config.offset) as usize] =
                        "╰".to_string().color(config.series_colors[i]);
                    plot[(rows - y0) as usize][(x as u32 + config.offset) as usize] =
                        "╮".to_string().color(config.series_colors[i]);
                } else {
                    plot[(rows - y1) as usize][(x as u32 + config.offset) as usize] =
                        "╭".to_string().color(config.series_colors[i]);
                    plot[(rows - y0) as usize][(x as u32 + config.offset) as usize] =
                        "╯".to_string().color(config.series_colors[i]);
                }

                let start = f64::min(f64::from(y0), f64::from(y1)) as i32 + 1;
                let end = f64::max(f64::from(y0), f64::from(y1)) as i32;

                for y in start..end {
                    plot[(rows - y) as usize][(x as u32 + config.offset) as usize] =
                        "│".to_string().color(config.series_colors[i]);
                }
            }
        }
    }

    let mut res: String = plot
        .into_iter()
        .map(|line| line.into_iter().map(|s| s.to_string()).collect::<String>())
        .collect::<Vec<String>>()
        .join("\n");

    //res.pop();

    let mut caption = String::new();

    if !config.caption.is_empty() {
        caption.push('\n');
        caption.push_str(
            std::iter::repeat(" ")
                .take(config.offset as usize + max_label_width as usize)
                .collect::<String>()
                .as_ref(),
        );
        if config.caption.len() < len_max {
            caption.push_str(
                std::iter::repeat(" ")
                    .take((len_max - config.caption.len()) / 2)
                    .collect::<String>()
                    .as_ref(),
            );
        }
        caption.push_str(
            config
                .caption
                .color(config.caption_color)
                .to_string()
                .as_ref(),
        );
    }
    res.push_str(caption.as_ref());

    if config.series_legends.len() > 0 {
        add_legends(
            &mut res,
            &config,
            len_max,
            config.offset as usize + max_label_width,
        )
    }
    res.into()
}

fn interpolate(series: &[f64], count: u32) -> Vec<f64> {
    let mut result = Vec::new();
    let spring_factor = (series.len() - 1) as f64 / f64::from(count - 1);
    result.push(series[0]);
    for i in 1..count - 1 {
        let spring = f64::from(i) * spring_factor;
        let before = spring.floor();
        let after = spring.ceil();
        let at_point = spring - before;
        result.push(linear_interpolate(
            series[before as usize],
            series[after as usize],
            at_point,
        ))
    }
    result.push(series[series.len() - 1]);
    result
}

fn linear_interpolate(before: f64, after: f64, at_point: f64) -> f64 {
    before + (after - before) * at_point
}

fn min_max(series: &[f64]) -> (f64, f64) {
    let min = series
        .iter()
        .fold(std::f64::MAX, |accu, &x| if x < accu { x } else { accu });
    let max = series
        .iter()
        .fold(std::f64::MIN, |accu, &x| if x > accu { x } else { accu });
    (min, max)
}

#[cfg(test)]
#[rustfmt::skip]
mod tests {

    macro_rules! graph_eq {
        ($fname:ident ? [$($series:expr),*]  => $rhs:expr) => {
            #[test]
            fn $fname(){
              let res = crate::plot(vec![$(f64::from($series),)*], crate::Config::default());
              assert_eq!(res, $rhs);
        }};
        ($fname:ident ? [$($series:expr),*]  ? $config:expr => $rhs:expr) => {
            #[test]
            fn $fname(){
              let res = crate::plot(vec![$(f64::from($series),)*], $config);
              assert_eq!(res, $rhs);
        }};
    }

    graph_eq!(test_ones  ? [1, 1, 1, 1, 1] => " 1.00 ┼────");
    graph_eq!(test_zeros ? [0, 0, 0, 0, 0] => " 0.00 ┼────");
    graph_eq!(test_three ? [2, 1, 1, 2, -2, 5, 7, 11, 3, 7, 1] => " 11.00 ┤      ╭╮   
 10.00 ┤      ││   
  9.00 ┤      ││   
  8.00 ┤      ││   
  7.00 ┤     ╭╯│╭╮ 
  6.00 ┤     │ │││ 
  5.00 ┤    ╭╯ │││ 
  4.00 ┤    │  │││ 
  3.00 ┤    │  ╰╯│ 
  2.00 ┼╮ ╭╮│    │ 
  1.00 ┤╰─╯││    ╰ 
  0.00 ┤   ││      
 -1.00 ┤   ││      
 -2.00 ┤   ╰╯     ");

    graph_eq!(test_four ? [2, 1, 1, 2, -2, 5, 7, 11, 3, 7, 4, 5, 6, 9, 4, 0, 6, 1, 5, 3, 6, 2] ? 
    crate::Config::default().with_caption("Plot using asciigraph.".to_string()) 
    => " 11.00 ┤      ╭╮              
 10.00 ┤      ││              
  9.00 ┤      ││    ╭╮        
  8.00 ┤      ││    ││        
  7.00 ┤     ╭╯│╭╮  ││        
  6.00 ┤     │ │││ ╭╯│ ╭╮  ╭╮ 
  5.00 ┤    ╭╯ │││╭╯ │ ││╭╮││ 
  4.00 ┤    │  ││╰╯  ╰╮││││││ 
  3.00 ┤    │  ╰╯     ││││╰╯│ 
  2.00 ┼╮ ╭╮│         ││││  ╰ 
  1.00 ┤╰─╯││         ││╰╯    
  0.00 ┤   ││         ╰╯      
 -1.00 ┤   ││                 
 -2.00 ┤   ╰╯                
        Plot using asciigraph.");

    graph_eq!(test_five ? [ 2, 1, 1, 2, -2, 5, 7, 11, 3, 7, 4, 5, 6, 9, 4, 0, 6, 1, 5, 3, 6, 2] ? 
                crate::Config::default().with_caption("Plot using asciigraph.".to_string()) 
     => " 11.00 ┤      ╭╮              
 10.00 ┤      ││              
  9.00 ┤      ││    ╭╮        
  8.00 ┤      ││    ││        
  7.00 ┤     ╭╯│╭╮  ││        
  6.00 ┤     │ │││ ╭╯│ ╭╮  ╭╮ 
  5.00 ┤    ╭╯ │││╭╯ │ ││╭╮││ 
  4.00 ┤    │  ││╰╯  ╰╮││││││ 
  3.00 ┤    │  ╰╯     ││││╰╯│ 
  2.00 ┼╮ ╭╮│         ││││  ╰ 
  1.00 ┤╰─╯││         ││╰╯    
  0.00 ┤   ││         ╰╯      
 -1.00 ┤   ││                 
 -2.00 ┤   ╰╯                
        Plot using asciigraph." );

    graph_eq!(test_six ? [0.2, 0.1, 0.2, 2, -0.9, 0.7, 0.91, 0.3, 0.7, 0.4, 0.5] ? 
    crate::Config::default().with_caption("Plot using asciigraph.".to_string())
    => "  2.00 ┤  ╭╮ ╭╮    
  0.55 ┼──╯│╭╯╰─── 
 -0.90 ┤   ╰╯     
        Plot using asciigraph." );

    graph_eq!(test_seven ? [2, 1, 1, 2, -2, 5, 7, 11, 3, 7, 1] ? 
    crate::Config::default().with_height(4).with_offset(3)
    => " 11.00 ┤      ╭╮   
  7.75 ┤    ╭─╯│╭╮ 
  4.50 ┼╮ ╭╮│  ╰╯│ 
  1.25 ┤╰─╯││    ╰ 
 -2.00 ┤   ╰╯     "
    );

    graph_eq!(test_eight ? [0.453, 0.141, 0.951, 0.251, 0.223, 0.581, 0.771, 0.191, 0.393, 0.617, 0.478]
    => " 0.95 ┤ ╭╮        
 0.85 ┤ ││  ╭╮    
 0.75 ┤ ││  ││    
 0.65 ┤ ││ ╭╯│ ╭╮ 
 0.55 ┤ ││ │ │ │╰ 
 0.44 ┼╮││ │ │╭╯  
 0.34 ┤│││ │ ││   
 0.24 ┤││╰─╯ ╰╯   
 0.14 ┤╰╯        ");

    graph_eq!(test_nine ? [0.01, 0.004, 0.003, 0.0042, 0.0083, 0.0033, 0.0079] 
    => " 0.010 ┼╮      
 0.009 ┤│      
 0.008 ┤│  ╭╮╭ 
 0.007 ┤│  │││ 
 0.006 ┤│  │││ 
 0.005 ┤│  │││ 
 0.004 ┤╰╮╭╯││ 
 0.003 ┤ ╰╯ ╰╯"
    );

    graph_eq!(test_ten ? [192, 431, 112, 449, -122, 375, 782, 123, 911, 1711, 172] ? crate::Config::default().with_height(10)
    => " 1711 ┤        ╭╮ 
 1528 ┤        ││ 
 1344 ┤        ││ 
 1161 ┤        ││ 
  978 ┤       ╭╯│ 
  794 ┤     ╭╮│ │ 
  611 ┤     │││ │ 
  428 ┤╭╮╭╮╭╯││ │ 
  245 ┼╯╰╯││ ╰╯ ╰ 
   61 ┤   ││      
 -122 ┤   ╰╯     ");

    graph_eq!(test_eleven ? [0.3189989805, 0.149949026, 0.30142492354, 0.195129182935, 0.3142492354, 
    0.1674974513, 0.3142492354, 0.1474974513, 0.3047974513] ?
    crate::Config::default().with_width(30).with_height(5).with_caption("Plot with custom height & width.".to_string())
        => " 0.32 ┼╮            ╭─╮     ╭╮     ╭ 
 0.29 ┤╰╮    ╭─╮   ╭╯ │    ╭╯│     │ 
 0.26 ┤ │   ╭╯ ╰╮ ╭╯  ╰╮  ╭╯ ╰╮   ╭╯ 
 0.23 ┤ ╰╮ ╭╯   ╰╮│    ╰╮╭╯   ╰╮ ╭╯  
 0.20 ┤  ╰╮│     ╰╯     ╰╯     │╭╯   
 0.16 ┤   ╰╯                   ╰╯   
       Plot with custom height & width."
    );

    graph_eq!(test_twelve ? [0, 0, 0, 0, 1.5, 0, 0, -0.5, 9, -3, 0, 0, 1, 2, 1, 0, 0, 0, 0,
				0, 0, 0, 0, 1.5, 0, 0, -0.5, 8, -3, 0, 0, 1, 2, 1, 0, 0, 0, 0,
				0, 0, 0, 0, 1.5, 0, 0, -0.5, 10, -3, 0, 0, 1, 2, 1, 0, 0, 0, 0] ? 
                crate::Config::default().with_offset(10).with_height(10).with_caption("I'm a doctor, not an engineer.".to_string())
    => "     10.00    ┤                                             ╭╮          
      8.70    ┤       ╭╮                                    ││          
      7.40    ┤       ││                 ╭╮                 ││          
      6.10    ┤       ││                 ││                 ││          
      4.80    ┤       ││                 ││                 ││          
      3.50    ┤       ││                 ││                 ││          
      2.20    ┤       ││   ╭╮            ││   ╭╮            ││   ╭╮     
      0.90    ┤   ╭╮  ││  ╭╯╰╮       ╭╮  ││  ╭╯╰╮       ╭╮  ││  ╭╯╰╮    
     -0.40    ┼───╯╰──╯│╭─╯  ╰───────╯╰──╯│╭─╯  ╰───────╯╰──╯│╭─╯  ╰─── 
     -1.70    ┤        ││                 ││                 ││         
     -3.00    ┤        ╰╯                 ╰╯                 ╰╯        
                            I'm a doctor, not an engineer.");

    graph_eq!(test_thirteen ? [-5, -2, -3, -4, 0, -5, -6, -7, -8, 0, -9, -3, -5, -2, -9, -3, -1]
    => "  0.00 ┤   ╭╮   ╭╮       
 -1.00 ┤   ││   ││     ╭ 
 -2.00 ┤╭╮ ││   ││  ╭╮ │ 
 -3.00 ┤│╰╮││   ││╭╮││╭╯ 
 -4.00 ┤│ ╰╯│   │││││││  
 -5.00 ┼╯   ╰╮  │││╰╯││  
 -6.00 ┤     ╰╮ │││  ││  
 -7.00 ┤      ╰╮│││  ││  
 -8.00 ┤       ╰╯││  ││  
 -9.00 ┤         ╰╯  ╰╯ ");

    graph_eq!(test_fourteen ? [-0.000018527, -0.021, -0.00123, 0.00000021312, 
    -0.0434321234, -0.032413241234, 0.0000234234] ?
        crate::Config::default().with_height(5).with_width(45)
        => "  0.000 ┼─╮           ╭────────╮                    ╭ 
 -0.008 ┤ ╰──╮     ╭──╯        ╰─╮                ╭─╯ 
 -0.017 ┤    ╰─────╯             ╰╮             ╭─╯   
 -0.025 ┤                         ╰─╮         ╭─╯     
 -0.034 ┤                           ╰╮   ╭────╯       
 -0.042 ┤                            ╰───╯           "
    );

    graph_eq!(test_fifteen ? [57.76, 54.04, 56.31, 57.02, 59.5, 52.63, 52.97, 56.44, 56.75, 52.96, 55.54, 
    55.09, 58.22, 56.85, 60.61, 59.62, 59.73, 59.93, 56.3, 54.69, 55.32, 54.03, 50.98, 50.48, 54.55, 47.49, 
    55.3, 46.74, 46, 45.8, 49.6, 48.83, 47.64, 46.61, 54.72, 42.77, 50.3, 42.79, 41.84, 44.19, 43.36, 45.62, 
    45.09, 44.95, 50.36, 47.21, 47.77, 52.04, 47.46, 44.19, 47.22, 45.55, 40.65, 39.64, 37.26, 40.71, 42.15, 
    36.45, 39.14, 36.62]
   => " 60.61 ┤             ╭╮ ╭╮                                          
 59.60 ┤   ╭╮        │╰─╯│                                          
 58.60 ┤   ││      ╭╮│   │                                          
 57.59 ┼╮ ╭╯│      │││   │                                          
 56.58 ┤│╭╯ │ ╭─╮  │╰╯   ╰╮                                         
 55.58 ┤││  │ │ │╭─╯      │╭╮    ╭╮                                 
 54.57 ┤╰╯  │ │ ││        ╰╯╰╮ ╭╮││      ╭╮                         
 53.56 ┤    │╭╯ ╰╯           │ ││││      ││                         
 52.56 ┤    ╰╯               │ ││││      ││           ╭╮            
 51.55 ┤                     ╰╮││││      ││           ││            
 50.54 ┤                      ╰╯│││      ││╭╮      ╭╮ ││            
 49.54 ┤                        │││  ╭─╮ ││││      ││ ││            
 48.53 ┤                        │││  │ │ ││││      ││ ││            
 47.52 ┤                        ╰╯│  │ ╰╮││││      │╰─╯╰╮╭╮         
 46.52 ┤                          ╰─╮│  ╰╯│││      │    │││         
 45.51 ┤                            ╰╯    │││   ╭──╯    ││╰╮        
 44.50 ┤                                  │││ ╭╮│       ╰╯ │        
 43.50 ┤                                  ││╰╮│╰╯          │        
 42.49 ┤                                  ╰╯ ╰╯            │   ╭╮   
 41.48 ┤                                                   │   ││   
 40.48 ┤                                                   ╰╮ ╭╯│   
 39.47 ┤                                                    ╰╮│ │╭╮ 
 38.46 ┤                                                     ││ │││ 
 37.46 ┤                                                     ╰╯ │││ 
 36.45 ┤                                                        ╰╯╰"
    );

    #[test]
    fn test_min_max() {
        assert_eq!(
            (-2f64, 11f64),
            crate::min_max(&[2f64, 1f64, 1f64, 2f64, -2f64, 5f64, 7f64, 11f64, 3f64, 7f64, 1f64])
        );
    }

    #[test]
    fn test_plot_many(){
        let res = super::plot_many(vec![vec![0f64,1.0,2.0,3.0,3.0,3.0,2.0,0.0], vec![5.0,4.0,2.0,1.0, 4.0, 6.0,6.0]], super::Config::default());
        let exp = " 6.00 ┤    ╭─  
 5.00 ┼╮   │   
 4.00 ┤╰╮ ╭╯   
 3.00 ┤ │╭│─╮  
 2.00 ┤ ╰╮│ ╰╮ 
 1.00 ┤╭╯╰╯  │ 
 0.00 ┼╯     ╰";
        assert_eq!(res, exp);
    }

    #[test]
    fn test_plot_many_two(){
        let res = super::plot_many(vec![vec![0.0f64, 1.0, 0.0], vec![2.0, 3.0, 4.0, 3.0, 2.0], vec![4.0, 5.0, 6.0, 7.0, 6.0, 5.0,4.0]], super::Config::default().with_width(21));
        let exp = " 7.00 ┤        ╭──╮         
 6.00 ┤    ╭───╯  ╰───╮     
 5.00 ┤ ╭──╯          ╰──╮  
 4.00 ┼─╯  ╭───╮         ╰─ 
 3.00 ┤ ╭──╯   ╰──╮         
 2.00 ┼─╯         ╰──       
 1.00 ┤ ╭───╮               
 0.00 ┼─╯   ╰─             ";
        assert_eq!(res, exp);
        // assert_eq!(res.as_bytes(), exp.as_bytes());
    }
    
    #[test]
    fn test_plot_many_three(){
        let res = super::plot_many(vec![vec![0.0f64, 0.0, 2.0, 2.0, f64::NAN], vec![1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0], vec![f64::NAN, f64::NAN, f64::NAN, 0.0, 0.0, 2.0, 2.0]], super::Config::default());
        let exp = " 2.00 ┤ ╭──╭─ 
 1.00 ┼────│─ 
 0.00 ┼─╯──╯ "; 
        assert_eq!(res, exp);
    }

    #[test]
    fn test_plot_many_four(){
        
        let res = super::plot_many(vec![vec![0.1f64, 0.2, 0.3, f64::NAN, 0.5, 0.6, 0.7, f64::NAN, f64::NAN, 0.9, 1.0]], super::Config::default());
        let exp = " 1.00 ┤         ╭ 
 0.90 ┤        ─╯ 
 0.80 ┤           
 0.70 ┤     ╭─    
 0.60 ┤    ╭╯     
 0.50 ┤   ─╯      
 0.40 ┤           
 0.30 ┤ ╭─        
 0.20 ┤╭╯         
 0.10 ┼╯         "; 
        assert_eq!(res, exp);
    }

}
