// Pattern Matching — enum tags, payload bindings, literals, ranges, wildcard
import Std::Io::{PrintLine, PrintInt};
import Std::Test::{Test_AssertEqInt, Test_Pass};

enum Option {
    Some(int),
    None
}

enum Msg {
    Quit,
    Move(int),
    Write(String)
}

func GetValue(opt: Option) -> int {
    // Pattern binding: value is bound from Option::Some payload
    match opt {
        Option::Some(value) => value,
        Option::None => 0
    }
}

func DoubleSome(opt: Option) -> int {
    match opt {
        Option::Some(n) => n + n,
        Option::None => -1
    }
}

func MsgCode(m: Msg) -> int {
    match m {
        Msg::Quit => 0,
        Msg::Move(x) => x,
        Msg::Write(s) => 1
    }
}

func Classify(n: int) -> int {
    // literal arms + exclusive range + inclusive range + wildcard
    match n {
        0 => 100,
        1 => 101,
        2..5 => 200,
        6..=10 => 300,
        _ => -1
    }
}

func ColorName(c: int) -> String {
    match c {
        1 => "red",
        2 => "green",
        3 => "blue",
        _ => "unknown"
    }
}

func Main() -> int {
    let opt1: Option = Option { tag: Option_Some };
    opt1.data.Some_0 = 42;

    let opt2: Option = Option { tag: Option_None };

    Test_AssertEqInt(GetValue(opt1), 42);
    Test_AssertEqInt(GetValue(opt2), 0);
    Test_AssertEqInt(DoubleSome(opt1), 84);
    Test_AssertEqInt(DoubleSome(opt2), -1);

    let m: Msg = Msg { tag: Msg_Move };
    m.data.Move_0 = 7;
    Test_AssertEqInt(MsgCode(m), 7);

    PrintLine("opt1 value: ");
    PrintInt(GetValue(opt1));
    PrintLine("");

    PrintLine("opt2 value: ");
    PrintInt(GetValue(opt2));
    PrintLine("");

    PrintLine("classify:");
    PrintInt(Classify(0));
    PrintLine("");
    PrintInt(Classify(3));
    PrintLine("");
    PrintInt(Classify(8));
    PrintLine("");
    PrintInt(Classify(99));
    PrintLine("");

    PrintLine("color:");
    PrintLine(ColorName(2));

    Test_Pass("pattern_matching");
    return 0;
}
