use cdoc_parser::code_ast::types::CodeContent;
use cdoc_parser::document::CodeOutput;
use cowstr::CowStr;

use cdoc_parser::Span;

use rhai::serde::{from_dynamic, to_dynamic};
use rhai::{CustomType, Dynamic, TypeBuilder};

#[derive(Clone)]
pub(crate) struct ScriptCodeBlock {
    source: CodeContent,
    attributes: Vec<CowStr>,
    outputs: Dynamic,
    display_cell: bool,
    global_idx: usize,
    #[allow(unused)]
    span: Span,
}

impl ScriptCodeBlock {
    pub fn new(
        source: &CodeContent,
        attributes: &[CowStr],
        outputs: &Option<&mut CodeOutput>,
        display_cell: bool,
        global_idx: usize,
        pos: &Span,
    ) -> Self {
        ScriptCodeBlock {
            source: source.clone(),
            attributes: attributes.to_vec(),
            outputs: to_dynamic(outputs).unwrap(),
            display_cell,
            global_idx,
            span: pos.clone(),
        }
    }

    pub fn apply_changes(
        self,
        source: &mut CodeContent,
        tags: &mut Vec<CowStr>,
        outputs: Option<&mut CodeOutput>,
        display_cell: &mut bool,
        global_idx: &mut usize,
    ) -> anyhow::Result<()> {
        *source = self.source;

        *tags = self.attributes;
        *display_cell = self.display_cell;
        *global_idx = self.global_idx;

        if let Some(out) = outputs {
            *out = from_dynamic(&self.outputs)?;
        }

        Ok(())
    }
}

impl CustomType for ScriptCodeBlock {
    fn build(mut builder: TypeBuilder<Self>) {
        builder
            .with_name("CodeBlock")
            .with_get_set(
                "source",
                |s: &mut Self| s.source.clone(),
                |s: &mut Self, v: CodeContent| s.source = v,
            )
            .with_get_set(
                "tags",
                |s: &mut Self| s.attributes.clone(),
                |s: &mut Self, v: Vec<CowStr>| s.attributes = v,
            )
            .with_get_set(
                "outputs",
                |s: &mut Self| s.outputs.clone(),
                |s: &mut Self, v: Dynamic| s.outputs = v,
            )
            .with_get_set(
                "display_cell",
                |s: &mut Self| s.display_cell,
                |s: &mut Self, v: bool| s.display_cell = v,
            )
            .with_get_set(
                "global_idx",
                |s: &mut Self| s.global_idx,
                |s: &mut Self, v: usize| s.global_idx = v,
            );
    }
}
