#[macro_use]
extern crate rocket;

mod argp;
mod err;
mod procres;

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

use tokio::sync::mpsc;

use rocket::{Build, Ignite, Rocket};

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


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

#[qsu::async_trait]
impl RocketServiceHandler for MyService {
  type AppErr = Error;

  async fn init(
    &mut self,
    ictx: &mut InitCtx
  ) -> Result<Vec<Rocket<Build>>, Self::AppErr> {
    tracing::trace!("Running init()");

    let mut rockets = vec![];

    ictx.report(Some("Building a rocket!"));
    let rocket = rocket::build().mount("/", routes![index]);

    ictx.report(Some("Pushing a rocket"));
    rockets.push(rocket);

    Ok(rockets)
  }

  #[allow(clippy::redundant_pub_crate)]
  async fn run(
    &mut self,
    rockets: Vec<Rocket<Ignite>>,
    _re: &RunEnv
  ) -> Result<(), Self::AppErr> {
    for rocket in rockets {
      tokio::task::spawn(async {
        rocket.launch().await.unwrap();
      });
    }

    loop {
      tokio::select! {
        evt = self.rx.recv() => {
          match evt {
            Some(SvcEvt::Shutdown(_)) => {
              tracing::info!("The service subsystem requested that the
                  application shut down");
              break;
            }
            Some(SvcEvt::ReloadConf) => {
              tracing::info!("The service subsystem requested that application
                  reload configuration");
            }
            _ => { }
          }
        }
      }
    }

    Ok(())
  }

  async fn shutdown(
    &mut self,
    tctx: &mut TermCtx
  ) -> Result<(), Self::AppErr> {
    let msg = format!("Entered {}", "shutdown");
    tctx.report(Some(&msg));
    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");

  let bldr = Box::new(|| {
    let (tx, rx) = mpsc::unbounded_channel();
    let svcevt_handler = Box::new(move |msg| {
      // Just foward messages to runtime handler
      tx.send(msg).unwrap();
    });
    let rt_handler = Box::new(MyService { rx });
    SrvAppRt::Rocket {
      svcevt_handler,
      rt_handler
    }
  });

  // Parse, and process, command line arguments.
  let mut argsproc = argp::AppArgsProc { bldr };
  let ap =
    ArgParser::new(&svcname, &mut argsproc).regsvc_proc(|mut regsvc| {
      // Hard-code this as a network service application.
      regsvc.netservice_ref();

      // 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 simple Rocket server that logs requests");
      }

      regsvc
    });
  ap.proc()?;

  Ok(())
}

#[get("/")]
fn index() -> &'static str {
  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");

  "Hello, world!"
}

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