use core::cell::RefCell;

use cortex_m::interrupt::{CriticalSection, Mutex};

use stm32f0xx_hal::delay::Delay;
use stm32f0xx_hal::pac::{interrupt, Interrupt, EXTI, RCC, SYSCFG};

static GLOBALS: Mutex<RefCell<Option<Globals>>> = Mutex::new(RefCell::new(None));

pub struct SysCfg {
    syscfg: SYSCFG,
}

impl SysCfg {
    pub fn new(syscfg: SYSCFG, rcc: &mut RCC) -> Self {
        cortex_m::interrupt::free(|_| {
            rcc.apb2enr.modify(|_, w| w.syscfgen().set_bit());
        });

        Self { syscfg }
    }

    fn take(self) -> SYSCFG {
        self.syscfg
    }
}

pub struct Touch;

impl Touch {
    pub fn new(sys_cfg: SysCfg, exti: EXTI, delay: Delay) -> Self {
        let _ = cortex_m::interrupt::free(|cs| {
            GLOBALS
                .borrow(cs)
                .replace(Some(Globals::new(sys_cfg.take(), exti, delay)))
        });

        Self
    }

    pub fn enable(&self) {
        cortex_m::interrupt::free(|cs| {
            GLOBALS
                .borrow(cs)
                .borrow()
                .as_ref()
                .expect("GLOBALS should not be None")
                .setup_interrupts(cs)
        });
    }
}

struct Globals {
    syscfg: SYSCFG,
    exti: EXTI,
    /// Currently only used to ensure SysTick timer is set up correctly
    #[allow(dead_code)]
    delay: Delay,
}

impl Globals {
    fn new(syscfg: SYSCFG, exti: EXTI, delay: Delay) -> Self {
        Self {
            syscfg,
            exti,
            delay,
        }
    }

    fn setup_interrupts(&self, _cs: &CriticalSection) {
        self.syscfg.exticr1.modify(|_, w| {
            w.exti0().pb0();
            w.exti1().pb1();
            w.exti2().pb2();
            w
        });
        self.syscfg.exticr3.modify(|_, w| {
            w.exti10().pb10();
            w.exti11().pb11();
            w
        });
        self.syscfg.exticr4.modify(|_, w| {
            w.exti13().pc13();
            w.exti14().pc14();
            w.exti15().pc15();
            w
        });

        self.exti.imr.modify(|_, w| {
            w.mr0().unmasked();
            w.mr1().unmasked();
            w.mr2().unmasked();
            w.mr10().unmasked();
            w.mr11().unmasked();
            w.mr13().unmasked();
            w.mr14().unmasked();
            w.mr15().unmasked();
            w
        });

        self.exti.rtsr.modify(|_, w| {
            w.tr0().enabled();
            w.tr1().enabled();
            w.tr2().enabled();
            w.tr10().enabled();
            w.tr11().enabled();
            w.tr13().enabled();
            w.tr14().enabled();
            w.tr15().enabled();
            w
        });

        unsafe {
            cortex_m::peripheral::NVIC::unmask(Interrupt::EXTI0_1);
            cortex_m::peripheral::NVIC::unmask(Interrupt::EXTI2_3);
            cortex_m::peripheral::NVIC::unmask(Interrupt::EXTI4_15);
        }

        cortex_m::peripheral::NVIC::unpend(Interrupt::EXTI0_1);
        cortex_m::peripheral::NVIC::unpend(Interrupt::EXTI2_3);
        cortex_m::peripheral::NVIC::unpend(Interrupt::EXTI4_15);
    }
}

#[interrupt]
fn EXTI0_1() {
    cortex_m::interrupt::free(|cs| {
        if let Some(globals) = GLOBALS.borrow(cs).borrow().as_ref() {
            let pr = &globals.exti.pr;

            if pr.read().pif0().is_pending() {
                pr.write(|w| w.pif0().set_bit());
            }

            if pr.read().pif1().is_pending() {
                pr.write(|w| w.pif1().set_bit());
            }
        }
    });
}

#[interrupt]
fn EXTI2_3() {
    cortex_m::interrupt::free(|cs| {
        if let Some(globals) = GLOBALS.borrow(cs).borrow().as_ref() {
            let pr = &globals.exti.pr;

            if pr.read().pif2().is_pending() {
                pr.write(|w| w.pif2().set_bit());
            }

            if pr.read().pif3().is_pending() {
                pr.write(|w| w.pif3().set_bit());
            }
        }
    });
}

#[interrupt]
fn EXTI4_15() {
    cortex_m::interrupt::free(|cs| {
        if let Some(globals) = GLOBALS.borrow(cs).borrow().as_ref() {
            let pr = &globals.exti.pr;

            if pr.read().pif4().is_pending() {
                pr.write(|w| w.pif4().set_bit());
            }

            if pr.read().pif5().is_pending() {
                pr.write(|w| w.pif5().set_bit());
            }

            if pr.read().pif6().is_pending() {
                pr.write(|w| w.pif6().set_bit());
            }

            if pr.read().pif7().is_pending() {
                pr.write(|w| w.pif7().set_bit());
            }

            if pr.read().pif8().is_pending() {
                pr.write(|w| w.pif8().set_bit());
            }

            if pr.read().pif9().is_pending() {
                pr.write(|w| w.pif9().set_bit());
            }

            if pr.read().pif10().is_pending() {
                pr.write(|w| w.pif10().set_bit());
            }

            if pr.read().pif11().is_pending() {
                pr.write(|w| w.pif11().set_bit());
            }

            if pr.read().pif12().is_pending() {
                pr.write(|w| w.pif12().set_bit());
            }

            if pr.read().pif13().is_pending() {
                pr.write(|w| w.pif13().set_bit());
            }

            if pr.read().pif14().is_pending() {
                pr.write(|w| w.pif14().set_bit());
            }

            if pr.read().pif15().is_pending() {
                pr.write(|w| w.pif15().set_bit());
            }
        }
    });
}
