use std::ops::Index;
use std::vec::Vec;
use anyhow::anyhow;
use anyhow::Context;
use base::Error;
use base::Event;
use base::Result;
use hypervisor::IoapicState;
use hypervisor::IrqRoute;
use hypervisor::IrqSource;
use hypervisor::IrqSourceChip;
use hypervisor::LapicState;
use hypervisor::MPState;
use hypervisor::PicSelect;
use hypervisor::PicState;
use hypervisor::PitState;
use serde::Deserialize;
use serde::Serialize;
use snapshot::AnySnapshot;
use crate::IrqChip;
use crate::IrqChipCap;
pub trait IrqChipX86_64: IrqChip {
    fn try_box_clone(&self) -> Result<Box<dyn IrqChipX86_64>>;
    fn as_irq_chip(&self) -> &dyn IrqChip;
    fn as_irq_chip_mut(&mut self) -> &mut dyn IrqChip;
    fn get_pic_state(&self, select: PicSelect) -> Result<PicState>;
    fn set_pic_state(&mut self, select: PicSelect, state: &PicState) -> Result<()>;
    fn get_ioapic_state(&self) -> Result<IoapicState>;
    fn set_ioapic_state(&mut self, state: &IoapicState) -> Result<()>;
    fn get_lapic_state(&self, vcpu_id: usize) -> Result<LapicState>;
    fn set_lapic_state(&mut self, vcpu_id: usize, state: &LapicState) -> Result<()>;
    fn lapic_frequency(&self) -> u32;
    fn get_pit(&self) -> Result<PitState>;
    fn set_pit(&mut self, state: &PitState) -> Result<()>;
    fn pit_uses_speaker_port(&self) -> bool;
    fn snapshot_chip_specific(&self) -> anyhow::Result<AnySnapshot>;
    fn restore_chip_specific(&mut self, data: AnySnapshot) -> anyhow::Result<()>;
    fn snapshot(&self, cpus_num: usize) -> anyhow::Result<AnySnapshot> {
        let mut lapics: Vec<LapicState> = Vec::new();
        let mut mp_states: Vec<MPState> = Vec::new();
        let has_mp_states = self.check_capability(IrqChipCap::MpStateGetSet);
        for i in 0..cpus_num {
            lapics.push(self.get_lapic_state(i)?);
            if has_mp_states {
                mp_states.push(self.get_mp_state(i)?);
            }
        }
        AnySnapshot::to_any(IrqChipSnapshot {
            ioapic_state: self.get_ioapic_state()?,
            lapic_state: lapics,
            pic_state_1: self.get_pic_state(PicSelect::Primary)?,
            pic_state_2: self.get_pic_state(PicSelect::Secondary)?,
            pit_state: self.get_pit()?,
            chip_specific_state: self.snapshot_chip_specific()?,
            mp_state: mp_states,
        })
        .context("failed to serialize KvmKernelIrqChip")
    }
    fn restore(&mut self, data: AnySnapshot, vcpus_num: usize) -> anyhow::Result<()> {
        let deser: IrqChipSnapshot =
            AnySnapshot::from_any(data).context("failed to deserialize data")?;
        if deser.lapic_state.len() != vcpus_num {
            return Err(anyhow!(
                "IrqChip has the wrong number of LAPIC state snapshots: got {}, expected {}",
                deser.lapic_state.len(),
                vcpus_num
            ));
        }
        let supports_mp_states = self.check_capability(IrqChipCap::MpStateGetSet);
        if supports_mp_states {
            if deser.mp_state.len() != vcpus_num {
                return Err(anyhow!(
                    "IrqChip has the wrong number of mp state snapshots: got {}, expected {}",
                    deser.mp_state.len(),
                    vcpus_num
                ));
            }
        } else if !deser.mp_state.is_empty() {
            return Err(anyhow!(
                "IrqChip does not support mp state, but mp state was in the snapshot"
            ));
        }
        self.set_pit(&deser.pit_state)?;
        self.set_pic_state(PicSelect::Primary, &deser.pic_state_1)
            .context("failed to set primary PIC")?;
        self.set_pic_state(PicSelect::Secondary, &deser.pic_state_2)
            .context("failed to set secondary PIC")?;
        self.set_ioapic_state(&deser.ioapic_state)
            .context("failed to set IOAPIC state")?;
        self.restore_chip_specific(deser.chip_specific_state)
            .context("failed to set chip specific data")?;
        for (i, lapic) in deser.lapic_state.iter().enumerate() {
            self.set_lapic_state(i, lapic)
                .context("failed to set LAPIC state")?;
        }
        if supports_mp_states {
            for (i, mp_state) in deser.mp_state.iter().enumerate() {
                self.set_mp_state(i, mp_state)
                    .context("failed to set mp state")?;
            }
        }
        Ok(())
    }
}
#[derive(Serialize, Deserialize)]
struct IrqChipSnapshot {
    ioapic_state: IoapicState,
    lapic_state: Vec<LapicState>,
    pic_state_1: PicState,
    pic_state_2: PicState,
    pit_state: PitState,
    chip_specific_state: AnySnapshot,
    mp_state: Vec<MPState>,
}
pub struct Routes {
    routes: Vec<Vec<IrqSource>>,
}
impl Routes {
    pub fn new() -> Self {
        Routes { routes: vec![] }
    }
    pub fn add(&mut self, route: IrqRoute) -> Result<()> {
        let routes = self.get_mut(route.gsi as usize);
        if routes.iter().any(|r| !Self::same_source(&route.source, r)) {
            return Err(Error::new(libc::EINVAL));
        }
        routes.retain(|r| !Self::conflict(&route.source, r));
        routes.push(route.source);
        Ok(())
    }
    pub fn replace_all(&mut self, routes: &[IrqRoute]) -> Result<()> {
        self.routes.clear();
        for r in routes {
            self.add(*r)?;
        }
        Ok(())
    }
    pub fn default_pic_ioapic_routes(ioapic_pins: usize) -> Vec<IrqRoute> {
        let mut routes: Vec<IrqRoute> = Vec::new();
        for i in 0..8 {
            routes.push(IrqRoute::pic_irq_route(IrqSourceChip::PicPrimary, i));
            routes.push(IrqRoute::ioapic_irq_route(i));
        }
        for i in 8..16 {
            routes.push(IrqRoute::pic_irq_route(IrqSourceChip::PicSecondary, i));
            routes.push(IrqRoute::ioapic_irq_route(i));
        }
        for i in 16..ioapic_pins as u32 {
            routes.push(IrqRoute::ioapic_irq_route(i));
        }
        routes
    }
    pub fn get_routes(&self) -> Vec<IrqRoute> {
        let mut routes = Vec::with_capacity(self.routes.len());
        for (gsi, sources) in self.routes.iter().enumerate() {
            for source in sources.iter() {
                routes.push(IrqRoute {
                    gsi: gsi.try_into().expect("GSIs must be < u32::MAX"),
                    source: *source,
                });
            }
        }
        routes
    }
    fn conflict(source: &IrqSource, other: &IrqSource) -> bool {
        use IrqSource::*;
        if let (Msi { .. }, Msi { .. }) = (source, other) {
            return true;
        }
        if let (
            Irqchip { chip, .. },
            Irqchip {
                chip: other_chip, ..
            },
        ) = (source, other)
        {
            return chip == other_chip;
        }
        false
    }
    fn same_source(source: &IrqSource, other: &IrqSource) -> bool {
        use IrqSource::*;
        matches!(
            (source, other),
            (Irqchip { .. }, Irqchip { .. }) | (Msi { .. }, Msi { .. })
        )
    }
    fn get_mut(&mut self, irq: usize) -> &mut Vec<IrqSource> {
        if irq >= self.routes.len() {
            self.routes.resize_with(irq + 1, Vec::new);
        }
        self.routes.get_mut(irq).unwrap()
    }
}
impl Default for Routes {
    fn default() -> Self {
        Self::new()
    }
}
const EMPTY_ROUTE: [IrqSource; 0] = [];
impl Index<usize> for Routes {
    type Output = [IrqSource];
    fn index(&self, irq: usize) -> &Self::Output {
        if irq < self.routes.len() {
            self.routes[irq].as_slice()
        } else {
            &EMPTY_ROUTE
        }
    }
}
pub(super) struct DelayedIoApicIrqEvents {
    pub events: Vec<usize>,
    pub trigger: Event,
}
impl DelayedIoApicIrqEvents {
    pub fn new() -> Result<Self> {
        Ok(DelayedIoApicIrqEvents {
            events: Vec::new(),
            trigger: Event::new()?,
        })
    }
}