/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.javascript.tests;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;

import java.util.HashMap;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.mozilla.javascript.BaseFunction;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.NativeArray;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.Undefined;

public class UndefinedOrNullThisInFunctionCallOrApplyTest {

    private Context cx;

    private BaseFunction function;

    @Before
    public void setUp() throws Exception {
        cx = Context.enter();
        function =
                new BaseFunction() {
                    @Override
                    public Object call(
                            Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
                        System.out.println("QWE");
                        return thisObj;
                    }
                };
    }

    @After
    public void tearDown() throws Exception {
        Context.exit();
    }

    @Test
    @Ignore
    public void whenVersionGt17ThenPassNullAsThisObjJavaFunc() {
        HashMap<String, Scriptable> bindings = new HashMap<String, Scriptable>();
        bindings.put("F2", function);

        cx.setLanguageVersion(Context.VERSION_1_8);
        NativeArray arr =
                (NativeArray) Evaluator.eval("[this, F2.apply(), F2.apply(undefined)];", bindings);

        assertNotEquals(arr.get(0), arr.get(1));
        assertNotEquals(arr.get(0), arr.get(2));
        assertEquals(arr.get(1), arr.get(2));

        arr = (NativeArray) Evaluator.eval("[this, F2.apply(), F2.apply(null)];", bindings);
        assertNotEquals(arr.get(0), arr.get(1));
        assertNotEquals(arr.get(0), arr.get(2));
        assertEquals(arr.get(1), arr.get(2));

        cx.setLanguageVersion(Context.VERSION_ES6);
        arr = (NativeArray) Evaluator.eval("[this, F2.apply(), F2.apply(undefined)];", bindings);

        assertNotEquals(arr.get(0), arr.get(1));
        assertNotEquals(arr.get(0), arr.get(2));
        assertEquals(arr.get(1), arr.get(2));

        arr = (NativeArray) Evaluator.eval("[this, F2.apply(), F2.apply(null)];", bindings);
        assertNotEquals(arr.get(0), arr.get(1));
        assertNotEquals(arr.get(0), arr.get(2));
        assertEquals(arr.get(1), arr.get(2));
    }

    @Test
    @Ignore
    public void whenVersionLtEq17ThenPassGlobalThisObjJavaFunc() {
        HashMap<String, Scriptable> bindings = new HashMap<String, Scriptable>();
        bindings.put("F2", function);

        cx.setLanguageVersion(Context.VERSION_1_7);
        NativeArray arr =
                (NativeArray)
                        Evaluator.eval(
                                "{return this;};[this, F2.apply(), F2.apply(undefined)];",
                                bindings);

        assertEquals(arr.get(0), arr.get(1));
        assertEquals(arr.get(0), arr.get(2));
        assertEquals(arr.get(1), arr.get(2));

        arr = (NativeArray) Evaluator.eval("[this, F2.apply(), F2.apply(null)];", bindings);

        assertEquals(arr.get(0), arr.get(1));
        assertEquals(arr.get(0), arr.get(2));
        assertEquals(arr.get(1), arr.get(2));
    }

    @Test
    public void whenVersionGt17ThenPassNullAsThisObjForApplyJS() {
        cx.setLanguageVersion(Context.VERSION_1_8);
        NativeArray arr =
                (NativeArray)
                        Evaluator.eval(
                                cx,
                                "function F2() {return this;};[this, F2.apply(), F2.apply(undefined)];");

        assertNotEquals(arr.get(0), arr.get(1));
        assertNotEquals(arr.get(0), arr.get(2));
        assertNotEquals(arr.get(1), arr.get(2));
        assertEquals(Undefined.instance, arr.get(2));
        assertEquals(Undefined.SCRIPTABLE_UNDEFINED, arr.get(2));

        arr =
                (NativeArray)
                        Evaluator.eval(
                                cx,
                                "function F2() {return this;};[this, F2.apply(), F2.apply(null)];");
        assertNotEquals(arr.get(0), arr.get(1));
        assertNotEquals(arr.get(0), arr.get(2));
        assertEquals(arr.get(1), arr.get(2));

        cx.setLanguageVersion(Context.VERSION_ES6);
        arr =
                (NativeArray)
                        Evaluator.eval(
                                cx,
                                "function F2() {return this;};[this, F2.apply(), F2.apply(undefined)];");

        assertNotEquals(arr.get(0), arr.get(1));
        assertNotEquals(arr.get(0), arr.get(2));
        assertNotEquals(arr.get(1), arr.get(2));
        assertEquals(Undefined.instance, arr.get(2));
        assertEquals(Undefined.SCRIPTABLE_UNDEFINED, arr.get(2));

        arr =
                (NativeArray)
                        Evaluator.eval(
                                cx,
                                "function F2() {return this;};[this, F2.apply(), F2.apply(null)];");
        assertNotEquals(arr.get(0), arr.get(1));
        assertNotEquals(arr.get(0), arr.get(2));
        assertEquals(arr.get(1), arr.get(2));
    }

    @Test
    public void whenVersionLtEq17ThenPassGlobalThisObjForApplyJS() {
        cx.setLanguageVersion(Context.VERSION_1_7);
        NativeArray arr =
                (NativeArray)
                        Evaluator.eval(
                                cx,
                                "function F2() {return this;};[this, F2.apply(), F2.apply(undefined)];");

        assertEquals(arr.get(0), arr.get(1));
        assertEquals(arr.get(0), arr.get(2));
        assertEquals(arr.get(1), arr.get(2));

        arr =
                (NativeArray)
                        Evaluator.eval(
                                cx,
                                "function F2() {return this;};[this, F2.apply(), F2.apply(null)];");

        assertEquals(arr.get(0), arr.get(1));
        assertEquals(arr.get(0), arr.get(2));
        assertEquals(arr.get(1), arr.get(2));
    }

    @Test
    public void whenVersionGt17ThenPassNullAsThisObjForCallJS() {
        cx.setLanguageVersion(Context.VERSION_1_8);
        NativeArray arr =
                (NativeArray)
                        Evaluator.eval(
                                cx,
                                "function F2() {return this;};[this, F2.call(), F2.call(undefined)];");

        assertNotEquals(arr.get(0), arr.get(1));
        assertNotEquals(arr.get(0), arr.get(2));
        assertNotEquals(arr.get(1), arr.get(2));
        assertEquals(Undefined.instance, arr.get(2));
        assertEquals(Undefined.SCRIPTABLE_UNDEFINED, arr.get(2));

        arr =
                (NativeArray)
                        Evaluator.eval(
                                cx,
                                "function F2() {return this;};[this, F2.call(), F2.call(null)];");
        assertNotEquals(arr.get(0), arr.get(1));
        assertNotEquals(arr.get(0), arr.get(2));
        assertEquals(arr.get(1), arr.get(2));

        cx.setLanguageVersion(Context.VERSION_ES6);
        arr =
                (NativeArray)
                        Evaluator.eval(
                                cx,
                                "function F2() {return this;};[this, F2.call(), F2.call(undefined)];");

        assertNotEquals(arr.get(0), arr.get(1));
        assertNotEquals(arr.get(0), arr.get(2));
        assertNotEquals(arr.get(1), arr.get(2));
        assertEquals(Undefined.instance, arr.get(2));
        assertEquals(Undefined.SCRIPTABLE_UNDEFINED, arr.get(2));

        arr =
                (NativeArray)
                        Evaluator.eval(
                                cx,
                                "function F2() {return this;};[this, F2.call(), F2.call(null)];");
        assertNotEquals(arr.get(0), arr.get(1));
        assertNotEquals(arr.get(0), arr.get(2));
        assertEquals(arr.get(1), arr.get(2));
    }

    @Test
    public void whenVersionLtEq17ThenPassGlobalThisObjForCallJS() {
        cx.setLanguageVersion(Context.VERSION_1_7);
        NativeArray arr =
                (NativeArray)
                        Evaluator.eval(
                                cx,
                                "function F2() {return this;};[this, F2.call(), F2.call(undefined)];");

        assertEquals(arr.get(0), arr.get(1));
        assertEquals(arr.get(0), arr.get(2));
        assertEquals(arr.get(1), arr.get(2));

        arr =
                (NativeArray)
                        Evaluator.eval(
                                cx,
                                "function F2() {return this;};[this, F2.call(), F2.call(null)];");

        assertEquals(arr.get(0), arr.get(1));
        assertEquals(arr.get(0), arr.get(2));
        assertEquals(arr.get(1), arr.get(2));
    }
}
