//! service

use futures::StreamExt;
use msft::service::message::{Arguments, ServiceMessageStream};
use msft::service::status::{CurrentState, ServiceControlAccept, ServiceType, StatusHandle};
use tracing::info;
use tracing::trace;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::{filter::LevelFilter, fmt, prelude::*};
use win_etw_tracing::TracelogSubscriber;

fn main() {
    // Setup logging + welcome message
    let guid = make_guid();
    let etw = TracelogSubscriber::new(guid, "Altronix Service Tracelog").unwrap();
    let file_appender =
        tracing_appender::rolling::daily("C:\\Users\\Tom\\Documents", "demo-service.log");
    let (non_blocking, _guard) = tracing_appender::non_blocking(file_appender);
    let fmt = fmt::layer().with_target(false).with_writer(non_blocking);
    tracing_subscriber::registry()
        .with(fmt)
        .with(etw)
        .with(LevelFilter::TRACE)
        .init();
    info!("Application service starting...");

    // Register a ServiceMain function
    msft::service::start_service_ctrl_dispatcher![
        // The Altronix ZDK device management service
        ("Altronix ZDK Device Service", svc_dev),
        // The Altronix ZDK update service
        ("Altronix ZDK Update Service", svc_update),
    ];
}

/// This service will manage I/O communication with Altronix LinQ devices. Including managing
/// firmware updates.
#[msft::service::service(name = "Altronix ZDK Device Service", mt = true)]
async fn svc_dev(mut hstatus: StatusHandle, mut stream: ServiceMessageStream, _arg: Arguments) {
    // Print some welcome logs and set service status
    info!("Starting Altronix ZDK Device Service...");
    hstatus
        .set_service_type(ServiceType::UserShareProcess)
        .set_current_state(CurrentState::ServiceRunning)
        .set_control_accept(ServiceControlAccept::all())
        .set_status()?;

    // Register a status handle and listen for incoming SCM messages
    info!("Register Altronix ZDK Device Service StatusHandle Ok");
    while let Some(msg) = stream.next().await {
        trace!(?msg, "Received a SCM msg");
    }

    // We received a stop or shutdown, end our program
    info!("Altronix ZDK Device Service Stopping");
    hstatus
        .set_current_state(CurrentState::ServiceStopped)
        .set_status()?;
    info!("Altronix ZDK Device Service Stopped");
    std::io::Result::Ok(())
}

/// This service will manage Host updates. NOTE that this service does not manage firmware updates
/// for devices. Firmware updates for devices are managed by the "Device Service"
#[msft::service::service(name = "Altronix ZDK Update Service")]
async fn svc_update(mut hstatus: StatusHandle, mut stream: ServiceMessageStream, _arg: Arguments) {
    // Print some welcome logs and set service status
    info!("Starting Altronix ZDK Update Service...");
    hstatus
        .set_service_type(ServiceType::UserShareProcess)
        .set_current_state(CurrentState::ServiceRunning)
        .set_control_accept(ServiceControlAccept::all())
        .set_status()?;

    // Register a status handle and listen for incoming SCM messages
    info!("Register Altronix ZDK Update Service StatusHandle Ok");
    while let Some(msg) = stream.next().await {
        trace!(?msg, "Received a SCM msg");
    }

    // We received a stop or shutdown, end our program
    info!("Altronix ZDK Update Service Stopping");
    hstatus
        .set_current_state(CurrentState::ServiceStopped)
        .set_status()?;
    info!("Altronix ZDK Device Service Stopped");
    std::io::Result::Ok(())
}

/// temp work around because windows_sys::GUID and win_etc_provider::GUID are not the same.
fn make_guid() -> win_etw_provider::GUID {
    let guid = msft::service::util::guid::new("a9214533-3f5f-475b-8140-cb96b289270b");
    win_etw_provider::GUID {
        data1: guid.0.data1,
        data2: guid.0.data2,
        data3: guid.0.data3,
        data4: guid.0.data4,
    }
}
