//! Testing the CORBA device backend.

use tango::dev::{DevProxy, DevState};
use tango::database::DatabaseProxy;
use tango::zeromq::ZmqEventBackend;
use tango::omniorb::CorbaBackend;

use tracing::info;

use tokio::select;


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

    tracing_subscriber::fmt()
        .with_target(false)
        .with_env_filter(tracing_subscriber::EnvFilter::builder()
            .with_default_directive(tracing_subscriber::filter::LevelFilter::DEBUG.into())
            .from_env_lossy())
        .init();

    CorbaBackend::init().unwrap().register();
    ZmqEventBackend::init().await.unwrap().register();

    let db = DatabaseProxy::new_from_env();
    info!("Database: {:?}", db.trl().await);
    info!("Database ping: {:?}", db.ping().await);
    info!("Database get props: {:?}", db.get_dev_prop::<String, _>("elin/master/op", "mod_devs").await);
    info!("Database list domains: {:?}", db.list_dev_domain("*/*/*").await);
    info!("Database command with bad arg: {:?}", db.command::<_, ()>("DbAddServer", false).await);
    info!("Database get alias (not existing): {:?}", db.get_dev_alias("elin/master/op").await);
    
    let univ_dev = DevProxy::new("sys/universaltest/test-ub22");
    info!("Device: {:?}", univ_dev.trl().await);
    info!("Device name: {:?}", univ_dev.name().await);
    info!("Device ping: {:?}", univ_dev.ping().await);
    info!("Device state: {:?}", univ_dev.state().await);
    info!("Device status: {:?}", univ_dev.status().await);
    info!("Device get command info: {:#?}", univ_dev.get_all_command_info().await);
    info!("Device command: {:#?}", univ_dev.command::<_, ()>("Init", ()).await);
    // info!("Device read all attr: {:#?}", dev.read_all_attr().await);
    // info!("Device get all attr info: {:#?}", dev.get_all_attr_info().await);
    info!("Device get attr info: {:#?}", univ_dev.get_many_attr_info("DevStringRO").await);
    info!("Device read attr (raw): {:#?}", univ_dev.read_many_attr(["DevStateRW", "DevStringRO"]).await);
    info!("Device read attr (raw, ignore case): {:#?}", univ_dev.read_many_attr(["devstaterw"]).await);
    info!("Device read attr: {:#?}", univ_dev.read_attr::<(DevState, String), _>(("DevStateRW", "DevStringRO")).await);
    info!("Device write attr: {:#?}", univ_dev.write_attr(("DevStateRW", DevState::On)).await);
    info!("Device write state (should error): {:?}", univ_dev.write_attr(("State", DevState::On)).await);
    info!("Device write attr with invalid type: {:?}", univ_dev.write_attr(("DevStateRW", false)).await);
    info!("Device all prop: {:#?}", univ_dev.get_all_prop().await);
    info!("Device get prop: {:#?}", univ_dev.get_prop::<(String, f32), _>(("test_prop", "test_float")).await);
    info!("Device get prop (ignore case): {:#?}", univ_dev.get_prop::<String, _>("TeSt_PrOp").await);
    // info!("Device black box: {:#?}", dev.black_box(None).await);

    let test_dev = DevProxy::new("sys/tango-test/test-ub22");
    info!("Device enum attr info: {:#?}", test_dev.get_many_attr_info("enum_scalar").await);
    info!("Device enum read attr: {:#?}", test_dev.read_attr::<i16, _>("enum_scalar").await);
    
    let univ_admin_dev = univ_dev.admin_dev().await.unwrap();
    info!("Admin device: {:?}", univ_admin_dev.trl().await);
    info!("Admin device ping: {:?}", univ_admin_dev.ping().await);
    
    let pg1 = DevProxy::new("test/playground/1");
    select! {
        res = pg1.command::<_, ()>("Sleep", 10.0) => {
            info!("Sleep(10) finished: {res:?}");
        }
        res = pg1.command::<_, ()>("Sleep", 5.0) => {
            info!("Sleep(5) finished: {res:?}");
        }
    }

    // let mut channel = linac.subscribe_attr_value::<String>("Sequencef", AttrValueEvent::Change).await.unwrap();
    // let start_time = Instant::now() + Duration::from_secs(5);
    // loop {
    //     select! {
    //         event = channel.wait() => {
    //             info!("Linac sequence event: {event:?}");
    //         }
    //         () = sleep_until(start_time.into()) => {
    //             break;
    //         }
    //     }
    // }
    // info!("Dropped event channel...");
    // drop(channel);

    std::future::pending::<()>().await;
    unreachable!();

}
