use crate::{system::*, alu::*, printer::*, keyboard::*, halt::*};
use std::sync::mpsc;

pub struct SystemBuilder {
    halt_component: bool,
    cli_keyboard: bool,
    keyboard_script: Option<Vec<String>>,
    cli_printer: bool,
    p101_alu: bool
}

impl SystemBuilder {
    pub fn new () -> Self {
        Self {
            halt_component: false,
            cli_keyboard: false,
            keyboard_script: None,
            cli_printer: false,
            p101_alu: false
        }
    }

    pub fn with_halt_component(mut self) -> Self {
        self.halt_component = true;
        self
    }

    pub fn with_cli_keyboard(mut self) -> Self {
        self.cli_keyboard = true;
        self
    }

    pub fn with_script_keyboard(mut self, script: Vec<String>) -> Self {
        self.keyboard_script = Some(script);
        self
    }

    pub fn with_cli_printer(mut self) -> Self {
        self.cli_printer = true;
        self
    }

    pub fn with_p101_alu(mut self) -> Self {
        self.p101_alu = true;
        self
    }

    pub fn build(self) -> (mpsc::Sender<Message>, System) {
        let (tx, rx) = mpsc::channel();
        let mut sys = System::new(rx);

        if self.halt_component {
            sys.register_component(Self::create_halt_component(tx.clone()));
        }

        if self.cli_keyboard {
            sys.register_component(Self::create_cli_keyboard_component(tx.clone()));
        }

        if self.keyboard_script.is_some() {
            sys.register_component(Self::create_script_keyboard_component(tx.clone(), self.keyboard_script.unwrap()));
        }

        if self.cli_printer {
            sys.register_component(Self::create_cli_printer_component(tx.clone()));
        }

        if self.p101_alu {
            sys.register_component(Self::create_p101_alu(tx.clone()));
        }

        (tx, sys)
    }

    fn create_p101_alu(tx: mpsc::Sender<Message>) -> Box<AluComponent<DefaultAlu>> {
        let alu_provider = DefaultAlu::new(tx.clone());
        let component = AluComponent::new(alu_provider, tx);
        Box::new(component)
    }

    fn create_cli_printer_component(tx: mpsc::Sender<Message>) -> Box<PrinterComponent<CliPrinter>> {
        let provider = CliPrinter::new();
        let component = PrinterComponent::new(provider, tx);
        Box::new(component)
    }

    fn create_halt_component(tx: mpsc::Sender<Message>) -> Box<HaltComponent> {
        let component = HaltComponent::new(tx);
        Box::new(component)
    }

    fn create_cli_keyboard_component(tx: mpsc::Sender<Message>) -> Box<KeyboardComponent<CliKeyboard>> {
        let provider = CliKeyboard::new();
        let component = KeyboardComponent::new(provider, tx);
        Box::new(component)
    }

    fn create_script_keyboard_component(tx: mpsc::Sender<Message>, script: Vec<String>) -> Box<KeyboardComponent<ScriptKeyboard>> {
        let provider = ScriptKeyboard::new(script);
        let component = KeyboardComponent::new(provider, tx);
        Box::new(component)
    }
}

impl Default for SystemBuilder {
    fn default() -> Self {
        Self::new()
    }
}

