use std::sync::Arc;

use liblumen_alloc::erts::exception::system::Alloc;
use liblumen_alloc::erts::process::code::stack::frame::{Frame, Placement};
use liblumen_alloc::erts::process::{code, Process};
use liblumen_alloc::erts::term::{atom_unchecked, Term};
use liblumen_alloc::ModuleFunctionArity;

use super::label_5;

pub fn place_frame_with_arguments(
    process: &Process,
    placement: Placement,
    document: Term,
    parent: Term,
    reference_child: Term,
) -> Result<(), Alloc> {
    process.stack_push(reference_child)?;
    process.stack_push(parent)?;
    process.stack_push(document)?;
    process.place_frame(frame(), placement);

    Ok(())
}

// Private

// ```elixir
// # label 4
// # pushed to stack: (document, parent, reference_child)
// # returned form call: :ok
// # full stack: (:ok, document, parent, reference_child)
// # returns: :ok
// :ok = Lumen.Web.Node.append_child(parent, reference_child)
// {:ok, new_child} = Lumen.Web.Document.create_element(document, "ul");
// {:ok, inserted_child} = Lumen.Web.insert_before(parent, new_child, reference_child)
// ```
fn code(arc_process: &Arc<Process>) -> code::Result {
    arc_process.reduce();

    let ok = arc_process.stack_pop().unwrap();
    assert_eq!(ok, atom_unchecked("ok"));

    let document = arc_process.stack_pop().unwrap();
    assert!(document.is_resource_reference());

    let parent = arc_process.stack_pop().unwrap();
    assert!(parent.is_resource_reference());

    let reference_child = arc_process.stack_pop().unwrap();
    assert!(reference_child.is_resource_reference());

    label_5::place_frame_with_arguments(
        arc_process,
        Placement::Replace,
        document,
        parent,
        reference_child,
    )?;

    lumen_web::node::append_child_2::place_frame_with_arguments(
        arc_process,
        Placement::Push,
        parent,
        reference_child,
    )?;

    Process::call_code(arc_process)
}

fn frame() -> Frame {
    let module_function_arity = Arc::new(ModuleFunctionArity {
        module: super::module(),
        function: super::function(),
        arity: 0,
    });

    Frame::new(module_function_arity, code)
}
