//! Ursprüngliche Reproduktionen: fünf behobene Befunde und eine Kontrollprobe.
//! Separater Cargo-Treiber; siehe review.md. Alle sechs Prüfungen müssen bestehen.
use tb_frontend::forms::{FormCatalog, ObjectClass};
use tb_runtime::host::{CaptureHost, Ereignis, Host, MausArt, MausEreignis};
use tb_runtime::screen::TextScreen;
use tb_runtime::value::Value;
use tb_vm::interp::{RunEvent, Vm};

fn vm(src: &str) -> Vm {
    let mut catalog = FormCatalog::default();
    catalog.add("Form1", ObjectClass::Form, None, false);
    catalog.add("Text1", ObjectClass::TextBox, Some("Form1"), false);
    catalog.add("Text2", ObjectClass::TextBox, Some("Form1"), false);
    catalog.add("Timer1", ObjectClass::Timer, Some("Form1"), false);
    catalog.add("Menu1", ObjectClass::Menu, Some("Form1"), false);
    catalog.add("Item1", ObjectClass::Menu, Some("Menu1"), false);
    Vm::new(
        tb_vm::compile_source_with_forms("FORM1", src, &catalog)
            .unwrap_or_else(|d| panic!("{d:?}")),
    )
}

fn stop(vm: &mut Vm, host: &mut dyn Host) {
    assert!(matches!(vm.run(host), RunEvent::Stopped { .. }));
}

fn string(vm: &Vm, name: &str) -> String {
    match vm.inspect(name) {
        Some(Value::Str(s)) => s.to_string(),
        other => panic!("{other:?}"),
    }
}

fn key(s: &str) -> Ereignis {
    Ereignis::Taste(s.into(), 0)
}
fn mouse() -> Ereignis {
    Ereignis::Maus(MausEreignis {
        art: MausArt::Druck,
        taste: 1,
        shift: 0,
        zeile: 1,
        spalte: 1,
    })
}

#[test]
fn review_signal_waehrend_blockierender_eingabe() {
    struct Input {
        reads: usize,
    }
    impl Host for Input {
        fn present(&mut self, _: &TextScreen) {}
        fn next_event(&mut self, block: bool) -> Option<Ereignis> {
            if !block {
                return None;
            }
            self.reads += 1;
            Some(if self.reads == 1 {
                Ereignis::Signal(2)
            } else {
                Ereignis::Ende
            })
        }
        fn jetzt_ms(&mut self) -> u64 {
            0
        }
        fn warten(&mut self, _: Option<u64>) -> Option<Ereignis> {
            self.next_event(true)
        }
    }
    let mut results = vec![];
    for statement in ["INPUT s$", "LINE INPUT s$", "s$ = INPUT$(1)"] {
        let src =
            format!("ON SIGNAL(2) GOSUB H\nSIGNAL(2) ON\n{statement}\nEND\nH:\nPRINT \"H\"\nEND");
        let mut vm = Vm::new(tb_vm::compile_source("TEST", &src).unwrap());
        let result = vm.run(&mut Input { reads: 0 });
        println!(
            "{statement}: {result:?}; Ausgabe={:?}",
            tb_runtime::snapshot::text(&vm.rt.screen)
        );
        results.push(result);
    }
    assert!(results.iter().all(|result| *result == RunEvent::Ended));
}

#[test]
fn review_objektargument_darf_gotfocus_nicht_vorziehen() {
    let mut vm = vm("DIM SHARED s$\nText1.TabIndex = 0\nText2.TabIndex = 1\nForm1.Show\nText1.SetFocus\nSTOP\nDOEVENTS\nEND\nSUB Text1_LostFocus()\nSHARED s$\nCALL Mark(Text1)\ns$ = s$ + \"l\"\nEND SUB\nSUB Mark(c AS CONTROL)\nSHARED s$\ns$ = s$ + \"L\"\nEND SUB\nSUB Text2_GotFocus()\nSHARED s$\ns$ = s$ + \"G\"\ns$ = s$ + \"g\"\nEND SUB");
    let mut host = CaptureHost::default();
    stop(&mut vm, &mut host);
    host.ereignis(key("\t"));
    assert_eq!(vm.run(&mut host), RunEvent::Ended);
    assert_eq!(string(&vm, "s"), "LlGg");
}

#[test]
fn review_gleichzeitig_key_und_uevent_behalten_prioritaet() {
    let mut vm = vm("ON KEY(15) GOSUB K\nKEY 15, CHR$(0) + CHR$(20)\nKEY(15) ON\nON UEVENT GOSUB U\nUEVENT ON\nSTOP\nDOEVENTS\nEND\nK:\ns$ = s$ + \"K\"\nRETURN\nU:\ns$ = s$ + \"U\"\nRETURN");
    let mut host = CaptureHost::default();
    stop(&mut vm, &mut host);
    host.ereignis(key("t"));
    vm.rt.pump(&mut host, false);
    vm.rt.traps.melden(tb_runtime::traps::Quelle::UEvent);
    assert_eq!(vm.run(&mut host), RunEvent::Ended);
    assert_eq!(string(&vm, "s"), "KU");
}

#[test]
fn review_modale_eingabe_vor_eof_wird_zugestellt() {
    let mut vm = vm("DIM SHARED s$\nForm1.Show 1\nEND\nSUB Form_MouseDown(Button AS INTEGER, Shift AS INTEGER, X AS SINGLE, Y AS SINGLE)\nSHARED s$\ns$ = \"M\"\nUNLOAD Form1\nEND SUB");
    let mut host = CaptureHost::default();
    host.ereignis_nach(2, mouse());
    host.ereignis_nach(2, Ereignis::Ende);
    assert_eq!(vm.run(&mut host), RunEvent::Ended);
    assert_eq!(string(&vm, "s"), "M");
}

#[test]
fn review_menu_schliessen_stellt_pending_trap_vor_erneutem_warten_zu() {
    struct MenuHost {
        time: u64,
        waits: usize,
    }
    impl Host for MenuHost {
        fn present(&mut self, _: &TextScreen) {}
        fn next_event(&mut self, _: bool) -> Option<Ereignis> {
            None
        }
        fn jetzt_ms(&mut self) -> u64 {
            self.time
        }
        fn warten(&mut self, deadline: Option<u64>) -> Option<Ereignis> {
            self.waits += 1;
            if self.waits == 1 {
                return Some(key("\u{1b}"));
            }
            self.time = deadline.unwrap();
            None
        }
    }
    let mut vm = vm("ON UEVENT GOSUB H\nUEVENT ON\nMenu1.Caption = \"&Datei\"\nItem1.Caption = \"&Ende\"\nForm1.Show\nSTOP\nSLEEP 60\nEND\nH:\ns$ = \"U\"\nRETURN");
    let mut host = CaptureHost::default();
    stop(&mut vm, &mut host);
    vm.forms.handle_key("d", 4);
    vm.rt.traps.melden(tb_runtime::traps::Quelle::UEvent);
    let mut host = MenuHost { time: 0, waits: 0 };
    assert_eq!(vm.run(&mut host), RunEvent::Ended);
    println!(
        "Menü/SLEEP: Zeit={}, Warteaufrufe={}, Handler={}",
        host.time,
        host.waits,
        string(&vm, "s")
    );
    assert_eq!(host.waits, 1);
}

#[test]
fn review_maus_vor_getrappter_taste() {
    let mut vm = vm("DIM SHARED s$\nON KEY(15) GOSUB H\nKEY 15, CHR$(0) + CHR$(20)\nKEY(15) ON\nForm1.Show\nSTOP\nDOEVENTS\nDOEVENTS\nEND\nH:\ns$ = s$ + \"K\"\nRETURN\nSUB Form_MouseDown(Button AS INTEGER, Shift AS INTEGER, X AS SINGLE, Y AS SINGLE)\nSHARED s$\ns$ = s$ + \"M\"\nEND SUB");
    let mut host = CaptureHost::default();
    stop(&mut vm, &mut host);
    host.ereignis(mouse());
    host.ereignis(key("t"));
    assert_eq!(vm.run(&mut host), RunEvent::Ended);
    assert_eq!(string(&vm, "s"), "MK");
}
