#![no_std]
#![no_main]

use esp32_hal::{
    clock::ClockControl,
    dma::{DmaPriority, DmaTransferRxTx},
    pdma::Dma, 
    pac::Peripherals,
    gpio_types::*,
    gpio::*,
    prelude::*,
    spi::{dma::WithDmaSpi2, Spi, SpiMode},
    timer::TimerGroup,
    Rtc,
    IO,
    Delay,
};


// work pls

/* Display and graphics */
use ili9341::{DisplaySize240x320, Ili9341, Orientation};

use display_interface_spi::SPIInterfaceNoCS;


use embedded_graphics::mono_font::MonoTextStyle;
use embedded_graphics::pixelcolor::*;
use embedded_graphics::prelude::*;
use embedded_graphics::primitives::*;
use embedded_graphics::text::*;
use embedded_graphics::image::Image;
use embedded_graphics::geometry::*;
use embedded_graphics::draw_target::DrawTarget;
use embedded_hal;

use profont::{PROFONT_24_POINT, PROFONT_18_POINT};

use xtensa_lx_rt::entry;

use esp_println::println;
use esp_backtrace as _;

const textStyle : TextStyle = TextStyleBuilder::new()
    .alignment(embedded_graphics::text::Alignment::Center)
    .baseline(embedded_graphics::text::Baseline::Middle)
    .build();


/* Some stuff for correct orientation and color on ILI9341 */
pub enum KalugaOrientation {
    Portrait,
    PortraitFlipped,
    Landscape,
    LandscapeVericallyFlipped,
    LandscapeFlipped,
}

impl ili9341::Mode for KalugaOrientation {
    fn mode(&self) -> u8 {
        match self {
            Self::Portrait => 0,
            Self::LandscapeVericallyFlipped => 0x20,
            Self::Landscape => 0x20 | 0x40,
            Self::PortraitFlipped => 0x80 | 0x40,
            Self::LandscapeFlipped => 0x80 | 0x20 | 0x08,
        }
    }

    fn is_landscape(&self) -> bool {
        matches!(self, Self::Landscape | Self::LandscapeFlipped | Self::LandscapeVericallyFlipped)
    }
}

/* Debouncing algorythm */
pub enum Event {
    Pressed,
    Released,
    Nothing,
}
pub struct Button<T> {
    button: T,
    pressed: bool,
}
impl<T: ::embedded_hal::digital::v2::InputPin<Error = core::convert::Infallible>> Button<T> {
    pub fn new(button: T) -> Self {
        Button {
            button,
            pressed: true,
        }
    }
    pub fn check(&mut self){
        self.pressed = !self.button.is_low().unwrap();
    }

    pub fn poll(&mut self, delay :&mut Delay) -> Event {
        let pressed_now = !self.button.is_low().unwrap();
        if !self.pressed  &&  pressed_now
        {
            delay.delay_ms(30 as u32);
            self.check();
            if !self.button.is_low().unwrap() {
                Event::Pressed
            }
            else {
                Event::Nothing
            }
        }
        else if self.pressed && !pressed_now{
            delay.delay_ms(30 as u32);
            self.check();
            if self.button.is_low().unwrap()
            {
                Event::Released
            }
            else {
                Event::Nothing
            }
        }
        else{
            Event::Nothing
        }
        
    }
}





extern crate alloc;
#[global_allocator]
static ALLOCATOR: esp_alloc::EspHeap = esp_alloc::EspHeap::empty();

fn init_heap() {
    const HEAP_SIZE: usize = 32 * 1024;

    extern "C" {
        static mut _heap_start: u32;
    }

    unsafe {
        let heap_start = &_heap_start as *const _ as usize;
        ALLOCATOR.init(heap_start as *mut u8, HEAP_SIZE);
    }
}
 


#[entry]
fn main() -> ! {
    let peripherals = Peripherals::take().unwrap();

    let mut system = peripherals.DPORT.split();

    let mut clocks = ClockControl::boot_defaults(system.clock_control).freeze();

    // Disable the RTC and TIMG watchdog timers
    let mut rtc = Rtc::new(peripherals.RTC_CNTL);
    let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks);
    let mut wdt0 = timer_group0.wdt;
    let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks);
    let mut wdt1 = timer_group1.wdt;
    
    rtc.rwdt.disable();
    wdt0.disable();
    wdt1.disable();


    println!("About to initialize the SPI LED driver ILI9341");
    let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
    

    /* Set corresponding pins */
 
    let mosi = io.pins.gpio23;
    let cs = io.pins.gpio15;
    let rst = io.pins.gpio4;
    let dc = io.pins.gpio2;
    let sck = io.pins.gpio18;
    let miso = io.pins.gpio8;
    let backlight = io.pins.gpio17;

    
    /* Then set backlight (set_low() - display lights up when signal is in 0, set_high() - opposite case(for example.)) */
    let mut backlight = backlight.into_push_pull_output();
    backlight.set_high().unwrap();


    let mut descriptors = [0u32; 8 * 3];
    let mut rx_descriptors = [0u32; 8 * 3];

    println!("Initializing dma...");
    let dma = Dma::new(system.dma, &mut system.peripheral_clock_control);
    let dma_channel = dma.spi2channel;

    let mut descriptors = [0u32; 8 * 3];
    let mut rx_descriptors = [0u32; 8 * 3];


    /* Configure SPI */
    let mut spi = Spi::new(
        peripherals.SPI2,
        sck,
        mosi,
        miso,
        cs,
        100u32.kHz(),
        SpiMode::Mode0,
        &mut system.peripheral_clock_control,
        &clocks,
    )
    .with_dma(dma_channel.configure(
        false,
        &mut descriptors,
        &mut rx_descriptors,
        DmaPriority::Priority0,
    ));


    println!("SPI with DMA initialized!");

   let di = SPIInterfaceNoCS::new(spi, dc.into_push_pull_output());

    println!("SPI interfase with no CS initialized!");
    let reset = rst.into_push_pull_output();
    let mut delay = Delay::new(&clocks);
    println!("About to init ILI9341 display!");

    let mut display = Ili9341::new(
        di,
        reset, 
        &mut delay, 
        KalugaOrientation::LandscapeFlipped, 
        DisplaySize240x320
        ).unwrap();
        
    println!("Initialized");

    // display.clear(Rgb565::WHITE).unwrap();

    // let mut button_green = Button::new(io.pins.gpio0.into_pull_up_input());
    // let mut button_blue  = Button::new(io.pins.gpio1.into_pull_up_input());

 
    // Text::with_text_style("Press GREEN button",
    //         display.bounding_box().center() - Size::new(0, 35), 
    //         MonoTextStyle::new(&PROFONT_18_POINT, Rgb565::BLACK),
    //         textStyle,
    // )
    // .draw(&mut display)
    // .unwrap();


    // Text::with_text_style("Press BLUE button",
    //         display.bounding_box().center() + Size::new(0, 25), 
    //         MonoTextStyle::new(&PROFONT_18_POINT, Rgb565::BLACK),
    //         textStyle,
    // )
    // .draw(&mut display)
    // .unwrap();

    // let mut green_cnt = 0;
    // let mut blue_cnt = 0;

    // let mut last_pressed_blue : bool = false;

    loop {



    //     if let Event::Pressed = button_green.poll(&mut delay)
    //     {
    //         green_cnt += 1;

            
    //         Rectangle::with_center(display.bounding_box().center() - Size::new(0, 30), Size::new(270, 30))
    //         .into_styled(
    //             PrimitiveStyleBuilder::new()
    //                 .fill_color(Rgb565::WHITE)
    //                 .stroke_color(Rgb565::WHITE)
    //                 .stroke_width(1)
    //                 .build(),
    //         )
    //         .draw(&mut display);

    //             Text::with_text_style("Green button pressed!",
    //                 display.bounding_box().center() - Size::new(0, 35), 
    //                 MonoTextStyle::new(&PROFONT_18_POINT, Rgb565::CSS_GREEN),
    //                 textStyle,
    //             )
    //             .draw(&mut display)
    //             .unwrap();

    //         println!("Green! (x{})", green_cnt);

    //         if last_pressed_blue {
    //             Rectangle::with_center(display.bounding_box().center() + Size::new(0, 30), Size::new(270, 30))
    //                 .into_styled(
    //                     PrimitiveStyleBuilder::new()
    //                         .fill_color(Rgb565::WHITE)
    //                         .stroke_color(Rgb565::WHITE)
    //                         .stroke_width(1)
    //                         .build(),
    //                 )
    //                 .draw(&mut display);
                

    //             Text::with_text_style("Press BLUE button",
    //                 display.bounding_box().center() + Size::new(0, 25), 
    //                 MonoTextStyle::new(&PROFONT_18_POINT, Rgb565::BLACK),
    //                 textStyle,
    //             )
    //             .draw(&mut display)
    //             .unwrap();
    //         }
    //         last_pressed_blue = false;
    //     }
    //     if let Event::Pressed = button_blue.poll(&mut delay)
    //     {
    //         blue_cnt += 1;

    //             Rectangle::with_center(display.bounding_box().center() + Size::new(0, 30), Size::new(270, 30))
    //             .into_styled(
    //                 PrimitiveStyleBuilder::new()
    //                     .fill_color(Rgb565::WHITE)
    //                     .stroke_color(Rgb565::WHITE)
    //                     .stroke_width(1)
    //                     .build(),
    //             )
    //             .draw(&mut display);
            
    //             Text::with_text_style("Blue button pressed!",
    //                         display.bounding_box().center() + Size::new(0, 25), 
    //                         MonoTextStyle::new(&PROFONT_18_POINT, Rgb565::BLUE),
    //                         textStyle,
    //             )
    //             .draw(&mut display)
    //             .unwrap();
            
    //         println!("Blue! (x{})", blue_cnt);

    //         if !last_pressed_blue {

    //             Rectangle::with_center(display.bounding_box().center() - Size::new(0, 30), Size::new(270, 30))
    //             .into_styled(
    //                 PrimitiveStyleBuilder::new()
    //                     .fill_color(Rgb565::WHITE)
    //                     .stroke_color(Rgb565::WHITE)
    //                     .stroke_width(1)
    //                     .build(),
    //             )
    //             .draw(&mut display);

    //             Text::with_text_style("Press GREEN button",
    //                 display.bounding_box().center() - Size::new(0, 35), 
    //                 MonoTextStyle::new(&PROFONT_18_POINT, Rgb565::BLACK),
    //                 textStyle,
    //             )
    //             .draw(&mut display)
    //             .unwrap();
    //         }
    //         last_pressed_blue = true;
    //     }
    }
}

fn buffer1() -> &'static mut [u8; 32000] {
    static mut BUFFER: [u8; 32000] = [0u8; 32000];
    unsafe { &mut BUFFER }
}

fn buffer2() -> &'static mut [u8; 32000] {
    static mut BUFFER: [u8; 32000] = [0u8; 32000];
    unsafe { &mut BUFFER }
}