use std::sync::Arc;
use acpi_tables::aml;
use acpi_tables::aml::Aml;
use base::warn;
use sync::Condvar;
use sync::Mutex;
use crate::pci::CrosvmDeviceId;
use crate::BusAccessInfo;
use crate::BusDevice;
use crate::DeviceId;
use crate::Suspendable;
const PMC_RESERVED1: u32 = 0;
const _PMC_RESERVED2: u32 = 0x4;
const _PMC_RESERVED3: u32 = 0x8;
const _PMC_RESERVED4: u32 = 0xc;
pub const VPMC_VIRT_MMIO_SIZE: u64 = 0x10;
pub struct VirtualPmc {
    mmio_base: u64,
    guest_suspended_cvar: Arc<(Mutex<bool>, Condvar)>,
}
impl VirtualPmc {
    pub fn new(mmio_base: u64, guest_suspended_cvar: Arc<(Mutex<bool>, Condvar)>) -> Self {
        VirtualPmc {
            mmio_base,
            guest_suspended_cvar,
        }
    }
}
fn handle_s2idle_request(guest_suspended_cvar: &Arc<(Mutex<bool>, Condvar)>) {
    let (lock, cvar) = &**guest_suspended_cvar;
    let mut guest_suspended = lock.lock();
    *guest_suspended = true;
    cvar.notify_one();
}
impl BusDevice for VirtualPmc {
    fn device_id(&self) -> DeviceId {
        CrosvmDeviceId::VirtualPmc.into()
    }
    fn debug_label(&self) -> String {
        "PmcVirt".to_owned()
    }
    fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
        if data.len() != std::mem::size_of::<u32>() {
            warn!(
                "{}: unsupported read length {}, only support 4bytes read",
                self.debug_label(),
                data.len()
            );
            return;
        }
        warn!("{}: unsupported read address {}", self.debug_label(), info);
    }
    fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
        if data.len() != std::mem::size_of::<u32>() {
            warn!(
                "{}: unsupported write length {}, only support 4bytes write",
                self.debug_label(),
                data.len()
            );
            return;
        }
        match info.offset as u32 {
            PMC_RESERVED1 => {
                handle_s2idle_request(&self.guest_suspended_cvar);
            }
            _ => {
                warn!("{}: Bad write to address {}", self.debug_label(), info);
            }
        };
    }
}
impl Aml for VirtualPmc {
    fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
        let vpmc_uuid = "9ea49ba3-434a-49a6-be30-37cc55c4d397";
        aml::Device::new(
            "VPMC".into(),
            vec![
                &aml::Name::new("_CID".into(), &aml::EISAName::new("PNP0D80")),
                &aml::Name::new("_HID".into(), &"HYPE0001"),
                &aml::Name::new("UUID".into(), &aml::Uuid::new(vpmc_uuid)),
                &aml::OpRegion::new(
                    "VREG".into(),
                    aml::OpRegionSpace::SystemMemory,
                    &self.mmio_base,
                    &(16_u32),
                ),
                &aml::Field::new(
                    "VREG".into(),
                    aml::FieldAccessType::DWord,
                    aml::FieldLockRule::Lock,
                    aml::FieldUpdateRule::Preserve,
                    vec![aml::FieldEntry::Named(*b"RSV1", 32)],
                ),
                &aml::Method::new(
                    "_DSM".into(),
                    4,
                    true,
                    vec![
                        &aml::If::new(
                            &aml::Equal::new(&aml::Arg(0), &aml::Name::new_field_name("UUID")),
                            vec![
                                &aml::If::new(
                                    &aml::Equal::new(&aml::Arg(2), &aml::ZERO),
                                    vec![&aml::Return::new(&aml::BufferData::new(vec![3]))],
                                ),
                                &aml::If::new(
                                    &aml::Equal::new(&aml::Arg(2), &aml::ONE),
                                    vec![&aml::Store::new(
                                        &aml::Name::new_field_name("RSV1"),
                                        &0x3_usize,
                                    )],
                                ),
                            ],
                        ),
                        &aml::Return::new(&aml::BufferData::new(vec![3])),
                    ],
                ),
            ],
        )
        .to_aml_bytes(bytes);
    }
}
impl Suspendable for VirtualPmc {}