//! Simple service that does nothing other than log/trace every N seconds.

mod argp;
mod err;
mod procres;

use std::{
  thread,
  time::{Duration, Instant}
};

use tokio::sync::mpsc;

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

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


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

impl ServiceHandler for MyService {
  type AppErr = Error;

  fn init(&mut self, ictx: &mut InitCtx) -> Result<(), Self::AppErr> {
    ictx.report(Some("Entered init"));
    Ok(())
  }

  fn run(&mut self, _re: &RunEnv) -> Result<(), Self::AppErr> {
    const SECS: u64 = 30;
    // unwrap() is okay due to time scale
    let mut last_dump = Instant::now()
      .checked_sub(Duration::from_secs(SECS))
      .unwrap();
    loop {
      if last_dump.elapsed() > Duration::from_secs(SECS) {
        log::error!("error");
        log::warn!("warn");
        log::info!("info");
        log::debug!("debug");
        log::trace!("trace");

        tracing::error!("error");
        tracing::warn!("warn");
        tracing::info!("info");
        tracing::debug!("debug");
        tracing::trace!("trace");

        last_dump = Instant::now();
      }

      match self.rx.try_recv() {
        Ok(SvcEvt::Shutdown(_)) => {
          tracing::info!("Service application shutdown");
          break;
        }
        Ok(SvcEvt::ReloadConf) => {
          tracing::info!(
            "The service subsystem requested that the application reload its \
             configuration"
          );
        }
        _ => {}
      }

      thread::sleep(std::time::Duration::from_secs(1));
    }

    Ok(())
  }

  fn shutdown(&mut self, tctx: &mut TermCtx) -> Result<(), Self::AppErr> {
    tctx.report(Some("Entered 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> {
  // 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::unbounded_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 that says hello every 30 seconds");
      }
      regsvc
    });
  ap.proc()?;

  Ok(())
}

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