/*
 * Copyright (c) 2002-2020 Gargoyle Software Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.gargoylesoftware.htmlunit.javascript.regexp;

import org.junit.Test;
import org.junit.runner.RunWith;

import com.gargoylesoftware.htmlunit.BrowserRunner;
import com.gargoylesoftware.htmlunit.SimpleWebTestCase;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.gargoylesoftware.htmlunit.javascript.JavaScriptEngine;

import net.sourceforge.htmlunit.corejs.javascript.Context;
import net.sourceforge.htmlunit.corejs.javascript.ContextFactory;
import net.sourceforge.htmlunit.corejs.javascript.JavaScriptException;
import net.sourceforge.htmlunit.corejs.javascript.ScriptableObject;

/**
 * Tests for {@link HtmlUnitRegExpProxy}.
 *
 * @author Marc Guillemot
 * @author Ahmed Ashour
 * @author Carsten Steul
 */
@RunWith(BrowserRunner.class)
public class HtmlUnitRegExpProxy2Test extends SimpleWebTestCase {

    private static final String str_ = "(?:<script.*?>)((\\n|\\r|.)*?)(?:<\\/script>)";
    private static final String begin_ = "<div>bla</div>";
    private static final String end_ = "foo\n<span>bla2</span>";
    private static final String text_ = begin_ + "<script>var a = 123;</script>" + end_;
    private static final String expected_ = begin_ + end_;
    private static final String src_ = "var re = new RegExp(str, 'img');\n"
        + "var s = text.replace(re, '');\n"
        + "if (s != expected)\n"
        + "  throw 'Expected >' + expected + '< but got >' + s + '<';";

    private static final String scriptTestMatch_ = "function arrayToString(_arr) {\n"
            + "  if (_arr == null) return null;\n"
            + "  var s = '[';\n"
            + "  for (var i = 0; i < _arr.length; i++)\n"
            + "  {\n"
            + "    if (i != 0) s += ', ';\n"
            + "    s += _arr[i];\n"
            + "  }\n"
            + "  s += ']';\n"
            + "  return s;\n"
            + "}\n"
            + "function assertArrEquals(actual, expected) {\n"
            + "  if (expected == null) {\n"
            + "    if (actual != null)\n"
            + "      throw 'Expected >null< got >' + actual + '<';\n"
            + "    else return;\n"
            + "  }\n"
            + "  var expectedStr = arrayToString(expected);\n"
            + "  var actualStr = arrayToString(actual);\n"
            + "  if (expectedStr != actualStr)\n"
            + "    throw 'Expected >' + expectedStr + '< got >' + actualStr + '<';\n"
            + "}\n"
            + "assertArrEquals('ab'.match(), null);\n"
            + "assertArrEquals('ab'.match('foo'), null);\n"
            + "assertArrEquals('ab'.match('a'), ['a']);\n"
            + "assertArrEquals('abab'.match('a'), ['a']);\n"
            + "assertArrEquals('abab'.match('.a'), ['ba']);\n"
            + "assertArrEquals('abab'.match(/.a/), ['ba']);\n"
            + "assertArrEquals('li'.match(/^([^a-z0-9_-])?([a-z0-9_-]+)(.*)/i), ['li', undefined, 'li', '']);\n"
            + "assertArrEquals('abab'.match(new RegExp('.a')), ['ba']);\n"
            + "var s = '<script>var a = 1;</' + 'script>';\n"
            + "var re = '(?:<script.*?>)((\\n|\\r|.)*?)(?:<\\/script>)';\n"
            + "assertArrEquals(s.match(re), [s, 'var a = 1;', ';']);\n";

    /**
     * Test that string.replace works correctly (?) in HtmlUnit.
     * @throws Exception if the test fails
     */
    @Test
    public void fixedInHtmlUnit() throws Exception {
        final String html = "<html></html>";
        final HtmlPage page = loadPage(html);
        final ScriptableObject topScope = page.getEnclosingWindow().getScriptableObject();
        topScope.put("str", topScope, str_);
        topScope.put("text", topScope, text_);
        topScope.put("expected", topScope, expected_);
        page.executeJavaScript(src_);
    }

    /**
     * Tests if custom patch is still needed.
     */
    @Test
    public void needCustomFix() {
        final WebClient client = getWebClient();
        final ContextFactory cf = ((JavaScriptEngine) client.getJavaScriptEngine()).getContextFactory();
        final Context ctx = cf.enterContext();
        try {
            final ScriptableObject topScope = ctx.initStandardObjects();
            topScope.put("str", topScope, str_);
            topScope.put("text", topScope, text_);
            topScope.put("expected", topScope, expected_);
            assertEquals(begin_ + end_, text_.replaceAll(str_, ""));
            try {
                ctx.evaluateString(topScope, src_, "test script", 0, null);
            }
            catch (final JavaScriptException e) {
                assertTrue(e.getMessage().indexOf("Expected >") == 0);
            }
        }
        finally {
            Context.exit();
        }
    }

    /**
     * Test if the custom fix is needed or not. When this test fails, then it means that the problem is solved in
     * Rhino and that custom fix for String.match in {@link HtmlUnitRegExpProxy} is not needed anymore (and that
     * this test can be removed, or turned positive).
     * @throws Exception if the test fails
     */
    @Test
    public void matchFixNeeded() throws Exception {
        final WebClient client = getWebClient();
        final ContextFactory cf = ((JavaScriptEngine) client.getJavaScriptEngine()).getContextFactory();
        final Context cx = cf.enterContext();
        try {
            final ScriptableObject topScope = cx.initStandardObjects();
            cx.evaluateString(topScope, scriptTestMatch_, "test script String.match", 0, null);
            try {
                cx.evaluateString(topScope, scriptTestMatch_, "test script String.match", 0, null);
            }
            catch (final JavaScriptException e) {
                assertTrue(e.getMessage().indexOf("Expected >") == 0);
            }
        }
        finally {
            Context.exit();
        }
    }

    /**
     * @throws Exception if the test fails
     */
    @Test
    public void emptySubStringChanged() throws Exception {
        final String html = "<html></html>";
        final HtmlPage page = loadPage(html);
        page.executeJavaScript("'alpha'.replace(/alpha/, '');/beta/.test('abc beta def');");
    }
}