/**
 * JBoss, Home of Professional Open Source
 * Copyright 2011, Red Hat, Inc. and individual contributors
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.jboss.arquillian.ajocado.framework.internal;

import static org.apache.commons.lang3.StringEscapeUtils.escapeEcmaScript;
import static org.jboss.arquillian.ajocado.javascript.JavaScript.js;

import java.util.List;

import org.jboss.arquillian.ajocado.framework.GrapheneSelenium;
import org.jboss.arquillian.ajocado.framework.GrapheneSeleniumContext;
import org.jboss.arquillian.ajocado.framework.SeleniumExtensions;
import org.jboss.arquillian.ajocado.javascript.JavaScript;

/**
 * Defines the methods for loading the Selenium JS extensions to the Selenium Test Runner window.
 *
 * @author <a href="mailto:lfryc@redhat.com">Lukas Fryc</a>
 * @version $Revision$
 */
public class SeleniumExtensionsImpl implements SeleniumExtensions {

    /**
     * The associated AjaxSelenium object
     */
    GrapheneSelenium selenium = GrapheneSeleniumContext.getProxy();

    /*
     * JavaScript definitions for this object
     */
    final JavaScript getScriptWithResourceName = JavaScript.fromResource("javascript/get-script-with-resourcename.js");
    final JavaScript containsScriptWithResourceName = getScriptWithResourceName.append(" != null");
    final JavaScript getIdForScriptWithResourceName = getScriptWithResourceName.append(".getAttribute('id')");
    final JavaScript setResourceNameForId = js("document.getElementById('{0}').setAttribute('resourceName', '{1}')");
    final JavaScript removeScript = js("selenium.doRemoveScript('{0}')");

    /**
     * <p>
     * Loads the JavaScript extension by it's resourceName.
     * </p>
     *
     * <p>
     * If the JavaScript with given resourceName are already loaded, it will not be loaded again.
     * </p>
     *
     * <p>
     * If the JavaScript is already loaded but it's source has another checksum, it will be reloaded.
     * </p>
     *
     * @param resourceName the full path to resource
     */
    @Override
    public void requireResource(String resourceName) {
        if (!containsScript(resourceName)) {
            loadScript(resourceName);
        } else {
            refreshScript(resourceName);
        }
    }

    private boolean containsScript(String resourceName) {
        return Boolean.valueOf(selenium.getEval(containsScriptWithResourceName.parametrize(escapeEcmaScript(resourceName))));
    }

    private void loadScript(String resourceName) {
        JavaScript extension = JavaScript.fromResource(resourceName);
        String identification = extension.getIdentification();
        String escapedResourceName = escapeEcmaScript(resourceName);
        selenium.addScript(extension);
        selenium.getEval(setResourceNameForId.parametrize(identification, escapedResourceName));
    }

    private void refreshScript(String resourceName) {
        JavaScript extension = JavaScript.fromResource(resourceName);
        String identification = extension.getIdentification();
        String escapedResourceName = escapeEcmaScript(resourceName);

        String scriptId = selenium.getEval(getIdForScriptWithResourceName.parametrize(escapedResourceName));

        if (!scriptId.equals(identification)) {
            System.out.println("# Reloading extension: " + resourceName);
            selenium.getEval(removeScript.parametrize(scriptId));
            loadScript(resourceName);
        }
    }

    /**
     * Adds the JavaScript extensions by defining list of resource names.
     *
     * @param resourceNames the list of full paths to resources
     */
    @Override
    public void requireResources(List<String> resourceNames) {
        for (String resourceName : resourceNames) {
            requireResource(resourceName);
        }
    }

    /**
     * The SeleniumExtensions specifies new custom handlers, but the registration in commandFactory are triggered before the
     * loading of extensions. That is reason why we must explicitly register it before the test after each start of selenium.
     */
    @Override
    public void registerCustomHandlers() {
        final JavaScript registerCustomHandlers = js("currentTest.commandFactory.registerAll(selenium)");
        selenium.getEval(registerCustomHandlers);
    }
}
