
======

File path: ./Cargo.toml
[package]
name = "metatron"
version = "0.1.2"
edition = "2021"

authors = ["Evgeny Igumnov <igumnovnsk@gmail.com>"]
repository = "https://github.com/igumnoff/metatron"
documentation = "https://docs.rs/metatron"
license-file = "LICENSE"
keywords = ["report-generation", "data-reporting",  "text-report", "pdf", "template-engine"]
categories = ["text-processing", "web-programming"]
readme = "README.md"
description = "Metatron library"




[dependencies]
anyhow = "1.0.75"
thiserror = "1.0.44"
shiva = {path = "../shiva/lib"}
#shiva = "0.1.11"
serde_yaml = "0.9.34"
serde_json = "1.0.115"
bytes = "1.5.0"

[dev-dependencies]
env_logger = "0.10.0"

======

File path: ./data/report-data.json
{
   "rows": [
     {
       "name": "John",
       "age": 25,
       "salary": 50000
     },
     {
       "name": "Jane",
       "age": 30,
       "salary": 60000
     },
     {
       "name": "Jim",
       "age": 35,
       "salary": 70000
     }
   ],
   "params": {
     "company_name": "ABCDFG Ltd",
     "company_address": "1234 Elm St, Springfield, IL 62701",
     "average_age": 30,
     "average_salary": 60000
   }
}


======

File path: ./data/report-template.yaml
title:
  - header: $P{company_name} Employee Report
    level: 1
page_header:
  - text: Confidential information
    size: 7
column_header:
  - name: Name
    width: 20
  - name: Age
    width: 5
  - name: Salary
    width: 5
row:
  - value: $F(name)
  - value: $F(age)
  - value: $F(salary)
column_footer:
  - value: "AVERAGE:"
  - value: $P{average_age}
  - value: $P{average_salary}
page_footer:
  - text: "Tel: +1 123 456 789"
    size: 7
summary:
  - paragraph:
    - text: "Company address: $P{company_address}"
      size: 10
======

File path: ./src/core.rs
use bytes::Bytes;
use std::any::Any;
use std::collections::HashMap;
use std::fmt::Debug;
use thiserror::Error;

#[derive(Debug)]
pub struct Document {
    pub elements: Vec<Box<dyn Element>>,
    pub page_width: f32,
    pub page_height: f32,
    pub left_page_indent: f32,
    pub right_page_indent: f32,
    pub top_page_indent: f32,
    pub bottom_page_indent: f32,
    pub page_header: Vec<Box<dyn Element>>,
    pub page_footer: Vec<Box<dyn Element>>,
}

impl Document {
    pub fn new(elements: &Vec<Box<dyn Element>>) -> anyhow::Result<Document> {
        Ok(Document {
            elements: (&elements).to_vec(),
            page_width: 210.0,
            page_height: 297.0,
            left_page_indent: 10.0,
            right_page_indent: 10.0,
            top_page_indent: 10.0,
            bottom_page_indent: 10.0,
            page_header: vec![],
            page_footer: vec![],
        })
    }
}

pub trait TransformerTrait {
    fn parse(document: &Bytes, images: &HashMap<String, Bytes>) -> anyhow::Result<Document>;
    fn generate(document: &Document) -> anyhow::Result<(Bytes, HashMap<String, Bytes>)>;
}

#[derive(Error, Debug)]
pub enum ParserError {
    #[error("Parser error")]
    Common,
}
#[derive(Error, Debug)]
pub enum GeneratorError {
    #[error("Generator error")]
    Common,
}

#[derive(Error, Debug)]
pub enum CastingError {
    #[error("CastingError error")]
    Common,
}

pub trait Element: CloneableElement + Debug {
    fn as_any(&self) -> &dyn Any;

    fn as_any_mut(&mut self) -> &mut dyn Any;

    fn element_type(&self) -> ElementType;

    fn paragraph_as_ref(&self) -> anyhow::Result<&ParagraphElement> {
        Ok(self
            .as_any()
            .downcast_ref::<ParagraphElement>()
            .ok_or(CastingError::Common)?)
    }

    fn paragraph_as_mut(&mut self) -> anyhow::Result<&mut ParagraphElement> {
        Ok(self
            .as_any_mut()
            .downcast_mut::<ParagraphElement>()
            .ok_or(CastingError::Common)?)
    }

    fn header_as_ref(&self) -> anyhow::Result<&HeaderElement> {
        Ok(self
            .as_any()
            .downcast_ref::<HeaderElement>()
            .ok_or(CastingError::Common)?)
    }

    fn header_as_mut(&mut self) -> anyhow::Result<&mut HeaderElement> {
        Ok(self
            .as_any_mut()
            .downcast_mut::<HeaderElement>()
            .ok_or(CastingError::Common)?)
    }

    fn text_as_ref(&self) -> anyhow::Result<&TextElement> {
        Ok(self
            .as_any()
            .downcast_ref::<TextElement>()
            .ok_or(CastingError::Common)?)
    }

    fn text_as_mut(&mut self) -> anyhow::Result<&mut TextElement> {
        Ok(self
            .as_any_mut()
            .downcast_mut::<TextElement>()
            .ok_or(CastingError::Common)?)
    }

    fn table_as_ref(&self) -> anyhow::Result<&TableElement> {
        Ok(self
            .as_any()
            .downcast_ref::<TableElement>()
            .ok_or(CastingError::Common)?)
    }

    fn table_as_mut(&mut self) -> anyhow::Result<&mut TableElement> {
        Ok(self
            .as_any_mut()
            .downcast_mut::<TableElement>()
            .ok_or(CastingError::Common)?)
    }

    fn table_header_as_ref(&self) -> anyhow::Result<&TableHeaderElement> {
        Ok(self
            .as_any()
            .downcast_ref::<TableHeaderElement>()
            .ok_or(CastingError::Common)?)
    }

    fn table_header_as_mut(&mut self) -> anyhow::Result<&mut TableHeaderElement> {
        Ok(self
            .as_any_mut()
            .downcast_mut::<TableHeaderElement>()
            .ok_or(CastingError::Common)?)
    }

    fn table_row_as_ref(&self) -> anyhow::Result<&TableRowElement> {
        Ok(self
            .as_any()
            .downcast_ref::<TableRowElement>()
            .ok_or(CastingError::Common)?)
    }

    fn table_row_as_mut(&mut self) -> anyhow::Result<&mut TableRowElement> {
        Ok(self
            .as_any_mut()
            .downcast_mut::<TableRowElement>()
            .ok_or(CastingError::Common)?)
    }

    fn table_cell_as_ref(&self) -> anyhow::Result<&TableCellElement> {
        Ok(self
            .as_any()
            .downcast_ref::<TableCellElement>()
            .ok_or(CastingError::Common)?)
    }

    fn table_cell_as_mut(&mut self) -> anyhow::Result<&mut TableCellElement> {
        Ok(self
            .as_any_mut()
            .downcast_mut::<TableCellElement>()
            .ok_or(CastingError::Common)?)
    }

    fn list_as_ref(&self) -> anyhow::Result<&ListElement> {
        Ok(self
            .as_any()
            .downcast_ref::<ListElement>()
            .ok_or(CastingError::Common)?)
    }

    fn list_as_mut(&mut self) -> anyhow::Result<&mut ListElement> {
        Ok(self
            .as_any_mut()
            .downcast_mut::<ListElement>()
            .ok_or(CastingError::Common)?)
    }

    fn list_item_as_ref(&self) -> anyhow::Result<&ListItemElement> {
        Ok(self
            .as_any()
            .downcast_ref::<ListItemElement>()
            .ok_or(CastingError::Common)?)
    }

    fn list_item_as_mut(&mut self) -> anyhow::Result<&mut ListItemElement> {
        Ok(self
            .as_any_mut()
            .downcast_mut::<ListItemElement>()
            .ok_or(CastingError::Common)?)
    }

    fn image_as_ref(&self) -> anyhow::Result<&ImageElement> {
        Ok(self
            .as_any()
            .downcast_ref::<ImageElement>()
            .ok_or(CastingError::Common)?)
    }

    fn image_as_mut(&mut self) -> anyhow::Result<&mut ImageElement> {
        Ok(self
            .as_any_mut()
            .downcast_mut::<ImageElement>()
            .ok_or(CastingError::Common)?)
    }

    fn hyperlink_as_ref(&self) -> anyhow::Result<&HyperlinkElement> {
        Ok(self
            .as_any()
            .downcast_ref::<HyperlinkElement>()
            .ok_or(CastingError::Common)?)
    }

    fn hyperlink_as_mut(&mut self) -> anyhow::Result<&mut HyperlinkElement> {
        Ok(self
            .as_any_mut()
            .downcast_mut::<HyperlinkElement>()
            .ok_or(CastingError::Common)?)
    }
}

pub trait CloneableElement {
    fn clone_box(&self) -> Box<dyn Element>;
}

impl<T> CloneableElement for T
where
    T: 'static + Element + Clone,
{
    fn clone_box(&self) -> Box<dyn Element> {
        Box::new(self.clone())
    }
}

impl Clone for Box<dyn Element> {
    fn clone(&self) -> Box<dyn Element> {
        self.clone_box()
    }
}

#[derive(Debug, PartialEq)]
pub enum ElementType {
    Text,
    Paragraph,
    Image,
    Hyperlink,
    Header,
    Table,
    TableHeader,
    TableRow,
    TableCell,
    List,
    ListItem,
    PageBreak,
    TableOfContents,
}

#[derive(Clone, Debug)]
pub struct TextElement {
    pub text: String,
    pub size: u8,
}

impl TextElement {
    pub fn new(text: &str, size: u8) -> anyhow::Result<Box<dyn Element>> {
        Ok(Box::new(TextElement {
            text: text.to_string(),
            size,
        }))
    }
}

impl Element for TextElement {
    fn as_any(&self) -> &dyn Any {
        self
    }

    fn as_any_mut(&mut self) -> &mut dyn Any {
        self
    }

    fn element_type(&self) -> ElementType {
        ElementType::Text
    }
}

#[derive(Clone, Debug)]
pub struct HeaderElement {
    pub level: u8,
    pub text: String,
    pub children: Vec<Box<dyn Element>>,
}

impl HeaderElement {
    pub fn new(text: &str, level: u8) -> anyhow::Result<Box<dyn Element>> {
        Ok(Box::new(HeaderElement {
            level,
            text: text.to_string(),
            children: vec![],
        }))
    }
}

impl Element for HeaderElement {
    fn as_any(&self) -> &dyn Any {
        self
    }

    fn as_any_mut(&mut self) -> &mut dyn Any {
        self
    }

    fn element_type(&self) -> ElementType {
        ElementType::Header
    }
}

#[derive(Clone, Debug)]
pub struct ParagraphElement {
    pub elements: Vec<Box<dyn Element>>,
}

impl ParagraphElement {
    pub fn new(elements: &Vec<Box<dyn Element>>) -> anyhow::Result<Box<dyn Element>> {
        Ok(Box::new(ParagraphElement {
            elements: (&elements).to_vec(),
        }))
    }
}

impl Element for ParagraphElement {
    fn as_any(&self) -> &dyn Any {
        self
    }

    fn as_any_mut(&mut self) -> &mut dyn Any {
        self
    }

    fn element_type(&self) -> ElementType {
        ElementType::Paragraph
    }
}

#[derive(Clone, Debug)]
pub struct TableElement {
    pub rows: Vec<TableRowElement>,
    pub headers: Vec<TableHeaderElement>,
}

impl TableElement {
    pub fn new(
        headers: &Vec<TableHeaderElement>,
        rows: &Vec<TableRowElement>,
    ) -> anyhow::Result<Box<dyn Element>> {
        Ok(Box::new(TableElement {
            rows: rows.to_vec(),
            headers: headers.to_vec(),
        }))
    }
}

impl Element for TableElement {
    fn as_any(&self) -> &dyn Any {
        self
    }

    fn as_any_mut(&mut self) -> &mut dyn Any {
        self
    }

    fn element_type(&self) -> ElementType {
        ElementType::Table
    }
}

#[derive(Clone, Debug)]
pub struct TableHeaderElement {
    pub element: Box<dyn Element>,
    pub width: f32,
}

impl TableHeaderElement {
    pub fn new(element: &Box<dyn Element>) -> anyhow::Result<TableHeaderElement> {
        Ok(TableHeaderElement {
            element: element.clone(),
            width: 20.0,
        })
    }
}

impl Element for TableHeaderElement {
    fn as_any(&self) -> &dyn Any {
        self
    }

    fn as_any_mut(&mut self) -> &mut dyn Any {
        self
    }

    fn element_type(&self) -> ElementType {
        ElementType::TableHeader
    }
}

#[derive(Clone, Debug)]
pub struct TableRowElement {
    pub cells: Vec<TableCellElement>,
}

impl TableRowElement {
    pub fn new(cells: &Vec<TableCellElement>) -> anyhow::Result<TableRowElement> {
        Ok(TableRowElement {
            cells: cells.to_vec(),
        })
    }
}

impl Element for TableRowElement {
    fn as_any(&self) -> &dyn Any {
        self
    }

    fn as_any_mut(&mut self) -> &mut dyn Any {
        self
    }

    fn element_type(&self) -> ElementType {
        ElementType::TableRow
    }
}

#[derive(Clone, Debug)]
pub struct TableCellElement {
    pub element: Box<dyn Element>,
}

impl TableCellElement {
    pub fn new(element: &Box<dyn Element>) -> anyhow::Result<TableCellElement> {
        Ok(TableCellElement {
            element: element.clone(),
        })
    }
}

impl Element for TableCellElement {
    fn as_any(&self) -> &dyn Any {
        self
    }

    fn as_any_mut(&mut self) -> &mut dyn Any {
        self
    }

    fn element_type(&self) -> ElementType {
        ElementType::TableCell
    }
}

#[derive(Clone, Debug)]
pub struct ListElement {
    pub elements: Vec<ListItemElement>,
    pub numbered: bool,
}

impl ListElement {
    pub fn new(
        elements: &Vec<ListItemElement>,
        numbered: bool,
    ) -> anyhow::Result<Box<dyn Element>> {
        Ok(Box::new(ListElement {
            elements: elements.to_vec(),
            numbered,
        }))
    }
}

impl Element for ListElement {
    fn as_any(&self) -> &dyn Any {
        self
    }

    fn as_any_mut(&mut self) -> &mut dyn Any {
        self
    }

    fn element_type(&self) -> ElementType {
        ElementType::List
    }
}

#[derive(Clone, Debug)]
pub struct ListItemElement {
    pub element: Box<dyn Element>,
}

impl ListItemElement {
    pub fn new(element: &Box<dyn Element>) -> anyhow::Result<ListItemElement> {
        Ok(ListItemElement {
            element: element.clone(),
        })
    }
}

impl Element for ListItemElement {
    fn as_any(&self) -> &dyn Any {
        self
    }

    fn as_any_mut(&mut self) -> &mut dyn Any {
        self
    }

    fn element_type(&self) -> ElementType {
        ElementType::ListItem
    }
}

#[derive(Clone, Debug)]
pub enum ImageType {
    Png,
    Jpeg,
}

#[derive(Clone, Debug)]
pub struct ImageElement {
    pub bytes: Bytes,
    pub title: String,
    pub alt: String,
    pub image_type: ImageType,
}

impl ImageElement {
    pub fn new(
        bytes: &Bytes,
        title: &str,
        alt: &str,
        image_type: ImageType,
    ) -> anyhow::Result<Box<dyn Element>> {
        Ok(Box::new(ImageElement {
            bytes: bytes.clone(),
            title: title.to_string(),
            alt: alt.to_string(),
            image_type,
        }))
    }
}

impl Element for ImageElement {
    fn as_any(&self) -> &dyn Any {
        self
    }

    fn as_any_mut(&mut self) -> &mut dyn Any {
        self
    }

    fn element_type(&self) -> ElementType {
        ElementType::Image
    }
}

#[derive(Clone, Debug)]
pub struct HyperlinkElement {
    pub title: String,
    pub url: String,
    pub alt: String,
}

impl HyperlinkElement {
    pub fn new(title: &str, url: &str, alt: &str) -> anyhow::Result<Box<dyn Element>> {
        Ok(Box::new(HyperlinkElement {
            title: title.to_string(),
            url: url.to_string(),
            alt: alt.to_string(),
        }))
    }
}

impl Element for HyperlinkElement {
    fn as_any(&self) -> &dyn Any {
        self
    }

    fn as_any_mut(&mut self) -> &mut dyn Any {
        self
    }

    fn element_type(&self) -> ElementType {
        ElementType::Hyperlink
    }
}

======

File path: ./src/lib.rs
//!
#![doc = include_str!("../README.md")]
//!

use std::collections::HashMap;
use bytes::Bytes;
use serde_yaml::Value as YValue;
use serde_json::Value as JValue;
use shiva::core::{Document, Element, HeaderElement, ParagraphElement, TableCellElement, TableElement, TableHeaderElement, TableRowElement, TextElement};

use thiserror::Error;
use crate::ReportError::Common;


pub struct Report;


impl Report {

    pub fn generate(template: &str, data: &str,  _images: &HashMap<String, Bytes>) -> anyhow::Result<Document> {
        let template: YValue = serde_yaml::from_str(template)?;
        let data: JValue = serde_json::from_str(data)?;
        let params_src = data["params"].as_object().ok_or(Common("Missing 'params' in data".to_string()))?;
        let params: HashMap<String, String> = params_src.iter().map(|(k, v)| {
            let value:String = if v.is_number() {
                v.to_string()
            } else {
                v.as_str().unwrap_or("").to_string()
            };
            (k.clone(), value)
        }).collect();
        let mut elements: Vec<Box<dyn Element>> = vec![];
        if let Some(title) = template["title"].as_sequence() {
            for header in title {
                if let Some(header_text) = header["header"].as_str() {
                    let mut resolved_text = header_text.to_string();
                    for (key, value) in &params {
                        resolved_text = resolved_text.replace(&format!("$P{{{}}}", key), &value);
                    }
                    let header_element = HeaderElement::new(&resolved_text, header["level"].as_u64().unwrap_or(1) as u8)?;
                    elements.push(header_element);
                }
            }
        }



        // Process table, if present in template
        if let Some(columns) = template["column_header"].as_sequence() {
            let mut headers = Vec::new();
            for column in columns {
                if let Some(name) = column["name"].as_str() {
                    let text_element = TextElement::new(name, 8)?; // Assuming default font size for headers
                    let width = column["width"].as_f64().unwrap_or(20.0) as f32; // Default width if not specified
                    let mut header_element = TableHeaderElement::new(&text_element)?;
                    header_element.width = width;
                    headers.push(header_element);
                }
            }

            let mut rows = Vec::new();
            if let Some(data_rows) = data["rows"].as_array() {
                for data_row in data_rows {
                    let mut cells = Vec::new();
                    // Use the `row` section from the YAML template instead of directly using column names
                    if let Some(row_configs) = template["row"].as_sequence() {
                        for row_config in row_configs {
                            if let Some(value_key) = row_config["value"].as_str() {
                                // Determine the field name to extract from the JSON data row based on the template configuration
                                let field_name = value_key.trim_start_matches("$F(").trim_end_matches(")");
                                if let Some(value) = data_row[field_name].as_str() {
                                    let text_element = TextElement::new(value, 8)?; // Default font size for cells
                                    cells.push(TableCellElement::new(&text_element)?);
                                }
                                if let Some(value) = data_row[field_name].as_number() {
                                    let value = value.to_string();
                                    let text_element = TextElement::new(value.as_str(), 8)?; // Default font size for cells
                                    cells.push(TableCellElement::new(&text_element)?);
                                }
                            }
                        }
                    }
                    rows.push(TableRowElement::new(&cells)?);
                }
            }


            if let Some(footer_configs) = template["column_footer"].as_sequence() {
                let mut footer_cells = Vec::new();
                for footer_config in footer_configs {
                    if let Some(value_key) = footer_config["value"].as_str() {
                        // Replace placeholders with actual values
                        let mut resolved_text = value_key.to_string();
                        for (key, value) in &params {
                            resolved_text = resolved_text.replace(&format!("$P{{{}}}", key), &value);
                        }

                        // Handling empty values to maintain table structure
                        if resolved_text.is_empty() {
                            resolved_text = " ".to_string(); // Ensure empty cells are accounted for
                        }

                        let text_element = TextElement::new(&resolved_text, 8)?; // Assuming default font size for footer
                        footer_cells.push(TableCellElement::new(&text_element)?);
                    } else {
                        // If there's no value specified, insert a placeholder or space
                        let text_element = TextElement::new(" ", 8)?; // Ensure the table structure remains consistent
                        footer_cells.push(TableCellElement::new(&text_element)?);
                    }
                }

                // Create a footer row and add it to the rows
                if !footer_cells.is_empty() {
                    let footer_row = TableRowElement::new(&footer_cells)?;
                    // Assuming you have a method to add a row to the table, or you can directly append it if `rows` is accessible here
                    rows.push(footer_row);
                }
            }

            let table_element_with_footer = TableElement::new(&headers, &rows)?;

            elements.push(table_element_with_footer);

        }


        if let Some(summary) = template["summary"].as_sequence() {
            for paragraph_config in summary {
                if let Some(paragraph_items) = paragraph_config["paragraph"].as_sequence() {
                    let mut paragraph_elements: Vec<Box<dyn Element>> = vec![];
                    for text_item in paragraph_items {
                        if let Some(text_value) = text_item["text"].as_str() {
                            let mut resolved_text = text_value.to_string();
                            for (key, value) in &params {
                                resolved_text = resolved_text.replace(&format!("$P{{{}}}", key), value);
                            }
                            let text_size = text_item["size"].as_u64().unwrap_or(10) as u8; // Default size if not specified
                            let text_element = TextElement::new(&resolved_text, text_size)?;
                            paragraph_elements.push(text_element);
                        }
                    }
                    if !paragraph_elements.is_empty() {
                        let paragraph_element = ParagraphElement::new(&paragraph_elements)?;
                        elements.push(paragraph_element);
                    }
                }
            }
        }


        let document = Document::new(&elements)?;
        Ok(document)

    }

}

#[derive(Error, Debug)]
pub enum ReportError {
    #[error("Report error: {0}")]
    Common(String),
}


#[cfg(test)]
mod tests {
    use super::*;
    use std::collections::HashMap;
    use shiva::core::TransformerTrait;

    #[test]
    fn test_generate() -> anyhow::Result<()> {
        let template_vec = std::fs::read("data/report-template.yaml")?;
        let template = std::str::from_utf8(&template_vec).unwrap();
        let data_vec = std::fs::read("data/report-data.json")?;
        let data = std::str::from_utf8(&data_vec).unwrap();
        let images = HashMap::new();
        let result = Report::generate(template, data, &images);
        assert!(result.is_ok());
        let doc = result?;
        println!("{:?}", doc);
        println!("=========================");
        let result = shiva::markdown::Transformer::generate(&doc)?;
        let result_str = std::str::from_utf8(&result.0)?;
        println!("{}", result_str);
        Ok(())
    }
}

======

File path: ./src/markdown.rs
use crate::core::*;
use bytes::Bytes;
use regex::Regex;
use std::collections::HashMap;

pub struct Transformer;
impl TransformerTrait for Transformer {
    fn parse(document: &Bytes, images: &HashMap<String, Bytes>) -> anyhow::Result<Document>
    where
        Self: Sized,
    {
        let document_str = std::str::from_utf8(document)?;
        let mut elements: Vec<Box<dyn Element>> = Vec::new();
        let lines = document_str.lines();
        let mut current_paragraph_elements: Vec<Box<dyn Element>> = Vec::new();
        let mut in_table = false;
        let mut table_rows: Vec<TableRowElement> = Vec::new();
        let mut table_headers: Vec<TableHeaderElement> = Vec::new();
        let mut current_list_stack: Vec<Vec<ListItemElement>> = Vec::new();
        let mut current_list_type_stack: Vec<bool> = Vec::new();
        let img_regex = Regex::new("!\\[.*?\\]\\(.*? \".*?\"\\)").unwrap();
        for line in lines {
            let indent_level = line.chars().take_while(|c| c.is_whitespace()).count() / 2;
            let trimmed_line = line.trim_start();
            if trimmed_line.starts_with('-')
                || trimmed_line.starts_with('+')
                || trimmed_line.starts_with('*')
            {
                let list_item_text = trimmed_line[1..].trim();
                let list_item =
                    ListItemElement::new(&TextElement::new(list_item_text, 8)?).unwrap();
                if current_list_stack.len() <= indent_level {
                    while current_list_stack.len() <= indent_level {
                        current_list_stack.push(vec![]);
                        current_list_type_stack.push(false);
                    }
                } else {
                    while current_list_stack.len() > indent_level + 1 {
                        let nested_items = current_list_stack.pop().unwrap();
                        let nested_list = ListElement::new(
                            &nested_items,
                            current_list_type_stack.pop().unwrap(),
                        )?;
                        if let Some(parent_list) = current_list_stack.last_mut() {
                            parent_list.push(ListItemElement::new(&nested_list)?);
                        }
                    }
                }
                current_list_stack[indent_level].push(list_item);
                while current_list_stack.len() > indent_level + 1 {
                    let nested_items = current_list_stack.pop().unwrap();
                    let nested_list =
                        ListElement::new(&nested_items, current_list_type_stack.pop().unwrap())?;
                    if let Some(parent_list) = current_list_stack.last_mut() {
                        parent_list.push(ListItemElement::new(&nested_list)?);
                    }
                }
            } else if trimmed_line.chars().next().unwrap_or(' ').is_digit(10)
                && trimmed_line.chars().nth(1).unwrap_or(' ') == '.'
            {
                let list_item_text = trimmed_line.splitn(2, '.').nth(1).unwrap_or("").trim();
                let list_item =
                    ListItemElement::new(&TextElement::new(list_item_text, 8)?).unwrap();
                if current_list_stack.len() <= indent_level {
                    while current_list_stack.len() <= indent_level {
                        current_list_stack.push(vec![]);
                        current_list_type_stack
                            .push(trimmed_line.chars().next().unwrap().is_digit(10));
                    }
                } else {
                    while current_list_stack.len() > indent_level + 1 {
                        let nested_items = current_list_stack.pop().unwrap();
                        let nested_list = ListElement::new(
                            &nested_items,
                            current_list_type_stack.pop().unwrap(),
                        )?;
                        if let Some(parent_list) = current_list_stack.last_mut() {
                            parent_list.push(ListItemElement::new(&nested_list)?);
                        }
                    }
                }
                current_list_stack[indent_level].push(list_item);
                while current_list_stack.len() > indent_level + 1 {
                    let nested_items = current_list_stack.pop().unwrap();
                    let nested_list =
                        ListElement::new(&nested_items, current_list_type_stack.pop().unwrap())?;
                    if let Some(parent_list) = current_list_stack.last_mut() {
                        parent_list.push(ListItemElement::new(&nested_list)?);
                    }
                }
            } else {
                if line.starts_with('|') && line.ends_with('|') {
                    if !in_table {
                        // Start a new table
                        in_table = true;
                        table_headers = line
                            .split('|')
                            .filter(|x| !x.trim().is_empty() && !x.contains('-'))
                            .map(|header| {
                                TableHeaderElement::new(
                                    &TextElement::new(header.trim(), 8).unwrap(),
                                )
                                .unwrap()
                            })
                            .collect();
                    } else if line.contains("---") {
                        continue;
                    } else if line.starts_with('|') && in_table {
                        let cells = line
                            .split('|')
                            .filter(|x| !x.trim().is_empty())
                            .map(|cell| {
                                TableCellElement::new(&TextElement::new(cell.trim(), 8).unwrap())
                                    .unwrap()
                            })
                            .collect();
                        table_rows.push(TableRowElement::new(&cells).unwrap());
                    }
                } else {
                    if in_table {
                        elements.push(TableElement::new(&table_headers, &table_rows)?);
                        table_rows.clear();
                        table_headers.clear();
                        in_table = false;
                    }

                    if line.starts_with('#') {
                        let level = line.chars().take_while(|&c| c == '#').count() as u8;
                        let text = line.trim_start_matches('#').trim();
                        let header = HeaderElement::new(text, level)?;
                        if !current_paragraph_elements.is_empty() {
                            elements.push(ParagraphElement::new(&current_paragraph_elements)?);
                            current_paragraph_elements.clear();
                        }
                        elements.push(header);
                    } else if !line.trim().is_empty() {
                        fn parser_text(
                            text: &str,
                            current_paragraph_elements: &mut Vec<Box<dyn Element>>,
                        ) -> anyhow::Result<()> {
                            let mut start = 0;
                            let mut captures = 0;
                            let hyperlink_regex = Regex::new("(?:\\[([^\\]]+)\\])?(?:\\(|)(http[s]?:\\/\\/[^\\s\\)]+)(?:\\s\"([^\"]+)\")?\\)?").unwrap();
                            for cap in hyperlink_regex.captures_iter(text) {
                                captures += 1;
                                let (hyperlink_start, end) =
                                    (cap.get(0).unwrap().start(), cap.get(0).unwrap().end());
                                let markdown = &text[hyperlink_start..end];
                                if hyperlink_start > start {
                                    parser_text(
                                        &text[start..hyperlink_start],
                                        current_paragraph_elements,
                                    )?;
                                }
                                if markdown.starts_with("h") {
                                    current_paragraph_elements
                                        .push(HyperlinkElement::new(markdown, markdown, markdown)?);
                                } else if markdown.starts_with("[") && markdown.ends_with("\")") {
                                    let start_alt_text = markdown.find("[").unwrap() + 1;
                                    let end_alt_text = markdown.find("]").unwrap();
                                    let start_url_path = markdown.find("(").unwrap() + 1;
                                    let end_url_path = markdown.find(" ").unwrap();
                                    let start_title = markdown.find("\"").unwrap() + 1;
                                    let end_title = markdown.rfind("\"").unwrap();
                                    let alt_text = &markdown[start_alt_text..end_alt_text];
                                    let url = &markdown[start_url_path..end_url_path];
                                    let title = &markdown[start_title..end_title];
                                    current_paragraph_elements
                                        .push(HyperlinkElement::new(alt_text, url, title)?);
                                } else {
                                    let start_alt_text = markdown.find("[").unwrap() + 1;
                                    let end_alt_text = markdown.find("]").unwrap();
                                    let start_url_path = markdown.find("(").unwrap() + 1;
                                    let alt_text = &markdown[start_alt_text..end_alt_text];
                                    let url = &markdown[start_url_path..markdown.len() - 1];
                                    current_paragraph_elements
                                        .push(HyperlinkElement::new(alt_text, url, alt_text)?);
                                }
                                start = end;
                            }
                            if captures == 0 {
                                current_paragraph_elements.push(TextElement::new(text, 8)?);
                            } else {
                                if start < text.len() {
                                    parser_text(&text[start..], current_paragraph_elements)?;
                                }
                            }
                            Ok(())
                        }

                        let mut captures = 0;
                        let mut start = 0;

                        for cap in img_regex.captures_iter(line) {
                            captures += 1;
                            let (img_start, img_end) =
                                (cap.get(0).unwrap().start(), cap.get(0).unwrap().end());
                            let markdown = &line[img_start..img_end];
                            let start_alt_text = markdown.find("[").unwrap() + 1;
                            let end_alt_text = markdown.find("]").unwrap();
                            let start_file_path = markdown.find("(").unwrap() + 1;
                            let start_title = markdown.find("\"").unwrap() + 1;
                            let end_title = markdown.rfind("\"").unwrap();
                            let alt_text = &markdown[start_alt_text..end_alt_text];
                            let file_path = &markdown[start_file_path..end_title - 1];
                            let title = &markdown[start_title..end_title];

                            if img_start > start {
                                parser_text(
                                    &line[start..img_start],
                                    &mut current_paragraph_elements,
                                )?;
                            }
                            let image_empty = Bytes::new();
                            let image_bytes = images.get(file_path).map_or(&image_empty, |x| x);
                            current_paragraph_elements.push(ImageElement::new(
                                image_bytes,
                                &title,
                                &alt_text,
                                ImageType::Png,
                            )?);

                            start = img_end;
                        }

                        if captures == 0 {
                            parser_text(&line, &mut current_paragraph_elements)?;
                        } else {
                            if start < line.len() {
                                parser_text(&line[start..], &mut current_paragraph_elements)?;
                            }
                        }
                    } else if !current_paragraph_elements.is_empty() {
                        elements.push(ParagraphElement::new(&current_paragraph_elements)?);
                        current_paragraph_elements.clear();
                    }
                }
                while !current_list_stack.is_empty() {
                    let items = current_list_stack.pop().unwrap();
                    let list = ListElement::new(&items, current_list_type_stack.pop().unwrap())?;
                    if let Some(parent_list) = current_list_stack.last_mut() {
                        parent_list.push(ListItemElement::new(&list)?);
                    } else {
                        elements.push(list);
                    }
                }
            }
        }
        while !current_list_stack.is_empty() {
            let items = current_list_stack.pop().unwrap();
            let list = ListElement::new(&items, current_list_type_stack.pop().unwrap())?;
            if let Some(parent_list) = current_list_stack.last_mut() {
                parent_list.push(ListItemElement::new(&list)?);
            } else {
                elements.push(list);
            }
        }

        if !current_paragraph_elements.is_empty() {
            elements.push(ParagraphElement::new(&current_paragraph_elements)?);
        }

        if in_table {
            elements.push(TableElement::new(&table_headers, &table_rows)?);
        }

        Ok(Document::new(&elements)?)
    }

    fn generate(document: &Document) -> anyhow::Result<(Bytes, HashMap<String, Bytes>)>
    where
        Self: Sized,
    {
        let mut images: HashMap<String, Bytes> = HashMap::new();
        let mut image_num: i32 = 0;

        let mut markdown = String::new();
        fn generate_element(
            element: &Box<dyn Element>,
            markdown: &mut String,
            list_depth: usize,
            list_counters: &mut Vec<usize>,
            list_types: &mut Vec<bool>,
            images: &mut HashMap<String, Bytes>,
            image_num: &mut i32,
        ) -> anyhow::Result<()> {
            fn generate_list_item(
                element: &ListItemElement,
                markdown: &mut String,
                list_depth: usize,
                list_counters: &mut Vec<usize>,
                list_types: &mut Vec<bool>,
                images: &mut HashMap<String, Bytes>,
                image_num: &mut i32,
            ) -> anyhow::Result<()> {
                let prefix = if *list_types.last().unwrap() {
                    let counter = list_counters.last_mut().unwrap();
                    if &element.element.element_type() == &ElementType::Text {
                        *counter += 1;
                    }
                    format!("{}. ", counter)
                } else {
                    "- ".to_string()
                };
                // println!("list depth: {}", list_depth);
                markdown.push_str(&"  ".repeat(list_depth - 1));
                if &element.element.element_type() == &ElementType::Text {
                    markdown.push_str(&prefix);
                }
                generate_element(
                    &element.element,
                    markdown,
                    list_depth,
                    list_counters,
                    list_types,
                    images,
                    image_num,
                )?;
                if &element.element.element_type() == &ElementType::Text {
                    markdown.push('\n');
                }
                Ok(())
            }

            match element.element_type() {
                ElementType::Header => {
                    let header = element.header_as_ref()?;
                    markdown.push_str(&"#".repeat(header.level as usize));
                    markdown.push(' ');
                    markdown.push_str(&header.text);
                    markdown.push('\n');
                    markdown.push('\n');
                }
                ElementType::Paragraph => {
                    let paragraph = element.paragraph_as_ref()?;
                    for child in &paragraph.elements {
                        generate_element(
                            child,
                            markdown,
                            list_depth,
                            list_counters,
                            list_types,
                            images,
                            image_num,
                        )?;
                    }
                    markdown.push('\n');
                    markdown.push('\n');
                }
                ElementType::List => {
                    let list = element.list_as_ref()?;
                    // println!("{:?}", list);

                    list_counters.push(0);
                    list_types.push(list.numbered);
                    for item in &list.elements {
                        generate_list_item(
                            &item,
                            markdown,
                            list_depth + 1,
                            list_counters,
                            list_types,
                            images,
                            image_num,
                        )?;
                    }
                    list_counters.pop();
                    list_types.pop();

                    if list_counters.len() == 0 {
                        markdown.push('\n');
                    }
                }
                ElementType::Text => {
                    let text = element.text_as_ref()?;
                    markdown.push_str(&text.text);
                    if !text.text.ends_with(" ") {
                        markdown.push_str(" ");
                    }
                }
                ElementType::Hyperlink => {
                    let hyperlink = element.hyperlink_as_ref()?;
                    if hyperlink.url == hyperlink.alt && hyperlink.alt == hyperlink.url {
                        markdown.push_str(&format!("{}", hyperlink.url));
                    } else {
                        markdown.push_str(&format!(
                            "[{}]({} \"{}\")",
                            hyperlink.title, hyperlink.url, hyperlink.alt
                        ));
                    }
                }
                ElementType::Image => {
                    let image = element.image_as_ref()?;
                    let image_path = format!("image{}.png", image_num);
                    markdown.push_str(&format!(
                        "![{}]({} \"{}\")",
                        image.alt, image_path, image.title
                    ));
                    images.insert(image_path.to_string(), image.bytes.clone());
                    *image_num += 1;
                }
                ElementType::Table => {
                    let table = element.table_as_ref()?;

                    let mut max_lengths: Vec<usize> = Vec::new();

                    for header in &table.headers {
                        let header_text = header.element.text_as_ref()?;
                        max_lengths.push(header_text.text.len());
                    }
                    for row in &table.rows {
                        for (cell_index, cell) in row.cells.iter().enumerate() {
                            let cell_text = cell.element.text_as_ref()?;
                            if cell_index < max_lengths.len() {
                                max_lengths[cell_index] =
                                    max_lengths[cell_index].max(cell_text.text.len());
                            }
                        }
                    }

                    for (index, header) in table.headers.iter().enumerate() {
                        let header_text = header.element.text_as_ref()?;
                        let padding = max_lengths[index] - header_text.text.len();
                        markdown.push_str("| ");
                        markdown.push_str(&header_text.text);
                        markdown.push_str(&" ".repeat(padding));
                        markdown.push(' ');
                    }
                    markdown.push_str("|\n");

                    for max_length in &max_lengths {
                        markdown.push_str("|");
                        markdown.push_str(&"-".repeat(*max_length + 2));
                    }
                    markdown.push_str("|\n");

                    for row in &table.rows {
                        for (cell_index, cell) in row.cells.iter().enumerate() {
                            let cell_text = cell.element.text_as_ref()?;
                            let padding = max_lengths[cell_index] - cell_text.text.len();
                            markdown.push_str("| ");
                            markdown.push_str(&cell_text.text);
                            markdown.push_str(&" ".repeat(padding));
                            markdown.push(' ');
                        }
                        markdown.push_str("|\n");
                    }
                    markdown.push('\n');
                }
                _ => {}
            }
            Ok(())
        }

        let mut list_counters: Vec<usize> = Vec::new();
        let mut list_types: Vec<bool> = Vec::new();
        let all_elements: Vec<Box<dyn Element>> = document.page_header.iter().cloned().chain(document.elements.iter().cloned()).chain(document.page_footer.iter().cloned()).collect();
        for element in &all_elements {
            generate_element(
                element,
                &mut markdown,
                0,
                &mut list_counters,
                &mut list_types,
                &mut images,
                &mut image_num,
            )?;
        }

        Ok((Bytes::from(markdown), HashMap::new()))
    }
}

#[cfg(test)]
mod tests {
    use crate::core::*;
    use crate::markdown::*;
    use crate::text;

    #[test]
    fn test() -> anyhow::Result<()> {
        let document = r#"# First header

Paragraph  bla bla bla blabla bla bla blabla bla bla blabla bla bla blabla bla bla blabla bla bla blabla bla bla blabla bla bla blabla bla bla
blabla bla bla blabla bla bla blabla bla bla blabla bla bla bla

1. List item 1
2. List item 2
3. List item 3
   1. List item secode level 1
   2. List item secode level 2
4. List item 4
   1. List item secode level 3
   2. List item secode level 4
5. List item 5
   1. List item secode level 5

- List item one
  - List item two
- List item three
  - List item four
  - List item five
    - List item zzz
- List item six
  - List item seven

![Picture alt1](test/data/picture.png "Picture title1")


Bla bla bla ![Picture alt2](test/data/picture.png "Picture title2") bla. http://example.com  [Example](http://example.com) [Example](http://example.com "Example tooltip")


## Second header

| Syntax      | Description |
| ----------- | ----------- |
| Header      | Title       |
| Paragraph   | Text        |

Paragraph2  bla bla bla blabla bla bla blabla bla bla blabla bla bla blabla bla bla blabla bla bla blabla bla bla blabla bla bla blabla bla bla
blabla2 bla bla blabla bla bla blabla bla bla blabla bla bla bla"#;
        // println!("{:?}", document);
        let mut images = HashMap::new();
        let image_bytes = std::fs::read("test/data/picture.png")?;
        images.insert("test/data/picture.png".to_string(), image_bytes);
        let parsed = Transformer::parse(&document.as_bytes().into(), &HashMap::new());
        let document_string = std::str::from_utf8(document.as_bytes())?;
        println!("{}", document_string);
        assert!(parsed.is_ok());
        let parsed_document = parsed.unwrap();
        println!("==========================");
        println!("{:?}", parsed_document);
        println!("==========================");
        let generated_result = Transformer::generate(&parsed_document);
        assert!(generated_result.is_ok());
        // println!("{:?}", generated_result.unwrap());
        let generated_bytes = generated_result?;
        let generated_text = std::str::from_utf8(&generated_bytes.0)?;
        println!("{}", generated_text);
        println!("==========================");
        let generated_result = text::Transformer::generate(&parsed_document);
        assert!(generated_result.is_ok());
        // println!("{:?}", generated_result.unwrap());
        let generated_bytes = generated_result?;
        let generated_text = std::str::from_utf8(&generated_bytes.0)?;
        println!("{}", generated_text);

        Ok(())
    }
}
