// Copyright 2025 The Axvisor Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use ax_hal::{self, mem::virt_to_phys};
use axvisor_api::arch::ArchIf;

struct ArchImpl;

#[axvisor_api::api_impl]
impl ArchIf for ArchImpl {
    fn hardware_inject_virtual_interrupt(irq: axvisor_api::vmm::InterruptVector) {
        crate::hal::arch::inject_interrupt(irq as _);
    }

    fn read_vgicd_typer() -> u32 {
        let mut gic = rdrive::get_one::<rdif_intc::Intc>()
            .expect("Failed to get GIC driver")
            .lock()
            .unwrap();
        if let Some(gic) = gic.typed_mut::<arm_gic_driver::v2::Gic>() {
            return gic.typer_raw();
        }

        if let Some(gic) = gic.typed_mut::<arm_gic_driver::v3::Gic>() {
            // Use the GICv3 driver to read the typer register
            return gic.typer_raw();
        }
        panic!("No GIC driver found");
    }

    fn read_vgicd_iidr() -> u32 {
        // use ax_std::os::arceos::modules::ax_hal::irq::MyVgic;
        // MyVgic::get_gicd().lock().get_iidr()
        let mut gic = rdrive::get_one::<rdif_intc::Intc>()
            .expect("Failed to get GIC driver")
            .lock()
            .unwrap();
        if let Some(gic) = gic.typed_mut::<arm_gic_driver::v2::Gic>() {
            return gic.iidr_raw();
        }

        if let Some(gic) = gic.typed_mut::<arm_gic_driver::v3::Gic>() {
            // Use the GICv3 driver to read the typer register
            return gic.iidr_raw();
        }

        panic!("No GIC driver found");
    }

    fn get_host_gicd_base() -> ax_memory_addr::PhysAddr {
        let mut gic = rdrive::get_one::<rdif_intc::Intc>()
            .expect("Failed to get GIC driver")
            .lock()
            .unwrap();
        if let Some(gic) = gic.typed_mut::<arm_gic_driver::v2::Gic>() {
            let ptr: *mut u8 = gic.gicd_addr().as_ptr();
            return virt_to_phys((ptr as usize).into());
        }

        if let Some(gic) = gic.typed_mut::<arm_gic_driver::v3::Gic>() {
            let ptr: *mut u8 = gic.gicd_addr().as_ptr();
            // Use the GICv3 driver to read the typer register
            return virt_to_phys((ptr as usize).into());
        }
        panic!("No GIC driver found");
    }

    fn get_host_gicr_base() -> ax_memory_addr::PhysAddr {
        let mut gic = rdrive::get_one::<rdif_intc::Intc>()
            .expect("Failed to get GIC driver")
            .lock()
            .unwrap();
        if let Some(gic) = gic.typed_mut::<arm_gic_driver::v3::Gic>() {
            let ptr: *mut u8 = gic.gicr_addr().as_ptr();
            return virt_to_phys((ptr as usize).into());
        }
        panic!("No GICv3 driver found");
    }

    fn fetch_irq() -> u64 {
        /// TODO: better implementation
        let mut gic = rdrive::get_one::<rdif_intc::Intc>()
            .expect("Failed to get GIC driver")
            .lock()
            .unwrap();
        if let Some(gic) = gic.typed_mut::<arm_gic_driver::v2::Gic>() {
            return u32::from(gic.cpu_interface().ack()) as _;
        }
        if let Some(gic) = gic.typed_mut::<arm_gic_driver::v3::Gic>() {
            return gic.cpu_interface().ack1().to_u32() as _;
        }
        panic!("No GIC driver found");
    }

    fn handle_irq() {
        ax_hal::trap::irq_handler(0);
    }
}
