use std::{
    fs::File,
    sync::{Arc, Mutex},
};

use mlua::prelude::*;

use crate::renderer::{
    prelude::Config,
    types::{
        Animation, Element, Task, animation, circle_element, color, config, easing, line_element,
        point, rectangle_element, scene, size,
    },
};

use super::lua_env::LuaEnv;

// #[derive(Copy, Clone)]
pub struct LuaDecoder {
    // config: Table,
    tasks: Vec<Task>,
}

// impl LuaRenderer {
//     fn new(lua: &Lua) -> Result<Self, LuaError> {
//         Ok(Self {
//             config: Config::default().to_table(lua)?,
//             blocks: lua.create_table()?,
//         })
//     }
// }

impl Default for LuaDecoder {
    fn default() -> Self {
        Self { tasks: Vec::new() }
    }
}

impl LuaDecoder {
    pub fn run(file: &File) -> crate::Result<(Vec<Task>, Config)> {
        let lua = LuaEnv::new();

        let renderer = Arc::new(Mutex::new(Self::default()));

        circle_element::add_constructor(&lua)?;
        line_element::add_constructor(&lua)?;
        rectangle_element::add_constructor(&lua)?;
        animation::add_constructor(&lua)?;
        color::add_constructor(&lua)?;
        easing::add_constructor(&lua)?;
        point::add_constructor(&lua)?;
        size::add_constructor(&lua)?;

        lua.lua.globals().set("config", Config::default())?;

        Self::add_func(&lua, &renderer, "layer", Self::move_to_layer)?;
        Self::add_func(&lua, &renderer, "draw", Self::draw)?;
        Self::add_func(&lua, &renderer, "animate", Self::animate)?;
        Self::add_func(&lua, &renderer, "render", Self::render)?;

        lua.exec(std::io::read_to_string(file)?)?;

        Ok((
            renderer.lock().unwrap().tasks.clone(),
            lua.lua.globals().get("config")?,
        ))
    }

    fn add_func<F, A, R>(
        lua: &LuaEnv,
        this: &Arc<Mutex<Self>>,
        name: &str,
        func: F,
    ) -> crate::Result<()>
    where
        F: Fn(&Arc<Mutex<Self>>, A) -> R + 'static,
        A: FromLuaMulti,
        R: IntoLuaMulti,
    {
        let renderer = Arc::clone(this);

        lua.func(name, move |_, args| Ok(func(&renderer, args)))?;

        Ok(())
    }

    fn move_to_layer(this: &Arc<Mutex<Self>>, layer: usize) {
        this.lock().unwrap().tasks.push(Task::MoveToLayer(layer));
    }

    fn draw(this: &Arc<Mutex<Self>>, element: Element) {
        this.lock().unwrap().tasks.push(Task::Draw(element));
    }

    fn animate(this: &Arc<Mutex<Self>>, animation: Animation) {
        this.lock().unwrap().tasks.push(Task::Animate(animation));
    }

    fn render(this: &Arc<Mutex<Self>>, duration: f64) {
        this.lock().unwrap().tasks.push(Task::RenderScene(duration));
    }
}
