/*
 * ICESOFT COMMERCIAL SOURCE CODE LICENSE V 1.1
 *
 * The contents of this file are subject to the ICEsoft Commercial Source
 * Code License Agreement V1.1 (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.icesoft.com/license/commercial-source-v1.1.html
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations under
 * the License.
 *
 * Copyright 2009-2015 ICEsoft Technologies Canada, Corp. All Rights Reserved.
 */



package com.icesoft.components.accessible.datapaginator;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.lang.Character;
import javax.faces.FacesException;
import javax.faces.application.Application;
import javax.faces.component.UIComponent;
import javax.faces.component.UIParameter;
import javax.faces.context.FacesContext;
import java.util.Map.Entry;
import java.util.HashMap;
import org.w3c.dom.Element;
import javax.faces.component.UICommand;
import com.icesoft.components.accessible.util.CustomComponentUtils;
import com.icesoft.components.accessible.util.CoreComponentUtils;
import com.icesoft.components.accessible.ext.HtmlCommandLink;
import javax.faces.component.html.HtmlCommandButton;
import javax.faces.component.html.HtmlOutputText;
import com.icesoft.faces.component.panelseries.UISeries;
import com.icesoft.faces.component.CSS_DEFAULT;
import com.icesoft.faces.component.ExtendedAttributeConstants;
import com.icesoft.faces.context.DOMContext;
import com.icesoft.faces.context.effects.JavascriptContext;
import com.icesoft.faces.renderkit.dom_html_basic.DomBasicRenderer;
import com.icesoft.faces.renderkit.dom_html_basic.HTML;
import com.icesoft.faces.renderkit.dom_html_basic.PassThruAttributeRenderer;

import com.icesoft.util.pooling.ClientIdPool;

public class DataPaginatorRenderer extends DomBasicRenderer {
    public static final String RENDERER_TYPE = "com.icesoft.faces.accessible.DataScroller";

    protected static final String PAGE_NAVIGATION = "idx";

    private static final String[] PASSTHRU_EXCLUDE =
        new String[] { HTML.STYLE_ATTR };
    private static final String[] PASSTHRU =
        ExtendedAttributeConstants.getAttributes(
            ExtendedAttributeConstants.ICE_DATAPAGINATOR,
            PASSTHRU_EXCLUDE);

	//really should change to a bidirectional map
	private static final Map<String, String> noJsLabelMap = new HashMap<String,String>() {{
		  put("fastf", ">>");   
		  put("fastr", "<<"); 
		  put("previous", "<");   
		  put("next", ">");
		  put("last", ">|");   
		  put("first", "|<");
	}};    
    
	public boolean getRendersChildren() {
        return true;
    }

    public void decode(FacesContext context, UIComponent component) {
        validateParameters(context, component, DataPaginator.class);
        Map parameter = context.getExternalContext().getRequestParameterMap();
        String param = (String) parameter.get(component.getClientId(context));
        if (param != null) {
			if (!param.equals("")) {
            if (Character.isDigit(param.charAt(0))) {
                // queue a navigation event that results from the user pressing
                // on one of the page indexes
                component.queueEvent(new PaginatorActionEvent(component, Integer.parseInt(param)));
            } else {
                // queue a navigation event that results from the user pressing
                // on the navigation buttons
				for (Entry<String,String> e : noJsLabelMap.entrySet()) {
					if (e.getValue().equals(param)) param = e.getKey();
				}
                component.queueEvent(new PaginatorActionEvent(component, param));
            }}
        }
    }

    protected void setVariables(FacesContext facescontext,
                                DataPaginator scroller) throws IOException {
        Map requestMap = facescontext.getExternalContext().getRequestMap();

        String pageCountVar = scroller.getPageCountVar();
        if (pageCountVar != null) {
            int pageCount = scroller.getPageCount();
            requestMap.put(pageCountVar, new Integer(pageCount));
        }
        String pageIndexVar = scroller.getPageIndexVar();
        if (pageIndexVar != null) {
            int pageIndex = scroller.getPageIndex();
            if (pageIndex > scroller.getPageCount()) {
                pageIndex = scroller.getPageCount();
            }
            requestMap.put(pageIndexVar, new Integer(pageIndex));
        }
        String rowsCountVar = scroller.getRowsCountVar();
        if (rowsCountVar != null) {
            int rowsCount = scroller.getRowCount();
            requestMap.put(rowsCountVar, new Integer(rowsCount));
        }
        String displayedRowsCountVar = scroller.getDisplayedRowsCountVar();
        if (displayedRowsCountVar != null) {
            int displayedRowsCount = scroller.getRows();
            int max = scroller.getRowCount() - scroller.getFirstRow();
            if (displayedRowsCount > max) {
                displayedRowsCount = max;
            }
            if (displayedRowsCount == 0) {
                displayedRowsCount = scroller.getRowCount();
            }
            requestMap.put(displayedRowsCountVar,
                           new Integer(displayedRowsCount));
        }
        String firstRowIndexVar = scroller.getFirstRowIndexVar();
        if (firstRowIndexVar != null) {
            int firstRowIndex = scroller.getFirstRow();
            if (scroller.getRowCount() > 0) {
                firstRowIndex += 1;
            } else { // ICE-2782
                firstRowIndex = 0;
            }
            requestMap.put(firstRowIndexVar, new Integer(firstRowIndex));
        }
        String lastRowIndexVar = scroller.getLastRowIndexVar();
        if (lastRowIndexVar != null) {
            int lastRowIndex = scroller.getFirstRow() + scroller.getRows();
            int count = scroller.getRowCount();
            if (lastRowIndex > count || lastRowIndex == 0) {
                lastRowIndex = count;
            }
            requestMap.put(lastRowIndexVar, new Integer(lastRowIndex));
        }
    }

    public void removeVariables(FacesContext facescontext,
                                UIComponent uiComponent) throws IOException {
        DataPaginator scroller = (DataPaginator) uiComponent;
        Map requestMap = facescontext.getExternalContext().getRequestMap();

        String pageCountVar = scroller.getPageCountVar();
        if (pageCountVar != null) {
            requestMap.remove(pageCountVar);
        }
        String pageIndexVar = scroller.getPageIndexVar();
        if (pageIndexVar != null) {
            requestMap.remove(pageIndexVar);
        }
        String rowsCountVar = scroller.getRowsCountVar();
        if (rowsCountVar != null) {
            requestMap.remove(rowsCountVar);
        }
        String displayedRowsCountVar = scroller.getDisplayedRowsCountVar();
        if (displayedRowsCountVar != null) {
            requestMap.remove(displayedRowsCountVar);
        }
        String firstRowIndexVar = scroller.getFirstRowIndexVar();
        if (firstRowIndexVar != null) {
            requestMap.remove(firstRowIndexVar);
        }
        String lastRowIndexVar = scroller.getLastRowIndexVar();
        if (lastRowIndexVar != null) {
            requestMap.remove(lastRowIndexVar);
        }
    }

    public void encodeBegin(FacesContext facesContext, UIComponent uiComponent)
            throws IOException {
        validateParameters(facesContext, uiComponent, DataPaginator.class);
        DataPaginator scroller = (DataPaginator) uiComponent;
        if (!scroller.isModelResultSet()) {
            super.encodeBegin(facesContext, uiComponent);
                
            if (scroller.getChildCount() > 0) {
                // clear children before render
                List kids = scroller.getChildren();
                for (int i = 0; i < kids.size(); i++) {
                    UIComponent kid = (UIComponent) kids.get(i);
                    // do not remove the output text - bug #333
                    if (!kid.getFamily().equalsIgnoreCase("javax.faces.Output")) {
                        scroller.getChildren().remove(kids.get(i));
                    }
                }
            }
            //Reset the dataTable model before setting variables
            scroller.getUIData().setValue(null);
            scroller.getUIData().setRowIndex(-1);
            setVariables(facesContext, scroller);
        }
    }

    public void encodeChildren(FacesContext facescontext,
                               UIComponent uicomponent) throws IOException {
        validateParameters(facescontext, uicomponent, DataPaginator.class);
        DataPaginator scroller = (DataPaginator) uicomponent;
        if (!scroller.isModelResultSet()) {
            boolean singlePageScroller = scroller.getPageCount() <= 1 &&
                                         scroller.getRowsCountVar() == null &&
                                         scroller.getDisplayedRowsCountVar() ==
                                         null;
            if (!singlePageScroller) {
                CustomComponentUtils.renderChildren(facescontext, uicomponent);
            }
        }
    }

    public void encodeEnd(FacesContext facesContext, UIComponent uiComponent)
            throws IOException {
        validateParameters(facesContext, uiComponent, DataPaginator.class);
        DataPaginator scroller = (DataPaginator) uiComponent;
        if (scroller.getUIData() == null) {
            return;
        }
        renderScroller(facesContext, uiComponent);
        if (!scroller.isModelResultSet()) {
            removeVariables(facesContext, uiComponent);
        }
    }


    protected void renderScroller(FacesContext facesContext,
                                  UIComponent uiComponent) throws IOException {
        DataPaginator scroller = (DataPaginator) uiComponent;
        if (!scroller.isModelResultSet()) {
            if (!scroller.isRenderFacetsIfSinglePage() &&
                scroller.getPageCount() <= 1) {
                return;
            }
        }
        if (scroller.getFacets().size() <= 0) {
            return;
        }

        DOMContext domContext =
                DOMContext.attachDOMContext(facesContext, scroller);
        if (!domContext.isInitialized()) {
            Element rootDiv = domContext.createRootElement(HTML.DIV_ELEM);
            setRootElementId(facesContext, rootDiv, scroller);
            PassThruAttributeRenderer
                        .renderHtmlAttributes(facesContext, scroller, PASSTHRU);
        }
        Element rootDiv = (Element) domContext.getRootNode();
        DOMContext.removeChildren(rootDiv);
        if (scroller.isKeyboardNavigationEnabled()) {
            rootDiv.setAttribute(HTML.ONKEYDOWN_ATTR, "Ice.DatPagKybrd(this.id, event);");
        }
        rootDiv.setAttribute(HTML.NAME_ATTR, scroller.getUIData().getClientId(facesContext));
        Element div = domContext.createElement(HTML.DIV_ELEM);
        rootDiv.appendChild(div);

        String styleClass = scroller.getStyleClass();
        rootDiv.setAttribute(HTML.CLASS_ATTR, styleClass);

        String style = scroller.getStyle();
        if(style != null && style.length() > 0)
            rootDiv.setAttribute(HTML.STYLE_ATTR, style);
        else
            rootDiv.removeAttribute(HTML.STYLE_ATTR);
        String scrollButtonCellClass = scroller.getscrollButtonCellClass();

        handleFacet(facesContext, scroller, domContext, rootDiv, div,
                    scrollButtonCellClass, scroller.getFirst(),
                    DataPaginator.FACET_FIRST);
        
        // Horizontal shares a table row, but for vertical,
        // have them each auto-create their own table row
        if (scroller.isVertical()) {
            div = null;
        }
        
        handleFacet(facesContext, scroller, domContext, rootDiv, div,
                    scrollButtonCellClass, scroller.getFastRewind(),
                    DataPaginator.FACET_FAST_REWIND);
        
        handleFacet(facesContext, scroller, domContext, rootDiv, div,
                    scrollButtonCellClass, scroller.getPrevious(),
                    DataPaginator.FACET_PREVIOUS);
        
        if (!scroller.isModelResultSet() && scroller.isPaginator()) {
            if (scroller.isVertical()) {
                div = domContext.createElement(HTML.DIV_ELEM);
                rootDiv.appendChild(div);
            }
            Element paginatorTable;
            if (scroller.isVertical()) {
                Element paginatorDiv = domContext.createElement(HTML.DIV_ELEM);
                div.appendChild(paginatorDiv);
                paginatorTable = domContext.createElement(HTML.DIV_ELEM);
                paginatorDiv.appendChild(paginatorTable);
            } else {
                Element paginatorSpan = domContext.createElement(HTML.SPAN_ELEM);
                div.appendChild(paginatorSpan);
                paginatorTable = domContext.createElement(HTML.SPAN_ELEM);
                paginatorSpan.appendChild(paginatorTable);
            }
            renderPaginator(facesContext, uiComponent, paginatorTable,
                            domContext);
        }
        
        handleFacet(facesContext, scroller, domContext, rootDiv, div,
                    scrollButtonCellClass, scroller.getNext(),
                    DataPaginator.FACET_NEXT);
        
        handleFacet(facesContext, scroller, domContext, rootDiv, div,
                    scrollButtonCellClass, scroller.getFastForward(),
                    DataPaginator.FACET_FAST_FORWARD);
        
        handleFacet(facesContext, scroller, domContext, rootDiv, div,
                    scrollButtonCellClass, scroller.getLast(),
                    DataPaginator.FACET_LAST);
        
        domContext.stepOver();
    }
    
    protected void handleFacet(FacesContext facesContext,
                               DataPaginator scroller,
                               DOMContext domContext,
                               Element rootDiv,
                               Element div,
                               String scrollButtonCellClass,
                               UIComponent facetComp,
                               String facetName)
            throws IOException {
        if (facetComp != null) {
            // tr is null when scroller.isVertical()
            if (div == null) {
                div = domContext.createElement(HTML.DIV_ELEM);
                rootDiv.appendChild(div);
            }
            Element span = domContext.createElement(HTML.SPAN_ELEM);
            span.setAttribute(HTML.CLASS_ATTR, scrollButtonCellClass);
            div.appendChild(span);
            domContext.setCursorParent(span);
            Element e = renderFacet(facesContext, domContext, scroller, facetComp, facetName);
			if (e != null) span.appendChild(e);
        }
    }
    
    protected Element renderFacet(FacesContext facesContext, DOMContext domContext,
                               DataPaginator scroller,
                               UIComponent facetComp, String facetName)
            throws IOException {
        Object linkObj = getLink(facesContext, domContext, scroller, facetName);

        if (scroller.isDisabled() ||
            (!scroller.isModelResultSet() && scroller.getPageCount() <= 1) ||
            (scroller.getPageIndex() == 1 && (DataPaginator.FACET_FAST_REWIND.equals(facetName) ||
            DataPaginator.FACET_FIRST.equals(facetName) ||
            DataPaginator.FACET_PREVIOUS.equals(facetName) 
             )) ||
             (scroller.getPageIndex() == scroller.getPageCount() && (DataPaginator.FACET_FAST_FORWARD.equals(facetName) ||
             DataPaginator.FACET_LAST.equals(facetName) ||
             DataPaginator.FACET_NEXT.equals(facetName) 
             ))
            ) {
				if (!CoreComponentUtils.isJavaScriptDisabled(facesContext)) {
					UICommand link = (UICommand) linkObj;
		            link.getAttributes().put(HTML.ONCLICK_ATTR, "return false;");
		            link.getAttributes().put(HTML.STYLE_ATTR, "cursor:default;");
		            link.getAttributes().put(HTML.STYLE_CLASS_ATTR, CSS_DEFAULT.COMMAND_LINK_DEFAULT_STYLE_CLASS+"-dis");
				} else {
					Element button = (Element)linkObj;
					button.setAttribute(HTML.DISABLED_ATTR, "disabled");
					button.setAttribute(HTML.STYLE_ATTR, "cursor:default; color:#CFCFCF;");
		            button.setAttribute(HTML.CLASS_ATTR, CSS_DEFAULT.COMMAND_LINK_DEFAULT_STYLE_CLASS+"-dis");
				}	      
        } else if (linkObj instanceof HtmlCommandLink) {
			((HtmlCommandLink)linkObj).setDisabled(false);
        }
		
		if (!CoreComponentUtils.isJavaScriptDisabled(facesContext)) {
	        UICommand link = (UICommand) linkObj;
			link.encodeBegin(facesContext);
	        encodeParentAndChildren(facesContext, facetComp);
	        link.encodeEnd(facesContext);
			return null;
		} else {
			return (Element)linkObj;
		}
    }

    protected void renderPaginator(FacesContext facesContext,
                                   UIComponent uiComponent,
                                   Element paginatorTable,
                                   DOMContext domContext) throws IOException {
        DataPaginator scroller = (DataPaginator) uiComponent;
        int maxPages = scroller.getPaginatorMaxPages();
        if (maxPages <= 1) {
            maxPages = 2;
        }
        int pageCount = scroller.getPageCount();
        if (pageCount <= 1) {
            return;
        }
        int pageIndex = scroller.getPageIndex();
        if (pageIndex > pageCount) {
            pageIndex = pageCount;
        }
        int delta = maxPages / 2;
        int pages;
        int start;
        if (pageCount > maxPages && pageIndex > delta) {
            pages = maxPages;
            start = pageIndex - pages / 2 - 1;
            if (start + pages > pageCount) {
                start = pageCount - pages;
            }
        } else {
            pages = pageCount < maxPages ? pageCount : maxPages;
            start = 0;
        }
        String styleClass = scroller.getPaginatorTableClass();
        paginatorTable.setAttribute(HTML.CLASS_ATTR, styleClass);

        Element divOrSpan = null;
        if (!scroller.isVertical()) {
            divOrSpan = domContext.createElement(HTML.SPAN_ELEM);
            paginatorTable.appendChild(divOrSpan);
        }
        Element span;
        UIComponent form = findForm(scroller);
        String formId = null;
        if (form == null) {
            throw new FacesException("Form tag is missing");
        } else {
            formId = form.getClientId(facesContext);
        }
        for (int i = start, size = start + pages; i < size; i++) {
            int idx = i + 1;
            if (scroller.isVertical()) {
                divOrSpan = domContext.createElement(HTML.DIV_ELEM);
                paginatorTable.appendChild(divOrSpan);
            }
            span = domContext.createElement(HTML.SPAN_ELEM);
            divOrSpan.appendChild(span);
            domContext.setCursorParent(span);
            String cStyleClass;

            Element link = getLink(facesContext, domContext, scroller,
                    Integer.toString(idx), idx, formId);
            
            if (idx == pageIndex) {
                String clientId = scroller.getClientId(facesContext);
                cStyleClass = scroller.getPaginatorActiveColumnClass();
                // set the focus on current page, if it was a keyboard
                Map parameter = facesContext.getExternalContext().getRequestParameterMap();
                if (parameter.containsKey(clientId)
                        && parameter.containsKey(clientId+"kbd")) {
                    JavascriptContext.applicationFocus(facesContext, link.getAttribute("id"));
                }

            } else {
                cStyleClass = scroller.getPaginatorColumnClass();

            }
            if (cStyleClass != null) {
                span.setAttribute(HTML.CLASS_ATTR, cStyleClass);
            }


            span.appendChild(link);
        }
    }

    protected Element getLink(FacesContext facesContext, DOMContext domContext,
                              DataPaginator scroller,
                              String text, int pageIndex, String formId) {
		if (!CoreComponentUtils.isJavaScriptDisabled(facesContext)) {
	        Element link = domContext.createElement(HTML.ANCHOR_ELEM);
	        if (text != null) {
	            link.appendChild(domContext.createTextNode(text));
	        }
	        String linkid = ClientIdPool.get(scroller.getClientId(facesContext) +
	                        DataPaginatorRenderer.PAGE_NAVIGATION +
	                        Integer.toString(pageIndex));
	        String onClick = /*"document.forms['"+ formId + "']" + "['"+ formId +":_idcl']" + ".value='" +  linkid  + "'"+ 
	        		";*/"document.forms['" + formId + "']['" +
	              scroller.getClientId(facesContext) + "'].value='" + text + "'" +
	              ";iceSubmit(" + " document.forms['" + formId + "']," +
	              " this,event); " + "return false;";
	        link.setAttribute(HTML.ID_ATTR, linkid);
	        if (scroller.isDisabled()) {
	            link.removeAttribute(HTML.ONCLICK_ATTR);
	        } else {
	            link.setAttribute(HTML.ONCLICK_ATTR, onClick);
	        }
	        link.setAttribute(HTML.HREF_ATTR, "javascript:;");
	        PassThruAttributeRenderer.renderOnFocus(scroller, link);
	        PassThruAttributeRenderer.renderOnBlur(link);
	        return link;
		} else {
			Element button = domContext.createElement(HTML.INPUT_ELEM);
	        if (text != null) {
	            //button.appendChild(domContext.createTextNode(text));
	        }
			String linkid;
			if (text != "idx") linkid = 
							ClientIdPool.get(scroller.getClientId(facesContext) +
	                        DataPaginatorRenderer.PAGE_NAVIGATION +
	                        Integer.toString(pageIndex));
			else linkid = scroller.getClientId(facesContext);
	        
			button.setAttribute(HTML.ID_ATTR, linkid);
			button.setAttribute(HTML.NAME_ATTR,	scroller.getClientId(facesContext));
			button.setAttribute(HTML.CLASS_ATTR, "iceCmdLnkJSDis");
			button.setAttribute(HTML.VALUE_ATTR, text);
			//if (scroller.getPageIndex() == Integer.parseInt(text)) button.setAttribute(HTML.STYLE_ATTR, "color:#F; background-color:#2A6CC2;");					
	        if (scroller.isDisabled()) {
	            button.setAttribute(HTML.TYPE_ATTR, "hidden");
	        } else {
	            button.setAttribute(HTML.TYPE_ATTR, "submit");
	        }				
			return button;
		}
    }

    protected Object getLink(FacesContext facesContext, DOMContext domContext,
                                      DataPaginator scroller,
                                      String facetName) {
		Application application = facesContext.getApplication();
	
		if (!CoreComponentUtils.isJavaScriptDisabled(facesContext)) {
	        HtmlCommandLink link = (HtmlCommandLink) application.createComponent(HtmlCommandLink.COMPONENT_TYPE);
	        
			String id = scroller.getId() + facetName;
	        link.setId(id);
	        link.setTransient(true);
	        UIParameter parameter = (UIParameter) application
	                .createComponent(UIParameter.COMPONENT_TYPE);
	        parameter.setId(id + "_param");
	        parameter.setTransient(true);
	        parameter.setName(scroller.getClientId(facesContext));
	        parameter.setValue(facetName);
	        //getChildren doesn't need any check for the childCount
	        List children = link.getChildren();
	        children.add(parameter);
        
	        // For some reason, these components being marked transient isn't 
	        // resulting in them going away, so we'll explicitly remove old ones
	        for(int i = 0; i < scroller.getChildCount(); i++) {
	            UIComponent comp = (UIComponent) scroller.getChildren().get(i);
	            if (comp.getId().equals(id)) {
	                scroller.getChildren().remove(i);
	                break;
	            }
	        }
        
	        scroller.getChildren().add(link);
	        return link;
		} else {
			Element button = domContext.createElement(HTML.INPUT_ELEM);
	        //button.appendChild(domContext.createTextNode(noJsLabelMap.get(facetName)));

			String id = scroller.getId() + facetName;
	        
			button.setAttribute(HTML.ID_ATTR, id);
			button.setAttribute(HTML.NAME_ATTR,	scroller.getClientId(facesContext));
			button.setAttribute(HTML.VALUE_ATTR, noJsLabelMap.get(facetName)); 
			button.setAttribute(HTML.CLASS_ATTR, "iceCmdLnkJSDis");								
	        if (scroller.isDisabled()) {
	            button.setAttribute(HTML.TYPE_ATTR, "hidden");
	        } else {
	            button.setAttribute(HTML.TYPE_ATTR, "submit");
	        }				
			return button;	
		}
    }
}
