/*!
This is an `implementation crate`,
It exports the root module(a struct of function pointers) required by the 
`example_0_interface`(the `interface crate`).
*/

use abi_stable::{
    export_root_module,
    sabi_extern_fn,
    external_types::crossbeam_channel::RSender,
    prefix_type::PrefixTypeTrait,
    sabi_trait::prelude::TU_Opaque,
    std_types::{RStr,RVec, RString,RResult,ROk},
};
use example_interface::{
    AsyncCommand,
    ApplicationMut,
    CommandDescription,
    Error as AppError,
    Plugin,PluginType,PluginId,PluginMod,PluginMod_Ref,
    Plugin_TO,
    utils::process_command,
    WhichPlugin,
};
use core_extensions::SelfOps;
use serde::{Serialize, Deserialize};
use serde_json::value::Value;

///////////////////////////////////////////////////////////////////////////////////


/// Exports the root module of this library.
///
/// This code isn't run until the layout of the type it returns is checked.
#[export_root_module]
fn instantiate_root_module()->PluginMod_Ref{
    PluginMod {
        new,
    }.leak_into_prefix()
}


//////////////////////////////////////////////////////////////////////////////////////


#[sabi_extern_fn]
pub fn new(_sender:RSender<AsyncCommand>,plugin_id:PluginId) -> RResult<PluginType,AppError> {
    let this=CommandUtils{
        plugin_id,
    };
    ROk(Plugin_TO::from_value(this,TU_Opaque))
}


//////////////////////////////////////////////////////////////////////////////////////



#[derive(Debug,Serialize,Deserialize)]
pub enum UtilCmd{
    Repeat{
        how_much:usize,
        plugin:WhichPlugin,
        command:Value,
    },
    Batch{
        plugin:WhichPlugin,
        commands:Vec<Value>,
    },
}

#[derive(Debug,Serialize,Deserialize)]
pub enum ReturnValue{
    Repeat,
    Batch,
}





//////////////////////////////////////////////////////////////////////////////////////


fn run_command_inner(
    this:&mut CommandUtils,
    command:UtilCmd,
    mut app:ApplicationMut<'_>,
)->Result<ReturnValue,AppError>{
    match command {
        UtilCmd::Repeat{how_much,plugin,command}=>{
            let s=serde_json::to_string(&command).unwrap();
            for _ in 0..how_much {
                app.send_command_to_plugin(
                    this.plugin_id(),
                    plugin.clone(),
                    s.clone().into(),
                ).into_result()?;
            }
            ReturnValue::Repeat
        }
        UtilCmd::Batch{plugin,commands}=>{
            for command in commands {
                let s=serde_json::to_string(&command).unwrap();
                app.send_command_to_plugin(
                    this.plugin_id(),
                    plugin.clone(),
                    s.into(),
                ).into_result()?;
            }
            ReturnValue::Batch
        }
    }.piped(Ok)
}

//////////////////////////////////////////////////////////////////////////////////////


struct CommandUtils{
    plugin_id:PluginId,
}


impl Plugin for CommandUtils {
    fn json_command(
        &mut self,
        command: RStr<'_>,
        app:ApplicationMut<'_>,
    )->RResult<RString,AppError>{
        process_command(self,command,|this,command|{
            run_command_inner(this,command,app)
        })
    }

    fn plugin_id(&self)->&PluginId{
        &self.plugin_id
    }

    fn list_commands(&self)->RVec<CommandDescription>{
        vec![
            CommandDescription::from_literals(
                "Repeat",
"\
Sends a command to a plugin N times.

Command params:
{
    \"how_much\":10,
    \"plugin\":\"plugin:last\",
    \"command\":{ ... some command ... }
}
"
            ),
            CommandDescription::from_literals(
                "Batch",
"\
Sends a sequence of commands to a plugin.

Command params:
{
    \"plugin\":\"plugin_name\",
    \"commands\":[
        { ... some command ... },
        { ... some command ... },
        { ... some command ... }
    ]
}",
            ),
        ].into()
    }

    fn close(self,_app:ApplicationMut<'_>){}
}

