/*!
This is an example `interface crate`,
where all publically available modules(structs of function pointers) and types are declared,

To load the library and the modules together,
call `<PluginMod_Ref as RootModule>::load_from_directory`,
which will load the dynamic library from a directory(folder),
and all the modules inside of the library.

*/

use abi_stable::{
    StableAbi,
    sabi_trait,
    package_version_strings,
    declare_root_module_statics,
    library::RootModule,
    sabi_types::VersionStrings,
    external_types::{
        crossbeam_channel::RSender,
    },
    std_types::{RBox, RCow, RVec, RStr, RString, RResult, ROption, ROk, RSome},
};
use serde::{Serialize, Deserialize};
mod commands;
mod error;
mod which_plugin;
mod vec_from_map;

pub mod utils;

pub use self::{
    commands::{
        BasicCommand, BasicRetVal, CommandDescription, CommandTrait, WhichVariant, AsyncCommand,
    },
    error::{Error, Unsupported},
    which_plugin::WhichPlugin,
    vec_from_map::VecFromMap,
};


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


/**
The identifier for a plugin.
*/
#[repr(C)]
#[derive(Debug, Clone, PartialEq, Eq, StableAbi, Serialize, Deserialize)]
pub struct PluginId {
    pub named:RCow<'static,str>,
    /// The number of the instance of this Plugin.
    pub instance:u64,
}


/// Describes whether a boxed error is a command or a return value.
#[repr(u8)]
#[derive(Debug, Clone, PartialEq, Eq, StableAbi, Serialize, Deserialize)]
pub enum WhichCommandRet {
    Command,
    Return,
}


/// The response from having called `ApplicationMut::send_command_to_plugin` ealier.
#[repr(C)]
#[derive(Debug, Clone, PartialEq, Eq, StableAbi)]
pub struct PluginResponse<'a> {
    /// The id of the plugin that is responding.
    pub plugin_id:PluginId,
    /// The response from the plugin
    pub response:RCow<'a, str>,
}


impl<'a> PluginResponse<'a> {
    pub fn owned_response(plugin_id: PluginId, response: RString) -> Self {
        Self{plugin_id, response: response.into()}
    }
    pub fn borrowed_response(plugin_id: PluginId, response: RStr<'a>) -> Self {
        Self{plugin_id, response: response.into()}
    }
}



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


pub type PluginType=Plugin_TO<'static, RBox<()>>;


/**
A plugin which is loaded by the application,and provides some functionality.


*/
#[sabi_trait]
//#[sabi(debug_print)]
pub trait Plugin {

    /// Handles a JSON encoded command.
    fn json_command(
        &mut self,
        command: RStr<'_>,
        app:ApplicationMut<'_>,
    ) -> RResult<RString, Error>;

    /// Handles a response from another Plugin,
    /// from having called `ApplicationMut::send_command_to_plugin` ealier.
    fn handle_response<'a>(
        &mut self,
        response:PluginResponse<'a>,
        _app:ApplicationMut<'_>,
    ) -> RResult<ROption<PluginResponse<'a>>, Error> {
        ROk(RSome(response))
    }

    /// Gets the PluginId that was passed to this plugin in its constructor.
    fn plugin_id(&self) -> &PluginId;

    /// Gets a description of all commands from this Plugin.
    fn list_commands(&self) -> RVec<CommandDescription>;

    /*
Closes the plugin,

This does not unload the dynamic library of this plugin,
you can instantiate another instance of this plugin with 
`PluginMod_Ref::get_module().new()(application_handle)`.



The `#[sabi(last_prefix_field)]` attribute here means that this is the last method 
that was defined in the first compatible version of the library
(0.1.0, 0.2.0, 0.3.0, 1.0.0, 2.0.0 ,etc),
requiring new methods to always be added below preexisting ones.

The `#[sabi(last_prefix_field)]` attribute would stay on this method until the library 
bumps its "major" version,
at which point it would be moved to the last method at the time.
    */
    #[sabi(last_prefix_field)]
    fn close(self, app:ApplicationMut<'_>);
}


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

/// The root module of a`plugin` dynamic library.
///
/// To load this module,
/// call <PluginMod as RootModule>::load_from_directory(some_directory_path)
#[repr(C)]
#[derive(StableAbi)] 
#[sabi(kind(Prefix(prefix_ref="PluginMod_Ref")))]
#[sabi(missing_field(panic))]
pub struct PluginMod {
/**
Constructs the plugin.


The `#[sabi(last_prefix_field)]` attribute here means that this is the last field in this struct
that was defined in the first compatible version of the library
(0.1.0, 0.2.0, 0.3.0, 1.0.0, 2.0.0 ,etc),
requiring new fields to always be added below preexisting ones.

The `#[sabi(last_prefix_field)]` attribute would stay on this field until the library 
bumps its "major" version,
at which point it would be moved to the last field at the time.

*/
    #[sabi(last_prefix_field)]
    pub new: extern "C" fn(RSender<AsyncCommand>, PluginId) -> RResult<PluginType, Error>,
}


impl RootModule for PluginMod_Ref {
    declare_root_module_statics!{PluginMod_Ref}
    const BASE_NAME: &'static str = "plugin";
    const NAME: &'static str = "plugin";
    const VERSION_STRINGS: VersionStrings = package_version_strings!();
}



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


/// A mutable reference to the application implementation.
pub type ApplicationMut<'a> = Application_TO<'a, &'a mut ()>;


#[sabi_trait]
pub trait Application{

    /// Asynchronously Sends a command to the plugin(s) specified by `which_plugin`.
    ///
    /// # Errors
    ///
    /// Returns an `Error::InvalidPlugin` if `which_plugin` is invalid.
    fn send_command_to_plugin(
        &mut self,
        from: &PluginId,
        which_plugin: WhichPlugin,
        command: RString,
    ) -> RResult<(), Error>;

/**    
Gets the `PluginId`s of the plugins specified by `which_plugin`.


The `#[sabi(last_prefix_field)]` attribute here means that this is the last method 
that was defined in the first compatible version of the library
(0.1.0, 0.2.0, 0.3.0, 1.0.0, 2.0.0 ,etc),
requiring new methods to always be added below preexisting ones.

The `#[sabi(last_prefix_field)]` attribute would stay on this method until the library 
bumps its "major" version,
at which point it would be moved to the last method at the time.

*/
    #[sabi(last_prefix_field)]
    fn get_plugin_id(&self, which_plugin:WhichPlugin) -> RResult<RVec<PluginId>, Error>;

    /// Gets the sender end of a channel to send commands to the application/other plugins.
    fn sender(&self) -> RSender<AsyncCommand>;

    /// Gets the PluginId of all loaded plugins
    fn loaded_plugins(&self) -> RVec<PluginId>;
}