use brisk_it::{
    generator::{Generator, Result},
    Either,
};

#[derive(Default)]
pub(super) struct App {}

impl Generator for App {
    fn generate(
        &self,
        input: brisk_it::component::ComponentInput,
        manager: &brisk_it::generator::Manager,
    ) -> Result<proc_macro2::TokenStream> {
        let mut functions = Vec::<proc_macro2::TokenStream>::default();
        let mut content = Vec::<proc_macro2::TokenStream>::default();
        let mut target: Option<syn::Expr> = None;

        for prop in input.properties.into_iter() {
            if prop.name == "target" {
                target = Some(prop.expr)
            } else if prop.name == "save" {
                let expr = prop.expr;
                match expr {
                    syn::Expr::Lit(syn::ExprLit {
                        attrs: _,
                        lit:
                            syn::Lit::Bool(syn::LitBool {
                                span: _,
                                value: true,
                            }),
                    }) => {
                        functions.push(quote::quote! {
                            fn save(&mut self, storage: &mut dyn eframe::Storage) {
                                eframe::set_value(storage, eframe::APP_KEY, self);;
                            }
                        });
                    }
                    _ => {
                        functions.push(quote::quote! {
                            fn save(&mut self, storage: &mut dyn eframe::Storage) {
                                (#expr)(storage);
                            }
                        });
                    }
                }
            } else if prop.name == "clear_color" {
                let expr = prop.expr;
                functions.push(quote::quote! {
                    fn clear_color(&self, visuals: &egui::Visuals) -> [f32; 4]
                    {
                        (#expr)(visuals)
                    }
                });
            } else {
                return Err(brisk_it::errors::unknown_property(prop));
            }
        }

        for child in input.children.into_iter() {
            content.push(manager.generate(child).either());
        }

        Ok(quote::quote! {
            impl eframe::App for #target
            {
                #(#functions)*
                fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) {
                    let ctx = ui.ctx().clone();
                    #(#content)*
                }
            }
        })
    }
    fn boxed_clone(&self) -> Box<dyn Generator> {
        Box::new(Self {})
    }
}
