use std::sync::mpsc::{channel, Sender};
use std::thread;
use std::thread::{sleep, JoinHandle, Thread};
use std::time::{Duration, Instant};
use crate::chip8::computer::Chip8Computer;
use crate::chip8::cpu_states::Chip8CpuStates::WaitingForInstruction;

pub enum ManagerDumpables {
    Video,
    Registers,
    Keyboard
}


pub struct Chip8ComputerManager {
    pub core_should_run: bool,
    pub one_step: bool,
    pub core_cycle_timer: bool,
    pub core_last_cycle_start: Instant,
    computer: Chip8Computer
}

impl Default for Chip8ComputerManager {
    fn default() -> Self {
        Chip8ComputerManager {
            core_should_run: false,
            one_step: true,
            core_cycle_timer: false,
            core_last_cycle_start: Instant::now()   ,
            computer: Chip8Computer::new()
        }
    }
}

impl Chip8ComputerManager {

    pub fn reset(&mut self) {
        self.computer.reset();
    }
    pub fn new() -> Chip8ComputerManager {
        let core_handle = thread::spawn(move || {
           loop {
               let start_time = Instant::now();
               println!("Core Thread starting at {start_time:?}");


               let sleep_time = Instant::now().duration_since(start_time).as_millis();
               println!("Core Thread sleeping for {sleep_time}ms");
               sleep(Duration::from_millis((16 - sleep_time) as u64));
           }
        });

        Chip8ComputerManager::default()
    }

    pub fn start(managed: &mut Chip8ComputerManager) {
        managed.core_should_run = true;
    }

    pub fn stop(managed: &mut Chip8ComputerManager) {
        managed.core_should_run = false
    }

    pub fn step(managed: &mut Chip8ComputerManager) {
        managed.one_step = true;
    }

    pub fn state(&mut self) -> &Chip8Computer {
        &self.computer
    }

    pub fn tick( &mut self) {
        println!("STARTING TICK");
        if self.one_step | self.core_should_run {
            self.core_last_cycle_start = Instant::now();
            self.computer.step_system();
            println!("SYSTEM STEP");
        };
        if self.one_step {
            println!("SYSTEM HALTED AFTER 1 STEP");
            // stop the CPU for the next cycle, we are only
            // wanting one step.
            self.one_step = false;
        }
    }

    pub fn press_key(&mut self, key_index: u8) {
        self.computer.keypad.push_key(key_index);
    }
    pub fn release_key(&mut self, key_index: u8) {
        self.computer.keypad.release_key(key_index);
    }

    pub fn sound(managed: &Chip8ComputerManager) -> bool {
        managed.computer.sound_timer.current() > 0
    }

    pub fn wait_for_instruction(&mut self) {
        self.computer.state = WaitingForInstruction;
    }

    pub fn is_key_pressed(&self, key_index: u8) -> bool {
        self.computer.keypad.pressed(key_index)
    }

    pub fn num_cycles(&self) -> i32 {
        self.computer.num_cycles
    }

    pub fn load_bytes_to_system_memory(&mut self, bytes_to_load: Vec<u8>) {
        self.computer.load_bytes_to_memory(0x200, &bytes_to_load);
    }

    pub fn dump_to_string(&self, dump_type: ManagerDumpables) -> String {
        match dump_type {
            ManagerDumpables::Video => {
                self.computer.video_memory.format_as_string()
            }
            ManagerDumpables::Registers => {
                self.computer.registers.format_as_string()
            }
            ManagerDumpables::Keyboard => {
                self.computer.keypad.format_as_string()
            }
        }
    }
}