/*
DO NOT MODIFY THIS FILE {# unless you see this comment #}.  It is automatically generated and changes will be over-written
on the next generation.

It contains enumerations used by the {{stinger.name}} interface.
*/
use std::any::Any;

use mqttier::{MqttierClient, MqttierOptions, Connection};
use {{stinger.rust.package_name}}::server::{ {{stinger.rust.server_struct_name}}{%if stinger.methods|length>0%}, {{stinger.name | UpperCamelCase }}MethodHandlers{%endif%} };
use tokio::time::{sleep, Duration};
{%if stinger.methods|length > 0%}
use std::sync::Arc;
use tokio::sync::Mutex;
use tracing_subscriber;
use async_trait::async_trait;
{%endif%}
use tokio::join;
#[allow(unused_imports)]
use {{stinger.rust.package_name}}::payloads::{MethodReturnCode, *};

{%if stinger.methods|length > 0%}
struct {{stinger.name | UpperCamelCase }}MethodImpl {
    server: Option<{{stinger.rust.server_struct_name}}>,
}

impl {{stinger.name | UpperCamelCase }}MethodImpl {
    fn new() -> Self {
        Self {
            server: None,
        }
    }
}

#[async_trait]
impl {{stinger.name | UpperCamelCase }}MethodHandlers for {{stinger.name | UpperCamelCase }}MethodImpl {

    async fn initialize(&mut self, server: {{stinger.rust.server_struct_name}}) -> Result<(), MethodReturnCode> {
        self.server = Some(server.clone());
        Ok(())
    }

    {%for method_name, method in stinger.methods.items()%}
    async fn handle_{{method_name|snake_case}}(&self, {%for arg in method.arg_list%}_{{arg.name|snake_case}}: {{arg.rust_type}}{%if not loop.last%}, {%endif%}{%endfor%}) -> Result<{{method.return_value_rust_type}}, MethodReturnCode> {
        println!("Handling {{method_name}}");
        {%-if method.return_value_type == 'struct'%}
        let rv = {{method.return_arg_list[0].rust_temp_type}} {
            {%for arg in method.return_value.members-%}
            {{arg.name|snake_case}}: {{arg.get_random_example_value(lang="rust")}},
            {%endfor%}
        };
        {%if method.return_arg_list[0].optional %}
        Ok(Some(rv))
        {%else%}
        Ok(rv)
        {%endif%}
        {%-elif method.return_value_type == 'multiple'%}
        let rv = {{method.return_value_rust_type}} {
            {%for arg in method.return_value-%}
            {{arg.name|snake_case}}: {{arg.get_random_example_value(lang="rust")}},
            {%endfor%}
        };
        Ok(rv)
        {%elif method.return_value_type is not false %}
        Ok({{method.return_value.get_random_example_value(lang="rust")}})
        {%else%}
        Ok(())
        {%-endif%}
    }
    {%endfor%}

    fn as_any(&self) -> &dyn Any {
        self
    }
}
{%endif%}

#[tokio::main]
async fn main() {

    // Initialize tracing subscriber to see log output
    tracing_subscriber::fmt()
        .with_env_filter(
            tracing_subscriber::EnvFilter::try_from_default_env()
                .unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info"))
        )
        .init();

    let mqttier_options = MqttierOptions::new()
        .connection(Connection::TcpLocalhost(1883))
        .client_id("rust-server-demo".to_string())
        .build();
    let mut connection = MqttierClient::new(mqttier_options).unwrap();

    {%if stinger.methods|length > 0%}
    let handlers: Arc<Mutex<Box<dyn {{stinger.name | UpperCamelCase }}MethodHandlers>>> = Arc::new(Mutex::new(Box::new({{stinger.name | UpperCamelCase }}MethodImpl::new())));

    let mut server = {{stinger.rust.server_struct_name}}::new(&mut connection, handlers.clone(), "rust-server-demo:1".to_string()).await;
    {%else%}
    let mut server = {{stinger.rust.server_struct_name}}::new(&mut connection, "rust-server-demo:1".to_string()).await;
    {%endif%}

    let mut looping_server = server.clone();
    let _loop_join_handle = tokio::spawn(async move {
        println!("Starting connection loop");
        let _conn_loop = looping_server.run_loop().await;
    });

    {%for prop_name, prop in stinger.properties.items()%}
    println!("Setting initial value for property '{{prop_name}}'");
    {%-if prop.arg_list | length == 1 %}
    let prop_init_future = server.set_{{prop_name | snake_case}}({{prop.arg_list[0].get_random_example_value(lang="rust")}}).await;
    {%-else%}{# many args in property #}
    let new_value = {{prop.rust_type}} {
        {%-for arg in prop.arg_list %}
            {{arg.name|snake_case}}: {{arg.get_random_example_value(lang="rust")}},
        {%-endfor%}
    };
    let prop_init_future = server.set_{{prop_name | snake_case}}(new_value).await;
    {%endif%}
    if let Err(e) = prop_init_future.await {
        eprintln!("Error initializing property '{{prop_name}}': {:?}", e);
    }
    {%endfor%}{# property initialization -#}

    {% if stinger.signals|length > 0 %}
    let mut server_clone1 = server.clone();{%endif%}
    let signal_publish_task = tokio::spawn(async move {
        loop {
            sleep(Duration::from_secs(9)).await;
            {%for sig_name, sig in stinger.signals.items()%}
            sleep(Duration::from_secs(1)).await;
            println!("Emitting signal '{{sig_name}}'");
            let signal_result_future = server_clone1.emit_{{sig_name|snake_case}}({%for arg in sig.arg_list%}{{arg.get_random_example_value(lang="rust")}}{%if not loop.last%}, {%endif%}{%endfor%}).await;
            let signal_result = signal_result_future.await;
            println!("Signal '{{sig_name}}' was sent: {:?}", signal_result);
            {%endfor%}
        }
    });

    {%if stinger.properties|length > 0 %}
    let mut server_clone2 = server.clone();{%endif%}
    let property_publish_task = tokio::spawn(async move {
        loop {
            sleep(Duration::from_secs(11)).await;
            
            {%for prop_name, prop in stinger.properties.items()%}
            sleep(Duration::from_secs(1)).await;
            println!("Changing property '{{prop_name}}'");
            {%-if prop.arg_list | length == 1 %}
            let prop_change_future = server_clone2.set_{{prop_name | snake_case}}({{prop.arg_list[0].get_random_example_value(lang="rust", seed=3)}}).await;
            if let Err(e) = prop_change_future.await {
                eprintln!("Error changing property '{{prop_name}}': {:?}", e);
            }
            {%-else%}{# many args in property #}
            let new_value = {{prop.rust_type}} {
                {%-for arg in prop.arg_list %}
                    {{arg.name|snake_case}}: {{arg.get_random_example_value(lang="rust", seed=4)}},
                {%-endfor%}
            };
            let _ = server_clone2.set_{{prop_name | snake_case}}(new_value).await;
            {%endif%}
            {%endfor%}{# property updates -#}
        }
    });

    let _ = join!(signal_publish_task, property_publish_task);

    // Ctrl-C to stop
}
