#![no_std]
#![no_main]

extern crate user_lib;

use user_lib::*;

// 教学目标：
// 信号功能总回归：覆盖非法参数、用户处理函数、屏蔽/恢复、stop/cont、kill 特例等路径。

fn func() {
    println!("user_sig_test succsess");
    sigreturn();
}

fn func2() {
    loop {
        print!("");
    }
}

fn func3() {
    println!("interrupt");
    sigreturn();
}

fn user_sig_test_failsignum() {
    let mut new = SignalAction::default();
    let old = SignalAction::default();
    new.handler = func as usize;
    if sigaction(50.into(), &new, &old) >= 0 {
        panic!("Wrong sigaction but success!");
    }
}

fn user_sig_test_kill() {
    let mut new = SignalAction::default();
    let old = SignalAction::default();
    new.handler = func as usize;

    if sigaction(SignalNo::SIGUSR1, &new, &old) < 0 {
        panic!("Sigaction failed!");
    }
    if kill(getpid(), SignalNo::SIGUSR1) < 0 {
        println!("Kill failed!");
        exit(1);
    }
}

fn user_sig_test_multiprocsignals() {
    let pid = fork();
    if pid == 0 {
        let mut new = SignalAction::default();
        let old = SignalAction::default();
        new.handler = func as *const () as usize;
        if sigaction(SignalNo::SIGUSR1, &new, &old) < 0 {
            panic!("Sigaction failed!");
        }
    } else {
        if kill(pid, SignalNo::SIGUSR1) < 0 {
            println!("Kill failed!");
            exit(1);
        }
        let mut exit_code = 0;
        wait(&mut exit_code);
    }
}

fn user_sig_test_restore() {
    let mut new = SignalAction::default();
    let old = SignalAction::default();
    let old2 = SignalAction::default();
    new.handler = func as *const () as usize;

    if sigaction(SignalNo::SIGUSR1, &new, &old) < 0 {
        panic!("Sigaction failed!");
    }

    if sigaction(SignalNo::SIGUSR1, &old, &old2) < 0 {
        panic!("Sigaction failed!");
    }

    if old2.handler != new.handler {
        println!("Restore failed!");
        exit(-1);
    }
}

fn kernel_sig_test_ignore() {
    sigprocmask(1 << SignalNo::SIGSTOP as usize);
    if kill(getpid(), SignalNo::SIGSTOP) < 0 {
        println!("kill faild\n");
        exit(-1);
    }
}

fn kernel_sig_test_stop_cont() {
    let pid = fork();
    if pid == 0 {
        kill(getpid(), SignalNo::SIGSTOP);
        sleep(1000);
        exit(-1);
    } else {
        sleep(5000);
        kill(pid, SignalNo::SIGCONT);
        let mut exit_code = 0;
        wait(&mut exit_code);
    }
}

fn kernel_sig_test_failignorekill() {
    let mut new = SignalAction::default();
    let old = SignalAction::default();
    new.handler = func as *const () as usize;

    if sigaction(SignalNo::SIGKILL, &new, &old) >= 0 {
        panic!("Should not set sigaction to kill!");
    }

    if sigaction(SignalNo::SIGKILL, &new, 0 as *const SignalAction) >= 0 {
        panic!("Should not set sigaction to kill!");
    }

    if sigaction(SignalNo::SIGKILL, 0 as *const SignalAction, &old) >= 0 {
        panic!("Should not set sigaction to kill!");
    }
}

fn final_sig_test() {
    let mut new = SignalAction::default();
    let old = SignalAction::default();
    new.handler = func2 as *const () as usize;

    let mut new2 = SignalAction::default();
    let old2 = SignalAction::default();
    new2.handler = func3 as *const () as usize;

    let pid = fork();
    if pid == 0 {
        if sigaction(SignalNo::SIGUSR1, &new, &old) < 0 {
            panic!("Sigaction failed!");
        }
        if sigaction(14.into(), &new2, &old2) < 0 {
            panic!("Sigaction failed!");
        }
        if kill(getpid(), SignalNo::SIGUSR1) < 0 {
            println!("Kill failed!");
            exit(-1);
        }
    } else {
        sleep(1000);
        if kill(pid, 14.into()) < 0 {
            println!("Kill failed!");
            exit(-1);
        }
        sleep(1000);
        kill(pid, SignalNo::SIGKILL);
    }
}

fn run(f: fn()) -> bool {
    // 每个子测试独立 fork 运行，避免互相污染信号处理状态。
    let pid = fork();
    if pid == 0 {
        f();
        exit(0);
        unreachable!()
    } else {
        let mut exit_code: i32 = 0;
        wait(&mut exit_code);
        if exit_code != 0 {
            println!("FAILED!");
        } else {
            println!("OK!");
        }
        exit_code == 0
    }
}

#[unsafe(no_mangle)]
pub extern "C" fn main() -> i32 {
    let tests: [(fn(), &str); 8] = [
        (user_sig_test_failsignum, "user_sig_test_failsignum"),
        (user_sig_test_kill, "user_sig_test_kill"),
        (
            user_sig_test_multiprocsignals,
            "user_sig_test_multiprocsignals",
        ),
        (user_sig_test_restore, "user_sig_test_restore"),
        (kernel_sig_test_ignore, "kernel_sig_test_ignore"),
        (kernel_sig_test_stop_cont, "kernel_sig_test_stop_cont"),
        (
            kernel_sig_test_failignorekill,
            "kernel_sig_test_failignorekill",
        ),
        (final_sig_test, "final_sig_test"),
    ];
    let mut fail_num = 0;
    for test in tests {
        println!("Testing {}", test.1);
        if !run(test.0) {
            fail_num += 1;
        }
    }
    if fail_num == 0 {
        println!("ALL TESTS PASSED");
        0
    } else {
        println!("SOME TESTS FAILED");
        -1
    }
}
