use embedded_hal::digital::v2::InputPin;
use crate::{Interface, Register};

#[repr(u32)]
pub enum IRQMask {
    /* Main interrupt register. */

    /* No ST25R3911 interrupt source                                */
    None = 0,
    /* All ST25R3911 interrupt sources                              */
    All = 0xFFFFFF,
    /* ST25R3911 oscillator stable interrupt                        */
    Osc = 0x80,
    /* ST25R3911 FIFO water level interrupt                         */
    Fwl = 0x40,
    /* ST25R3911 start of receive interrupt                         */
    Rxs = 0x20,
    /* ST25R3911 end of receive interrupt                           */
    Rxe = 0x10,
    /* ST25R3911 end of transmission interrupt                      */
    Txe = 0x08,
    /* ST25R3911 bit collision interrupt                            */
    Col = 0x04,

    /* Timer and NFC interrupt register. */

    /* ST25R3911 termination of direct command interrupt            */
    Dct = 0x8000,
    /* ST25R3911 no-response timer expired interrupt                */
    Nre = 0x4000,
    /* ST25R3911 general purpose timer expired interrupt            */
    Gpe = 0x2000,
    /* ST25R3911 external field on interrupt                        */
    Eon = 0x1000,
    /* ST25R3911 external field off interrupt                       */
    Eof = 0x0800,
    /* ST25R3911 collision during RF collision avoidance interrupt  */
    Cac = 0x0400,
    /* ST25R3911 minimum guard time expired interrupt               */
    Cat = 0x0200,
    /* ST25R3911 initiator bit rate recognized interrupt            */
    Nfct = 0x0100,

    /* Error and wake-up interrupt register. */

    /* ST25R3911 CRC error interrupt                                */
    Crc = 0x800000,
    /* ST25R3911 parity error interrupt                             */
    Par = 0x400000,
    /* ST25R3911 soft framing error interrupt                       */
    Err2 = 0x200000,
    /* ST25R3911 hard framing error interrupt                       */
    Err1 = 0x100000,
    /* ST25R3911 wake-up interrupt                                  */
    Wt = 0x080000,
    /* ST25R3911 wake-up due to amplitude interrupt                 */
    Wam = 0x040000,
    /* ST25R3911 wake-up due to phase interrupt                     */
    Wph = 0x020000,
    /* ST25R3911 wake-up due to capacitance measurement             */
    Wcap = 0x010000,


    /* additional interrupts in ST25R3911_REG_IRQ_TIMER_NFC         */
    Tim = 0x02,
    /* additional interrupts in ST25R3911_REG_IRQ_ERROR_WUP         */
    Err = 0x01,
}

type InterruptCallback = fn(&mut Interrupt);

const INT_REGS_LEN: u8 = Register::ErrorWakeUpInterruptStatus as u8 - Register::MainInterruptStatus as u8 + 1;
//FIXME: Finish this
pub(crate) fn default_callback<SPI, CS, IRQ>(interrupt: &mut Interrupt, interface: &mut Interface<SPI, CS, IRQ>, irq_pin: IRQ) {
    let mut iregs: [u8; INT_REGS_LEN as usize] = interface.read_register_range(Register::MainInterruptStatus, INT_REGS_LEN).unwrap();
    while irq_pin.is_high() {
        iregs = interface.read_multiple_registers(Register::MainInterruptStatus, INT_REGS_LEN).unwrap().into();
    }

    // FIXME: Protect IRQ status
    interrupt.status = (iregs[0] as u32) | (iregs[1] as u32) << 8 | iregs[2] << 16 as u32;
}

pub struct Interrupt {
    prevCallback: Option<InterruptCallback>,
    callback: Option<InterruptCallback>,
    status: u32,
    mask: u32,
}

//FIXME: Protect Status when writing

impl Interrupt {
    pub fn new() -> Self {
        Interrupt {
            prevCallback: None,
            callback: None,
            status: 0,
            mask: 0,
        }
    }
    pub fn modifyMask(&mut self, clr_mask: u32, set_mask: u32) {
        self.mask = (self.mask & !clr_mask) | set_mask;
    }
    pub fn status(&self) -> u32 {
        self.status
    }
    pub fn mask(&self) -> u32 {
        self.mask
    }
    pub fn set_prevCallback(&mut self, prevCallback: Option<InterruptCallback>) {
        self.prevCallback = prevCallback;
    }
    pub fn set_callback(&mut self, callback: Option<InterruptCallback>) {
        self.callback = callback;
    }
    pub fn set_status(&mut self, status: u32) {
        self.status = status;
    }
    pub fn set_mask(&mut self, mask: u32) {
        self.mask = mask;
    }
}