package org.skyve.impl.web.faces.components;

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

import javax.el.ELContext;
import javax.el.ExpressionFactory;
import javax.faces.FacesException;
import javax.faces.application.Application;
import javax.faces.component.FacesComponent;
import javax.faces.component.UIComponent;
import javax.faces.component.html.HtmlForm;
import javax.faces.component.html.HtmlOutputText;
import javax.faces.context.FacesContext;

import org.primefaces.component.autoupdate.AutoUpdateListener;
import org.skyve.util.Util;

@FacesComponent(CSRFForm.COMPONENT_TYPE)
public class CSRFForm extends HtmlForm {
	@SuppressWarnings("hiding")
	public static final String COMPONENT_TYPE = "org.skyve.impl.web.faces.components.CSRFForm";

	public CSRFForm() {
		// Default to not be a naming container so that csrfToken goes down with that parameter name.
		setPrependId(false);
	}

	@Override
	public void encodeBegin(FacesContext context) throws IOException {
		List<UIComponent> children = getChildren();
		if (children.isEmpty() || (! (children.get(0) instanceof HtmlOutputText))) {
			FacesContext fc = FacesContext.getCurrentInstance();
			Application a = fc.getApplication();
			ExpressionFactory ef = a.getExpressionFactory();
			ELContext elc = fc.getELContext();
	
	    	final String managedBeanName = Util.processStringValue((String) getAttributes().get("managedBean"));
	    	if (managedBeanName == null) {
	    		throw new FacesException("managedBean attribute is required");
	    	}
	    	
			// Add hidden csrfToken
			// <h:outputText style="display:none" escape="false" value="&lt;input type=&quot;hidden&quot; name=&quot;csrfToken&quot; value=&quot;#{fspView.csrfToken}&quot;/&gt;">
			// 		<p:autoUpdate />
			// </h:outputText>
	    	HtmlOutputText text = (HtmlOutputText) a.createComponent(HtmlOutputText.COMPONENT_TYPE);
	    	text.setStyle("display:none");
	    	text.setEscape(false);
	    	text.setValueExpression("value", ef.createValueExpression(elc, "<input type=\"hidden\" name=\"csrfToken\" value=\"#{" + managedBeanName + ".csrfToken}\"/>", String.class));
	        AutoUpdateListener.subscribe(text);
	
			children.add(0, text);
		}
		super.encodeBegin(context);
	}
}

@@ -311,32 +311,71 @@
 nt.UIComponent;%0A
+import javax.faces.component.UIOutput;%0A
 import javax.fac
@@ -557,16 +557,73 @@
 stener;%0A
+import org.primefaces.component.outputpanel.OutputPanel;%0A
 import o
@@ -1651,109 +1651,139 @@
 // %3C
-h
+p
 :output
-Text style=%22display:none%22 escape=%22false%22 value=%22&lt;input type=&quot;hidden&quot; name=&quot;
+Panel id=%22%3CformId%3E_csrfToken%22 style=%22display:none%22 layout=%22inline%22 deferred=%22false%22%3E%0A%09    %09// %09%09%3Cinput type=%22hidden%22 name=%22
 csrf
@@ -1791,27 +1791,17 @@
 oken
-&quot;
+%22
  value=
-&quot;
+%22
 #%7Bfs
@@ -1820,20 +1820,10 @@
 ken%7D
-&quot;/&gt;
 %22
+/
 %3E%0A%09%09
@@ -1857,28 +1857,29 @@
 / %3C/
-h
+p
 :output
-Text
+Panel
 %3E%0A%09    %09
 Html
@@ -1878,45 +1878,40 @@
    %09
-Html
 Output
-Text text = (Html
+Panel panel = (
 Output
-Text
+Panel
 ) a.
@@ -1926,30 +1926,27 @@
 mponent(
-Html
 Output
-Text
+Panel
 .COMPONE
@@ -1965,65 +1965,222 @@
    %09
-text.setStyle(%22display:none%22);%0A%09    %09text.setEscape(false
+panel.setId(getId() + %22_csrfToken%22);%0A%09    %09panel.setStyle(%22display:none%22);%0A%09    %09panel.setLayout(%22inline%22);%0A%09    %09panel.setDeferred(false);%0A%09    %09UIOutput text = (UIOutput) a.createComponent(UIOutput.COMPONENT_TYPE
 );%0A%09
@@ -2353,24 +2353,61 @@
 ng.class));%0A
+%09    %09panel.getChildren().add(text);%0A
 %09        Aut
@@ -2432,20 +2432,21 @@
 bscribe(
-text
+panel
 );%0A%09%0A%09%09%09
@@ -2461,20 +2461,21 @@
 .add(0, 
-text
+panel
 );%0A%09%09%7D%0A%09

Use an OutputPanel around CSRFToken hidden in CSRFForm so that partial updates finer than @form will update the panel.