/**
 * License Agreement.
 *
 *  JBoss RichFaces - Ajax4jsf Component Library
 *
 * Copyright (C) 2007  Exadel, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1 as published by the Free Software Foundation.
 *
 * This library 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 library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 */

package org.ajax4jsf.component;

import static org.ajax4jsf.resource.InternetResource.RESOURCE_URI_PREFIX;

import java.io.IOException;
import java.util.Collections;
import java.util.List;

import javax.faces.component.UIComponent;
import javax.faces.component.UIComponentBase;
import javax.faces.context.FacesContext;

import org.ajax4jsf.javascript.AjaxScript;
import org.ajax4jsf.renderkit.HeaderResourceProducer2;
import org.ajax4jsf.renderkit.ProducerContext;
import org.ajax4jsf.renderkit.RendererBase;
import org.ajax4jsf.renderkit.RendererUtils.HTML;
import org.ajax4jsf.resource.InternetResource;
import org.ajax4jsf.resource.InternetResourceBuilder;
import org.ajax4jsf.resource.ResourceNotFoundException;
import org.ajax4jsf.tests.AbstractAjax4JsfTestCase;

import com.gargoylesoftware.htmlunit.html.HtmlLink;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.gargoylesoftware.htmlunit.html.HtmlScript;

/**
 * @author Nick Belaevski
 * @since 3.3.0
 */

public class LoadResourceComponentTest extends AbstractAjax4JsfTestCase {

	private String componentJs;
	private String userJs;

	private String componentXcss;
	private String userXcss;

	/**
	 * @param name
	 */
	public LoadResourceComponentTest(String name) {
		super(name);
	}
	
	private void registerResource(String path) {
		InternetResourceBuilder resourceBuilder = InternetResourceBuilder.getInstance();

		try {
			resourceBuilder.getResource(path);
		} catch (ResourceNotFoundException e) {
			resourceBuilder.createResource(null, path);
		}
	}

	@Override
	public void setUp() throws Exception {
		super.setUp();
		renderKit.addRenderer(ResourceDependentComponent.COMPONENT_FAMILY, 
				ResourceDependentComponent.COMPONENT_TYPE, new ResourceDependentComponentRenderer());
	
		String resourcePackage = getClass().getPackage().getName().replace('.', '/') + "/";
		componentJs = resourcePackage + "component.js";
		componentXcss = resourcePackage + "component.xcss";
		
		userJs = resourcePackage + "user.js";
		userXcss = resourcePackage + "user.xcss";
		
		registerResource(componentJs); 
		registerResource(userJs); 
		registerResource(componentXcss); 
		registerResource(userXcss); 
	}
	
	@Override
	public void tearDown() throws Exception {
		super.tearDown();
		
		componentJs = null;
		userJs = null;
		
		componentXcss = null;
		userXcss = null;
	}
	
	private UIResource createLoadScriptComponent() {
		return (UIResource) application.createComponent("org.ajax4jsf.LoadScript");
	}

	private UIResource createLoadStyleComponent() {
		return (UIResource) application.createComponent("org.ajax4jsf.LoadStyle");
	}
	
	private List<HtmlScript> selectScriptsBySrc(HtmlPage page, String src) throws Exception {
		return Collections.checkedList(page.getByXPath("//script[@src[contains(string(.), '" + src + "')]]"), 
				HtmlScript.class);
	}
	
	private List<HtmlLink> selectStylesByHhref(HtmlPage page, String src) throws Exception {
		return Collections.checkedList(page.getByXPath("//link[@type = 'text/css'][@rel = 'stylesheet'][@href[contains(string(.), '" + src + "')]]"), 
				HtmlLink.class);
	}

	protected UIResource createAndAddScriptResource(Object src) {
		UIResource scriptComponent = createLoadScriptComponent();
		scriptComponent.setSrc(src);
		facesContext.getViewRoot().getChildren().add(scriptComponent);
	
		return scriptComponent;
	}
	
	protected UIResource createAndAddStyleResource(Object src) {
		UIResource styleComponent = createLoadStyleComponent();
		styleComponent.setSrc(src);
		facesContext.getViewRoot().getChildren().add(styleComponent);
	
		return styleComponent;
	}

	public void testRenderScript() throws Exception {
		createAndAddScriptResource(RESOURCE_URI_PREFIX + userJs);
		createAndAddScriptResource(RESOURCE_URI_PREFIX + componentJs).setRendered(false);
		
		HtmlPage page = renderView();
		List<HtmlScript> scripts;
		
		scripts = selectScriptsBySrc(page, userJs);
		assertEquals(1, scripts.size());

		scripts = selectScriptsBySrc(page, componentJs);
		assertEquals(0, scripts.size());
	}
	
	public void testRenderScriptResource() throws Exception {
		InternetResourceBuilder resourceBuilder = InternetResourceBuilder.getInstance();
		createAndAddScriptResource(resourceBuilder.getResourceForKey(userJs));
		createAndAddScriptResource(resourceBuilder.getResourceForKey(componentJs)).setRendered(false);

		HtmlPage page = renderView();
		List<HtmlScript> scripts;
		
		scripts = selectScriptsBySrc(page, userJs);
		assertEquals(1, scripts.size());

		scripts = selectScriptsBySrc(page, componentJs);
		assertEquals(1, scripts.size());
	}
	
	public void testRenderStyle() throws Exception {
		createAndAddStyleResource(RESOURCE_URI_PREFIX + userXcss);
		createAndAddStyleResource(RESOURCE_URI_PREFIX + componentXcss).setRendered(false);
		
		HtmlPage page = renderView();
		List<HtmlLink> styles;
		HtmlLink userLink;
		
		styles = selectStylesByHhref(page, userXcss);
		assertEquals(1, styles.size());
		userLink = styles.get(0);
		assertEquals("user", userLink.getClassAttribute());
		assertEquals("", userLink.getMediaAttribute());

		styles = selectStylesByHhref(page, componentXcss);
		assertEquals(0, styles.size());
	}
	
	public void testRenderStyleResource() throws Exception {
		InternetResourceBuilder resourceBuilder = InternetResourceBuilder.getInstance();
		createAndAddStyleResource(resourceBuilder.getResourceForKey(userXcss));
		createAndAddStyleResource(resourceBuilder.getResourceForKey(componentXcss)).setRendered(false);

		UIResource styleComponent = createLoadStyleComponent();
		styleComponent.setSrc(InternetResourceBuilder.getInstance().getResourceForKey(userXcss));
		facesContext.getViewRoot().getChildren().add(styleComponent);
		
		HtmlPage page = renderView();
		List<HtmlLink> styles;
		HtmlLink userLink;
		
		styles = selectStylesByHhref(page, userXcss);
		assertEquals(1, styles.size());
		userLink = styles.get(0);
		assertEquals("user", userLink.getClassAttribute());
		assertEquals("", userLink.getMediaAttribute());

		styles = selectStylesByHhref(page, componentXcss);
		assertEquals(1, styles.size());
		userLink = styles.get(0);
		assertEquals("user", userLink.getClassAttribute());
		assertEquals("", userLink.getMediaAttribute());
	}
	
	public void testRenderMedia() throws Exception {
		createAndAddStyleResource(RESOURCE_URI_PREFIX + userXcss).
			getAttributes().put(HTML.media_ATTRIBUTE, "screen print");
		
		HtmlPage page = renderView();
		List<HtmlLink> styles = selectStylesByHhref(page, userXcss);
		assertEquals(1, styles.size());
		HtmlLink userLink = styles.get(0);
		assertEquals("user", userLink.getClassAttribute());
		assertEquals("screen print", userLink.getMediaAttribute());
	}
	
	public void testRenderOverridenScript() throws Exception {
		List<UIComponent> childrenList = facesContext.getViewRoot().getChildren();

		UIResource userJsComponent = createLoadScriptComponent();
		userJsComponent.setSrc(RESOURCE_URI_PREFIX + userJs);
		childrenList.add(userJsComponent);
		
		InternetResourceBuilder resourceBuilder = InternetResourceBuilder.getInstance();
		
		childrenList.add(new ResourceDependentComponent(true, 
				resourceBuilder.getResourceForKey(componentJs),
				resourceBuilder.getResourceForKey(userJs)));

		UIResource componentJsComponent = createLoadScriptComponent();
		componentJsComponent.setSrc(RESOURCE_URI_PREFIX + componentJs);
		childrenList.add(componentJsComponent);
		
		HtmlPage page = renderView();
		List<HtmlScript> userScripts = selectScriptsBySrc(page, userJs);
		assertEquals(1, userScripts.size());
		List<HtmlScript> componentScripts = selectScriptsBySrc(page, componentJs);
		assertEquals(1, componentScripts.size());
	}
	
	public void testRenderOverridenStyle() throws Exception {
		List<UIComponent> childrenList = facesContext.getViewRoot().getChildren();

		UIResource userXcssComponent = createLoadStyleComponent();
		userXcssComponent.setSrc(RESOURCE_URI_PREFIX + userXcss);
		childrenList.add(userXcssComponent);
		
		InternetResourceBuilder resourceBuilder = InternetResourceBuilder.getInstance();
		
		childrenList.add(new ResourceDependentComponent(false, 
				resourceBuilder.getResourceForKey(componentXcss),
				resourceBuilder.getResourceForKey(userXcss)));
		
		UIResource componentXcssComponent = createLoadStyleComponent();
		componentXcssComponent.setSrc(RESOURCE_URI_PREFIX + componentXcss);
		childrenList.add(componentXcssComponent);
		
		HtmlPage page = renderView();
		List<HtmlLink> styles = selectStylesByHhref(page, userXcss);
		assertEquals(2, styles.size());
		assertEquals("component", styles.get(0).getClassAttribute());
		assertEquals("user", styles.get(1).getClassAttribute());

		styles = selectStylesByHhref(page, componentXcss);
		assertEquals(2, styles.size());
		assertEquals("component", styles.get(0).getClassAttribute());
		assertEquals("user", styles.get(1).getClassAttribute());
	}
	
	public void testRenderOverridenAjaxScript() throws Exception {
		UIResource userComponent = createLoadScriptComponent();
		userComponent.setSrc(RESOURCE_URI_PREFIX + "/org/ajax4jsf/javascript/scripts/AJAX.js");
		
		List<UIComponent> childrenList = facesContext.getViewRoot().getChildren();
		childrenList.add(userComponent);
		
		childrenList.add(new ResourceDependentComponent(true, new AjaxScript()));
		
		HtmlPage page = renderView();
		List<HtmlScript> scripts = selectScriptsBySrc(page, "AjaxScript");
		assertEquals(1, scripts.size());

		scripts = selectScriptsBySrc(page, "AJAX.js");
		//TODO change to 0 after related issue resolution
		assertEquals(1, scripts.size());
	}
}

class ResourceDependentComponent extends UIComponentBase {

	static String COMPONENT_TYPE = "org.ajax4jsf.component.Test$ResourceDependentComponent";
	
	static String COMPONENT_FAMILY = COMPONENT_TYPE;

	private InternetResource[] resources;
	
	private boolean useScripts;

	public ResourceDependentComponent(boolean useScripts, InternetResource... resources) {
		this.useScripts = useScripts;
		this.resources = resources;
	}
	
	@Override
	public String getFamily() {
		return COMPONENT_FAMILY;
	}
	
	@Override
	public String getRendererType() {
		return COMPONENT_TYPE;
	}
	
	public InternetResource[] getScripts() {
		return useScripts ? resources : null;
	}
	
	public InternetResource[] getStyles() {
		return useScripts ? null : resources;
	}
}

class ResourceDependentComponentRenderer extends RendererBase implements HeaderResourceProducer2 {

	@Override
	protected Class<? extends UIComponent> getComponentClass() {
		return ResourceDependentComponent.class;
	}

	public void encodeToHead(FacesContext context, UIComponent component,
			ProducerContext pc) throws IOException {

		ResourceDependentComponent resourceDependentComponent = (ResourceDependentComponent) component;
		
		if (pc.isProcessScripts()) {
			InternetResource[] scripts = resourceDependentComponent.getScripts();
			if (scripts != null) {
				for (InternetResource script : scripts) {
					script.encode(context, null);
				}
			}
		}

		if (pc.isProcessStyles()) {
			InternetResource[] styles = resourceDependentComponent.getStyles();
			if (styles != null) {
				for (InternetResource script : styles) {
					script.encode(context, null);
				}
			}
		}
	}
	
}