#![no_std]
#![no_main]

mod game;
mod sprites;

use embassy_executor::Spawner;
use embassy_time::Timer;
use crate::game::{Game, GameState};
use esp_backtrace as _;
use esp_hal::{
    gpio::{Input, Pull},
    i2c::{self, master::I2c},
    prelude::*,
};
use log::info;
use ssd1306::mode::DisplayConfig;
use ssd1306::prelude::DisplayRotation;
use ssd1306::size::DisplaySize128x64;
use ssd1306::{I2CDisplayInterface, Ssd1306};

#[main]
async fn main(_spawner: Spawner) {
    let peripherals = esp_hal::init(esp_hal::Config::default());


    esp_println::logger::init_logger_from_env();

    let timg0 = esp_hal::timer::timg::TimerGroup::new(peripherals.TIMG0);
    esp_hal_embassy::init(timg0.timer0);

    // Setting up I2C send text to OLED display
    // let i2c = i2c::I2c::new_async(peripherals.I2C1, scl, sda, Irqs, i2c::Config::default());
    let i2c = I2c::new(peripherals.I2C0, i2c::master::Config::default())
        .with_sda(peripherals.GPIO21)
        .with_scl(peripherals.GPIO22);

    // Setup the OLED Display
    let interface = I2CDisplayInterface::new(i2c);
    let mut display = Ssd1306::new(interface, DisplaySize128x64, DisplayRotation::Rotate0)
        .into_buffered_graphics_mode();
    display.init().unwrap();
    display.flush().unwrap();

    let button = Input::new(peripherals.GPIO4, Pull::Up);

    let random_gen = RandomGen::new(esp_hal::rng::Rng::new(peripherals.RNG));
    let mut game = Game::new(random_gen, display);
    game.draw_trex().unwrap();
    let mut clicked_count = 0; // To restart the game when it two times button clicked

    info!("Starting Game!");

    loop {
        if game.state == GameState::GameOver {
            if button.is_low() {
                clicked_count += 1;
            }
            if clicked_count > 2 {
                clicked_count = 0;
                game = Game::new(game.obstacles.rng, game.display);
                Timer::after_millis(500).await;
            }
            Timer::after_millis(50).await;
            continue;
        }

        game.clear_screen().unwrap();
        game.draw_score().unwrap();

        if button.is_low() {
            game.trex_jump();
        }

        game.move_world().unwrap();
        game.draw_ground().unwrap();
        game.draw_trex().unwrap();

        if game.check_collison() {
            game.game_over().unwrap();
            game.display.flush().unwrap();
            Timer::after_millis(500).await;
            continue;
        }

        game.display.flush().unwrap();
        Timer::after_millis(5).await;
    }
}

pub trait Rng {
    fn random_u32(&mut self) -> u32;
}


// Helper struct for Random number generation
struct RandomGen {
    rng: esp_hal::rng::Rng,
}

impl RandomGen {
    fn new(rng: esp_hal::rng::Rng) -> Self {
        Self { rng }
    }
}

impl Rng for RandomGen {
    fn random_u32(&mut self) -> u32 {
        self.rng.random()
    }
}