terrr 0.1.1

a linux horror game
use pulldown_cmark::{Parser, Event, Tag};
use ratatui::style::{Color, Style};
use std::path::Path;
use std::fs;

/// Represents a styled text segment for rendering markdown
pub struct StyledText {
    pub text: String,
    pub style: Style,
}

/// Renderer for Markdown content
pub struct MarkdownRenderer {
    pub styled_lines: Vec<Vec<StyledText>>,
}

impl MarkdownRenderer {
    /// Create a new markdown renderer from a file path
    pub fn from_file<P: AsRef<Path>>(path: P) -> anyhow::Result<Self> {
        let content = fs::read_to_string(path)?;
        Ok(Self::from_string(&content))
    }
    
    /// Create a new markdown renderer from a string
    pub fn from_string(markdown: &str) -> Self {
        let mut renderer = Self {
            styled_lines: Vec::new(),
        };
        
        let parser = Parser::new(markdown);
        renderer.process_events(parser);
        
        renderer
    }
    
    fn process_events<'a, I>(&mut self, parser: I)
    where
        I: Iterator<Item = Event<'a>>,
    {
        let mut current_line = Vec::new();
        let mut current_style = Style::default().fg(Color::White);
        let mut in_header = false;
        let mut in_bold = false;
        let mut in_italic = false;
        
        for event in parser {
            match event {
                Event::Start(tag) => match tag {
                    Tag::Heading(level, ..) => {
                        // Always ensure headers start on a new line
                        if !current_line.is_empty() {
                            self.styled_lines.push(current_line);
                            current_line = Vec::new();
                        }
                        
                        // Add an extra empty line before headers (except at the very beginning)
                        if !self.styled_lines.is_empty() {
                            self.styled_lines.push(Vec::new());
                        }
                        
                        in_header = true;
                        // Different header levels get different colors
                        current_style = match level {
                            pulldown_cmark::HeadingLevel::H1 => Style::default().fg(Color::Red),
                            pulldown_cmark::HeadingLevel::H2 => Style::default().fg(Color::Yellow),
                            _ => Style::default().fg(Color::Green),
                        };
                    },
                    Tag::Strong => {
                        in_bold = true;
                        // Bold text gets a brighter color
                        current_style = current_style.fg(Color::White);
                    },
                    Tag::Emphasis => {
                        in_italic = true;
                        // Italic text gets a different color
                        current_style = current_style.fg(Color::Cyan);
                    },
                    Tag::Paragraph => {
                        // Start a new paragraph
                        if !current_line.is_empty() {
                            self.styled_lines.push(current_line);
                            current_line = Vec::new();
                        }
                    },
                    _ => {}
                },
                Event::End(tag) => match tag {
                    Tag::Heading(..) => {
                        in_header = false;
                        self.styled_lines.push(current_line);
                        current_line = Vec::new();
                        
                        // Add an extra empty line after headers
                        self.styled_lines.push(Vec::new());
                        
                        current_style = Style::default().fg(Color::White);
                    },
                    Tag::Strong => {
                        in_bold = false;
                        // Reset style based on context
                        if in_header {
                            current_style = Style::default().fg(Color::Red);
                        } else if in_italic {
                            current_style = Style::default().fg(Color::Cyan);
                        } else {
                            current_style = Style::default().fg(Color::White);
                        }
                    },
                    Tag::Emphasis => {
                        in_italic = false;
                        // Reset style based on context
                        if in_header {
                            current_style = Style::default().fg(Color::Red);
                        } else if in_bold {
                            current_style = Style::default().fg(Color::White);
                        } else {
                            current_style = Style::default().fg(Color::White);
                        }
                    },
                    Tag::Paragraph => {
                        // End paragraph with empty line
                        self.styled_lines.push(Vec::new());
                    },
                    _ => {}
                },
                Event::Text(text) => {
                    current_line.push(StyledText {
                        text: text.to_string(),
                        style: current_style,
                    });
                },
                Event::SoftBreak => {
                    self.styled_lines.push(current_line);
                    current_line = Vec::new();
                },
                Event::HardBreak => {
                    self.styled_lines.push(current_line);
                    current_line = Vec::new();
                },
                _ => {}
            }
        }
        
        // Add any remaining content
        if !current_line.is_empty() {
            self.styled_lines.push(current_line);
        }
    }
}