/*
 * @(#)Bug421071Test.java
 *
 */

package org.mozilla.javascript.tests;

import junit.framework.TestCase;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.ContextFactory;
import org.mozilla.javascript.ImporterTopLevel;
import org.mozilla.javascript.Script;
import org.mozilla.javascript.Scriptable;

public class Bug421071Test extends TestCase {
    private ContextFactory factory;
    private TopLevelScope globalScope;
    private Script testScript;

    public void testProblemReplicator() throws Exception {
        // before debugging please put the breakpoint in the
        // NativeJavaPackage.getPkgProperty()
        // and observe names passed in there
        testScript = compileScript();
        runTestScript(); // this one does not get to the
                            // NativeJavaPackage.getPkgProperty() on my
                            // variables
        runTestScript(); // however this one does
    }

    private Script compileScript() {
        String scriptSource = "importPackage(java.util);\n"
                + "var searchmon = 3;\n"
                + "var searchday = 10;\n"
                + "var searchyear = 2008;\n"
                + "var searchwkday = 0;\n"
                + "\n"
                + "var myDate = Calendar.getInstance();\n // this is a java.util.Calendar"
                + "myDate.set(Calendar.MONTH, searchmon);\n"
                + "myDate.set(Calendar.DATE, searchday);\n"
                + "myDate.set(Calendar.YEAR, searchyear);\n"
                + "searchwkday.value = myDate.get(Calendar.DAY_OF_WEEK);";
        Script script;
        Context context = factory.enterContext();
        try {
            script = context.compileString(scriptSource, "testScript", 1, null);
            return script;
        } finally {
            Context.exit();
        }
    }

    private void runTestScript() throws InterruptedException {
        // will start new thread to get as close as possible to original
        // environment, however the same behavior is exposed using new
        // ScriptRunner(script).run();
        Thread thread = new Thread(new ScriptRunner(testScript));
        thread.start();
        thread.join();
    }

    static class DynamicScopeContextFactory extends ContextFactory {
        @Override
        public boolean hasFeature(Context cx, int featureIndex) {
            if (featureIndex == Context.FEATURE_DYNAMIC_SCOPE)
                return true;
            return super.hasFeature(cx, featureIndex);
        }
    }

    private TopLevelScope createGlobalScope() {
        factory = new DynamicScopeContextFactory();
        Context context = factory.enterContext();
        // noinspection deprecation
        TopLevelScope globalScope = new TopLevelScope(context);
        Context.exit();
        return globalScope;
    }

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        globalScope = createGlobalScope();
    }

    private class TopLevelScope extends ImporterTopLevel {
        private static final long serialVersionUID = 7831526694313927899L;

        public TopLevelScope(Context context) {
            super(context);
        }
    }

    private class ScriptRunner implements Runnable {
        private Script script;

        public ScriptRunner(Script script) {
            this.script = script;
        }

        public void run() {
            Context context = factory.enterContext();
            try {
                // Run each script in its own scope, to keep global variables
                // defined in each script separate
                Scriptable threadScope = context.newObject(globalScope);
                threadScope.setPrototype(globalScope);
                threadScope.setParentScope(null);
                script.exec(context, threadScope);
            } catch (Exception ee) {
                ee.printStackTrace();
            } finally {
                Context.exit();
            }
        }
    }
}
