//! Example primarily for `LOG_FILTER`/`LogFilter` testing.
//!
//! Play around with the following settings:
//! - `LOG_LEVEL=trace`
//! - `LOG_FILTER="warn,hellosvc_logging::bar=trace,
//!   hellosvc_logging::baz=debug"`
//!
//! And then run:
//!
//! `cargo run --features=full --example hellosvc-logging`

mod argp;
mod err;
mod procres;

use std::{sync::mpsc, time::Duration};


use qsu::{
  argp::ArgParser,
  rt::{InitCtx, RunEnv, ServiceHandler, SrvAppRt, SvcEvt, TermCtx},
  set_default_log_filters
};

use err::Error;
use procres::ProcRes;


mod foo {
  mod subfoo {
    pub fn say_hello() {
      log::error!("Hello error from subfoo");
      log::warn!("Hello warn from subfoo");
      log::info!("Hello info from subfoo");
      log::debug!("Hello debug from subfoo");
      log::trace!("Hello trace from subfoo");
    }
  }

  pub fn say_hello() {
    log::error!("Hello error from foo");
    log::warn!("Hello warn from foo");
    log::info!("Hello info from foo");
    log::debug!("Hello debug from foo");
    log::trace!("Hello trace from foo");

    subfoo::say_hello();
  }
}

mod bar {
  pub fn say_hello() {
    log::error!("Hello error from bar");
    log::warn!("Hello warn from bar");
    log::info!("Hello info from bar");
    log::debug!("Hello debug from bar");
    log::trace!("Hello trace from bar");
  }
}

mod baz {
  pub fn say_hello() {
    log::error!("Hello error from baz");
    log::warn!("Hello warn from baz");
    log::info!("Hello info from baz");
    log::debug!("Hello debug from baz");
    log::trace!("Hello trace from baz");
  }
}

mod buzz {
  pub fn say_hello() {
    log::error!("Hello error from buzz");
    log::warn!("Hello warn from buzz");
    log::info!("Hello info from buzz");
    log::debug!("Hello debug from buzz");
    log::trace!("Hello trace from buzz");
  }
}


struct MyService {
  rx: mpsc::Receiver<SvcEvt>
}

impl ServiceHandler for MyService {
  type AppErr = Error;

  fn init(&mut self, ictx: &mut InitCtx) -> Result<(), Self::AppErr> {
    log::trace!("Running init()");

    ictx.report(Some("Entered init"));

    log::info!("Calling foo's say_hello()");
    foo::say_hello();

    log::info!("Calling bar's say_hello()");
    bar::say_hello();

    log::info!("Calling baz's say_hello()");
    baz::say_hello();

    log::info!("Calling buzz's say_hello()");
    buzz::say_hello();

    Ok(())
  }

  fn run(&mut self, _re: &RunEnv) -> Result<(), Self::AppErr> {
    log::info!("Wait for termination event for up to 2 seconds ..");
    loop {
      let Ok(evt) = self.rx.recv_timeout(Duration::from_secs(2)) else {
        break;
      };
      if let SvcEvt::Shutdown(_) = evt {
        break;
      }
    }

    Ok(())
  }

  fn shutdown(&mut self, tctx: &mut TermCtx) -> Result<(), Self::AppErr> {
    tctx.report(Some("Entered shutdown"));
    log::trace!("Running shutdown()");

    Ok(())
  }
}


fn main() -> ProcRes {
  // In the future we'll be able to use Try to implement support for implicit
  // conversion to ProcRes from a Result using `?`, but for now use this hack.
  ProcRes::into(main2().into())
}

fn main2() -> Result<(), Error> {
  // Set up default logging filtering.
  //
  // These will take effect only if LOG_FILTER/LogFilter is not used.
  set_default_log_filters(&[
    ("hellosvc_logging::foo", log::LevelFilter::Info),
    ("hellosvc_logging::bar", log::LevelFilter::Warn),
    ("hellosvc_logging::baz", log::LevelFilter::Trace),
    ("qsu", log::LevelFilter::Trace)
  ]);

  // Derive default service name from executable name.
  let svcname = qsu::default_service_name()
    .expect("Unable to determine default service name");

  // Create a closure that will generate a sync service runtime with a
  // service event handler that will forward any messages it receives to a
  // channel receiver end-point held by the main service handler.
  let bldr = Box::new(|| {
    let (tx, rx) = mpsc::channel();
    SrvAppRt::Sync {
      svcevt_handler: Box::new(move |msg| {
        // Just foward messages to runtime handler
        tx.send(msg).unwrap();
      }),
      rt_handler: Box::new(MyService { rx })
    }
  });

  // Parse, and process, command line arguments.
  // Add a service registration callback that will set service's description.
  let mut argsproc = argp::AppArgsProc { bldr };
  let ap =
    ArgParser::new(&svcname, &mut argsproc).regsvc_proc(|mut regsvc| {
      // Set description, but only if it hasn't been set already (presumably
      // via the command line).
      if regsvc.description.is_none() {
        regsvc.description_ref("A sync server used to test logging");
      }
      regsvc
    });
  ap.proc()?;

  Ok(())
}

// vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 :
