mod callbacks;
mod gut;
mod html;
mod markdown;
mod resolve;
use crate::documentation::{Documentation, GdnativeClass, Method, Property};
use pulldown_cmark::{Alignment, CowStr, Event, LinkType, Options as MarkdownOptions, Parser, Tag};
pub(super) use gut::GutCallbacks;
pub(super) use html::HtmlCallbacks;
pub(super) use markdown::MarkdownCallbacks;
pub use callbacks::Callbacks;
pub use resolve::Resolver;
macro_rules! broken_link_callback {
    ($resolver:expr) => {
        move |broken_link: ::pulldown_cmark::BrokenLink| {
            use ::pulldown_cmark::CowStr;
            let mut link = broken_link.reference;
            if link.starts_with('`') && link.ends_with('`') && link.len() > 1 {
                link = &link[1..link.len() - 1];
            }
            $resolver
                .resolve(link)
                .map(|string| (CowStr::from(string), CowStr::Borrowed("")))
        }
    };
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum BuiltinBackend {
    
    
    
    
    Markdown,
    
    
    
    
    
    
    Html,
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    Gut,
}
#[derive(Debug)]
pub struct Generator<'a> {
    
    pub resolver: &'a Resolver,
    
    pub documentation: &'a Documentation,
    
    pub markdown_options: MarkdownOptions,
    
    
    
    
    pub opening_comment: bool,
}
impl<'a> Generator<'a> {
    pub(crate) fn new(
        resolver: &'a Resolver,
        documentation: &'a Documentation,
        markdown_options: MarkdownOptions,
        opening_comment: bool,
    ) -> Self {
        Self {
            resolver,
            documentation,
            markdown_options,
            opening_comment,
        }
    }
    
    
    
    
    
    
    
    
    
    
    
    
    pub fn generate_root_file(&self, extension: &str, callbacks: &mut dyn Callbacks) -> String {
        let resolver = self.resolver;
        let mut broken_link_callback = broken_link_callback!(resolver);
        let class_iterator = EventIterator {
            context: resolver,
            parser: pulldown_cmark::Parser::new_with_broken_link_callback(
                &self.documentation.root_documentation,
                self.markdown_options,
                Some(&mut broken_link_callback),
            ),
        };
        let mut events: Vec<_> = class_iterator.into_iter().collect();
        events.extend(vec![
            Event::Start(Tag::Heading(1)),
            Event::Text(CowStr::Borrowed("Classes:")),
            Event::End(Tag::Heading(1)),
            Event::Start(Tag::List(None)),
        ]);
        for class_name in self.documentation.classes.keys() {
            let link = Tag::Link(
                LinkType::Inline,
                format!("./{}.{}", class_name, extension).into(),
                CowStr::Borrowed(""),
            );
            events.extend(vec![
                Event::Start(Tag::Item),
                Event::Start(link.clone()),
                Event::Text(CowStr::Borrowed(&class_name)),
                Event::End(link.clone()),
                Event::End(Tag::Item),
            ])
        }
        events.push(Event::End(Tag::List(None)));
        let mut root_file = String::new();
        callbacks.encode(&mut root_file, events);
        root_file
    }
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    pub fn generate_file(
        &self,
        name: &str,
        class: &GdnativeClass,
        callbacks: &mut dyn Callbacks,
    ) -> String {
        let mut class_file = String::new();
        let resolver = &self.resolver;
        let inherit_link = resolver.resolve(&class.inherit);
        
        let mut events = vec![
            Event::Start(Tag::Heading(1)),
            Event::Text(CowStr::Borrowed(&name)),
            Event::End(Tag::Heading(1)),
            Event::Start(Tag::Paragraph),
            Event::Start(Tag::Strong),
            Event::Text(CowStr::Borrowed("Inherit:")),
            Event::End(Tag::Strong),
            Event::Text(CowStr::Borrowed(" ")),
        ];
        if let Some(inherit_link) = inherit_link.as_ref() {
            let link = Tag::Link(
                LinkType::Shortcut,
                CowStr::Borrowed(&inherit_link),
                CowStr::Borrowed(""),
            );
            events.extend(vec![
                Event::Start(link.clone()),
                Event::Text(CowStr::Borrowed(&class.inherit)),
                Event::End(link),
            ])
        } else {
            events.push(Event::Text(CowStr::Borrowed(&class.inherit)))
        }
        events.extend(vec![
            Event::End(Tag::Paragraph),
            Event::Start(Tag::Heading(2)),
            Event::Text(CowStr::Borrowed("Description")),
            Event::End(Tag::Heading(2)),
        ]);
        callbacks.encode(&mut class_file, events);
        
        let mut broken_link_callback = broken_link_callback!(resolver);
        let class_documentation = EventIterator {
            context: resolver,
            parser: pulldown_cmark::Parser::new_with_broken_link_callback(
                &class.documentation,
                self.markdown_options,
                Some(&mut broken_link_callback),
            ),
        }
        .into_iter()
        .collect();
        callbacks.encode(&mut class_file, class_documentation);
        
        if !class.properties.is_empty() {
            callbacks.encode(
                &mut class_file,
                Self::properties_table(&class.properties, resolver),
            )
        }
        
        callbacks.encode(
            &mut class_file,
            Self::methods_table(&class.methods, resolver),
        );
        
        if !class.properties.is_empty() {
            callbacks.encode(
                &mut class_file,
                vec![
                    Event::Start(Tag::Heading(2)),
                    Event::Text(CowStr::Borrowed("Properties Descriptions")),
                    Event::End(Tag::Heading(2)),
                ],
            );
            for property in &class.properties {
                callbacks.start_property(&mut class_file, resolver, property);
                let mut broken_link_callback = broken_link_callback!(resolver);
                let property_documentation = EventIterator {
                    context: resolver,
                    parser: pulldown_cmark::Parser::new_with_broken_link_callback(
                        &property.documentation,
                        self.markdown_options,
                        Some(&mut broken_link_callback),
                    ),
                }
                .into_iter()
                .collect();
                callbacks.encode(&mut class_file, property_documentation);
            }
        }
        
        callbacks.encode(
            &mut class_file,
            vec![
                Event::Start(Tag::Heading(2)),
                Event::Text(CowStr::Borrowed("Methods Descriptions")),
                Event::End(Tag::Heading(2)),
            ],
        );
        for method in &class.methods {
            callbacks.start_method(&mut class_file, resolver, method);
            let mut broken_link_callback = broken_link_callback!(resolver);
            let method_documentation = EventIterator {
                context: resolver,
                parser: pulldown_cmark::Parser::new_with_broken_link_callback(
                    &method.documentation,
                    self.markdown_options,
                    Some(&mut broken_link_callback),
                ),
            }
            .into_iter()
            .collect();
            callbacks.encode(&mut class_file, method_documentation);
        }
        class_file
    }
    
    fn properties_table<'ev>(
        properties: &'ev [Property],
        resolver: &'ev Resolver,
    ) -> Vec<Event<'ev>> {
        let mut events = vec![
            Event::Start(Tag::Heading(2)),
            Event::Text(CowStr::Borrowed("Properties")),
            Event::End(Tag::Heading(2)),
            Event::Start(Tag::Table(vec![Alignment::Left, Alignment::Left])),
            Event::Start(Tag::TableHead),
            Event::Start(Tag::TableCell),
            Event::Text(CowStr::Borrowed("type")),
            Event::End(Tag::TableCell),
            Event::Start(Tag::TableCell),
            Event::Text(CowStr::Borrowed("property")),
            Event::End(Tag::TableCell),
            Event::End(Tag::TableHead),
        ];
        for property in properties {
            let link = Tag::Link(
                LinkType::Reference,
                format!("#property-{}", property.name).into(),
                property.name.as_str().into(),
            );
            events.push(Event::Start(Tag::TableRow));
            events.push(Event::Start(Tag::TableCell));
            events.extend(resolver.encode_type(&property.typ));
            events.extend(vec![
                Event::End(Tag::TableCell),
                Event::Start(Tag::TableCell),
                Event::Start(link.clone()),
                Event::Text(CowStr::Borrowed(property.name.as_str())),
                Event::End(link),
                Event::End(Tag::TableCell),
                Event::End(Tag::TableRow),
            ]);
        }
        events.push(Event::End(Tag::Table(vec![
            Alignment::Left,
            Alignment::Left,
        ])));
        events
    }
    
    fn methods_table<'ev>(methods: &'ev [Method], resolver: &'ev Resolver) -> Vec<Event<'ev>> {
        let mut events = vec![
            Event::Start(Tag::Heading(2)),
            Event::Text(CowStr::Borrowed("Methods")),
            Event::End(Tag::Heading(2)),
            Event::Start(Tag::Table(vec![Alignment::Left, Alignment::Left])),
            Event::Start(Tag::TableHead),
            Event::Start(Tag::TableCell),
            Event::Text(CowStr::Borrowed("returns")),
            Event::End(Tag::TableCell),
            Event::Start(Tag::TableCell),
            Event::Text(CowStr::Borrowed("method")),
            Event::End(Tag::TableCell),
            Event::End(Tag::TableHead),
        ];
        for method in methods {
            let link = format!("#func-{}", method.name);
            events.push(Event::Start(Tag::TableRow));
            events.push(Event::Start(Tag::TableCell));
            events.extend(resolver.encode_type(&method.return_type));
            events.push(Event::End(Tag::TableCell));
            events.push(Event::Start(Tag::TableCell));
            let link = Tag::Link(
                LinkType::Reference,
                link.into(),
                method.name.as_str().into(),
            );
            events.extend(vec![
                Event::Start(link.clone()),
                Event::Text(CowStr::Borrowed(&method.name)),
                Event::End(link),
                Event::Text(CowStr::Borrowed("( ")),
            ]);
            for (index, (name, typ, _)) in method.parameters.iter().enumerate() {
                events.push(Event::Text(format!("{}: ", name).into()));
                events.extend(resolver.encode_type(typ));
                if index + 1 != method.parameters.len() {
                    events.push(Event::Text(CowStr::Borrowed(", ")));
                }
            }
            events.extend(vec![
                Event::Text(CowStr::Borrowed(" )")),
                Event::End(Tag::TableCell),
                Event::End(Tag::TableRow),
            ]);
        }
        events.push(Event::End(Tag::Table(vec![
            Alignment::Left,
            Alignment::Left,
        ])));
        events
    }
}
struct EventIterator<'resolver, 'parser> {
    context: &'resolver Resolver,
    parser: Parser<'parser>,
}
impl<'resolver, 'parser> Iterator for EventIterator<'resolver, 'parser> {
    type Item = Event<'parser>;
    fn next(&mut self) -> Option<Self::Item> {
        let mut next_event = self.parser.next()?;
        next_event = match next_event {
            
            
            Event::Start(Tag::Link(LinkType::ShortcutUnknown, dest, title)) => {
                Event::Start(Tag::Link(LinkType::Shortcut, dest, title))
            }
            Event::End(Tag::Link(LinkType::ShortcutUnknown, dest, title)) => {
                Event::End(Tag::Link(LinkType::Shortcut, dest, title))
            }
            _ => next_event,
        };
        self.context.resolve_event(&mut next_event);
        Some(next_event)
    }
}