#!/usr/bin/env qore
# -*- mode: qore; indent-tabs-mode: nil -*-

%new-style
%enable-all-warnings
%require-types
%strict-args

%requires ../../../../qlib/QUnit.qm

%exec-class BarerefTest

# Global test data
const closure = string sub () { return "global const closure"; };
const mclosure = string sub () { return "global const mclosure"; };

namespace Ns {
    our code vclosure;
}

public class BarerefTest inherits QUnit::Test {
    private {
        const const1 = "const1";
    }

    public {
        static string var1 = "var1";
        string mem2 = "mem2";

        code mclosure = string sub () { return "BarerefTest::mclosure member"; };
        static code vclosure = string sub () { return "BarerefTest::vclosure var"; };
        const closure = string sub () { return "BarerefTest::closure const"; };
    }

    constructor() : Test("BarerefTest", "1.0") {
        addTestCase("Closures test", \closuresTest());
        addTestCase("Skipped test", \lvarTest());
        addTestCase("Deref test", \derefTest());

        # Return for compatibility with test harness that checks return value.
        set_return_value(main());
    }

    list argTest(string str, any a) {
        return (str, a);
    }
    static string t1() {
        return "t1";
    }

    closuresTest() {
        Ns::vclosure = string sub () { return "Ns var vclosure"; };
        our code ::vclosure = string sub () { return "global var vclosure"; };

        testAssertion( "static method call 1", \t1(), NOTHING, new TestResultValue( "t1"));
        my code func = \t1();
        testAssertion( "static method call 2", func, NOTHING, new TestResultValue( "t1"));

        testAssertion( "static method call 3", \BarerefTest::t1(), NOTHING, new TestResultValue( "t1"));
        func = \BarerefTest::t1();
        testAssertion( "static method call 4", func, NOTHING, new TestResultValue( "t1"));

        testAssertion("arg test 1", \argTest(), ("2", False), new TestResultValue(("2", False)));
        func = \argTest();
        testAssertion("arg test 2", func, ("True", 2001-01-01), new TestResultValue(("True", 2001-01-01)));

        testAssertion("arg test 3", \BarerefTest::argTest(), ("2", False), new TestResultValue(("2", False)));
        func = \BarerefTest::argTest();
        testAssertion("arg test 4", func, ("True", 2001-01-01), new TestResultValue(("True", 2001-01-01)));

        testAssertion("scope 1", mclosure, NOTHING, new TestResultValue( "BarerefTest::mclosure member"));
        func = mclosure;
        testAssertion("scope 2", func, NOTHING, new TestResultValue( "BarerefTest::mclosure member"));

        testAssertion("scope 3", ::mclosure, NOTHING, new TestResultValue( "global const mclosure"));
        func = ::mclosure;
        testAssertion("scope 4", func, NOTHING, new TestResultValue( "global const mclosure"));

        testAssertion("scope 5", closure, NOTHING, new TestResultValue( "BarerefTest::closure const"));
        func = closure;
        testAssertion("scope 6", func, NOTHING, new TestResultValue( "BarerefTest::closure const"));

        testAssertion("scope 7", BarerefTest::closure, NOTHING, new TestResultValue( "BarerefTest::closure const"));
        func = BarerefTest::closure;
        testAssertion("scope 8", func, NOTHING, new TestResultValue( "BarerefTest::closure const"));

        testAssertion("scope 9", ::closure, NOTHING, new TestResultValue( "global const closure"));
        func = ::closure;
        testAssertion("scope 10", func, NOTHING, new TestResultValue( "global const closure"));

        testAssertion("scope 11", vclosure, NOTHING, new TestResultValue( "BarerefTest::vclosure var"));
        func = vclosure;
        testAssertion("scope 12", func, NOTHING, new TestResultValue( "BarerefTest::vclosure var"));

        testAssertion("scope 13", BarerefTest::vclosure, NOTHING, new TestResultValue( "BarerefTest::vclosure var"));
        func = BarerefTest::vclosure;
        testAssertion("scope 14", func, NOTHING, new TestResultValue( "BarerefTest::vclosure var"));

        testAssertion("scope 15", ::vclosure, NOTHING, new TestResultValue( "global var vclosure"));
        func = ::vclosure;
        testAssertion("scope 16", func, NOTHING, new TestResultValue( "global var vclosure"));

        testAssertion("scope 17", Ns::vclosure, NOTHING, new TestResultValue( "Ns var vclosure"));
        func = Ns::vclosure;
        testAssertion("scope 18", func, NOTHING, new TestResultValue( "Ns var vclosure"));
    }

    lvarTest() {
        int a = 1;
        any b = "1";
        our any c = 2.1;
        my (int a1, string b1, any c1) = (2, "four", "hi");
        our (int a2, string b2, any c2) = (4, "eight", "hello");
        thread_local (int a3, string b3, auto c3) = (4, "eight", "hello");

        testAssertionValue("var test 1", a, 1);
        testAssertionValue("var test 2", b, "1");
        testAssertionValue("var test 3", c, 2.1);
        testAssertionValue("var test 4", a1, 2);
        testAssertionValue("var test 5", b1, "four");
        testAssertionValue("var test 6", c1, "hi");
        testAssertionValue("var test 7", a2, 4);
        testAssertionValue("var test 8", b2, "eight");
        testAssertionValue("var test 9", c2, "hello");
        testAssertionValue("var test 10", a3, 4);
        testAssertionValue("var test 11", b3, "eight");
        testAssertionValue("var test 12", c3, "hello");
    }

    derefTest() {
        string a = "b";
        hash h = {
            "a": "a",
            "b": "b",
        };

        assertEq("a", h.a);
        # the following line should use the lvar a's value "b" to dereference the hash
        # https://github.com/qorelanguage/qore/issues/416
        assertEq("b", h.(a));
    }
}
