/*
 * JBoss, Home of Professional Open Source
 * Copyright ${year}, 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.richfaces.renderkit;

import static org.richfaces.component.DataScrollerControlsMode.auto;
import static org.richfaces.component.DataScrollerControlsMode.show;

import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import javax.faces.application.ResourceDependencies;
import javax.faces.application.ResourceDependency;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;

import org.ajax4jsf.javascript.JSFunction;
import org.ajax4jsf.javascript.JSFunctionDefinition;
import org.ajax4jsf.javascript.JSLiteral;
import org.ajax4jsf.javascript.JSReference;
import org.richfaces.component.AbstractDataScroller;
import org.richfaces.component.DataScrollerControlsMode;
import org.richfaces.event.DataScrollEvent;
import org.richfaces.renderkit.util.AjaxRendererUtils;

@ResourceDependencies({ @ResourceDependency(library = "javax.faces", name = "jsf.js"),
        @ResourceDependency(library = "org.richfaces", name = "jquery.js"),
        @ResourceDependency(library = "org.richfaces", name = "richfaces.js"),
        @ResourceDependency(library = "org.richfaces", name = "richfaces-queue.reslib"),
        @ResourceDependency(library = "org.richfaces", name = "richfaces-base-component.js"),
        @ResourceDependency(library = "org.richfaces", name = "richfaces-event.js"),
        @ResourceDependency(library = "org.richfaces", name = "datascroller.js"),
        @ResourceDependency(library = "org.richfaces", name = "datascroller.ecss") })
public class DataScrollerBaseRenderer extends RendererBase {
    public void doDecode(FacesContext context, UIComponent component) {
        Map<String, String> paramMap = context.getExternalContext().getRequestParameterMap();

        String clientId = component.getClientId(context);
        String param = (String) paramMap.get(clientId + ":page");

        if (param != null) {

            AbstractDataScroller scroller = (AbstractDataScroller) component;
            int newPage = scroller.getPageForFacet(param);
            int page = scroller.getPage();

            if (newPage != 0 && newPage != page) {
                new DataScrollEvent(scroller, String.valueOf(page), param, newPage).queue();
            }
        }
    }

    protected boolean shouldRender(UIComponent component){
        AbstractDataScroller ds = (AbstractDataScroller) component;
        if ((ds.getPageCount() == 1)&&(!ds.isRenderIfSinglePage())){
            return false;
        }else{
        return true;
        }
    }

    private DataScrollerControlsMode getModeOrDefault(UIComponent component, String attributeName) {
        DataScrollerControlsMode mode = (DataScrollerControlsMode) component.getAttributes().get(attributeName);
        if (mode == null) {
            mode = DataScrollerControlsMode.DEFAULT;
        }
        return mode;
    }

    public ControlsState getControlsState(FacesContext context, UIComponent component) {

        int fastStep = (Integer) component.getAttributes().get("fastStepOrDefault");
        int pageIndex = (Integer) component.getAttributes().get("page");
        int pageCount = (Integer) component.getAttributes().get("pageCount");

        int minPageIdx = 1;
        int maxPageIdx = pageCount;

        boolean useFirst = true;
        boolean useLast = true;

        boolean useBackFast = true;
        boolean useForwFast = true;

        ControlsState controlsState = new ControlsState();

        if (pageIndex <= minPageIdx) {
            useFirst = false;
        }

        if (pageIndex >= maxPageIdx) {
            useLast = false;
        }

        if (pageIndex - fastStep < minPageIdx) {
            useBackFast = false;
        }

        if (pageIndex + fastStep > maxPageIdx) {
            useForwFast = false;
        }

        DataScrollerControlsMode boundaryControls = getModeOrDefault(component, "boundaryControls");
        DataScrollerControlsMode stepControls = getModeOrDefault(component, "stepControls");
        DataScrollerControlsMode fastControls = getModeOrDefault(component, "fastControls");

        boolean isAuto = auto.equals(boundaryControls);
        if (isAuto || show.equals(boundaryControls)) {
            if (isAuto) {
                controlsState.setFirstRendered(useFirst);
                controlsState.setLastRendered(useLast);
            }

            controlsState.setFirstEnabled(useFirst);
            controlsState.setLastEnabled(useLast);
        } else {
            controlsState.setFirstRendered(false);
            controlsState.setLastRendered(false);
        }

        isAuto = auto.equals(stepControls);
        if (isAuto || show.equals(stepControls)) {
            if (isAuto) {
                controlsState.setPreviousRendered(useFirst);
                controlsState.setNextRendered(useLast);
            }

            controlsState.setPreviousEnabled(useFirst);
            controlsState.setNextEnabled(useLast);
        } else {
            controlsState.setPreviousRendered(false);
            controlsState.setNextRendered(false);
        }

        isAuto = auto.equals(fastControls);
        if (isAuto || show.equals(fastControls)) {
            if (isAuto) {
                controlsState.setFastForwardRendered(useForwFast);
                controlsState.setFastRewindRendered(useBackFast);
            }

            controlsState.setFastForwardEnabled(useForwFast);
            controlsState.setFastRewindEnabled(useBackFast);
        } else {
            controlsState.setFastForwardRendered(false);
            controlsState.setFastRewindRendered(false);
        }

        UIComponent controlsSeparatorFacet = component.getFacet(AbstractDataScroller.CONTROLS_SEPARATOR_FACET_NAME);
        if (controlsSeparatorFacet != null && controlsSeparatorFacet.isRendered()) {
            controlsState.setControlsSeparatorRendered(true);
        }

        return controlsState;
    }

    public Map<String, String> renderPagerFacet(ResponseWriter out, FacesContext context, UIComponent facet) throws IOException {
        out.startElement(HtmlConstants.SPAN_ELEM, facet);
        out.writeAttribute(HtmlConstants.CLASS_ATTRIBUTE, "rf-ds-pages ", null);
        encodeFacet(context, facet);
        out.endElement(HtmlConstants.SPAN_ELEM);
        return Collections.emptyMap();
    }

    public Map<String, String> renderPager(ResponseWriter out, FacesContext context, UIComponent component) throws IOException {
        UIComponent pagesFacet = component.getFacet(AbstractDataScroller.PAGES_FACET_NAME);
        if (pagesFacet != null && pagesFacet.isRendered()) {
            return renderPagerFacet(out, context, pagesFacet);
        }

        int currentPage = (Integer) component.getAttributes().get("page");
        int maxPages = (Integer) component.getAttributes().get("maxPagesOrDefault");
        int pageCount = (Integer) component.getAttributes().get("pageCount");

        Map<String, String> digital = new HashMap<String, String>();

        if (pageCount <= 1) {
            return digital;
        }

        int delta = maxPages / 2;

        int pages;
        int start;

        if (pageCount > maxPages && currentPage > delta) {
            pages = maxPages;
            start = currentPage - pages / 2 - 1;
            if (start + pages > pageCount) {
                start = pageCount - pages;
            }
        } else {
            pages = pageCount < maxPages ? pageCount : maxPages;
            start = 0;
        }

        String clientId = component.getClientId(context);

        int size = start + pages;
        for (int i = start; i < size; i++) {

            boolean isCurrentPage = (i + 1 == currentPage);
            String styleClass;
            String style;

            if (isCurrentPage) {
                styleClass = (String) component.getAttributes().get("selectedStyleClass");
                style = (String) component.getAttributes().get("selectedStyle");
            } else {
                styleClass = (String) component.getAttributes().get("inactiveStyleClass");
                style = (String) component.getAttributes().get("inactiveStyle");
            }

            if (styleClass == null) {
                styleClass = "";
            }

            if (isCurrentPage) {
                out.startElement(HtmlConstants.SPAN_ELEM, component);
                out.writeAttribute(HtmlConstants.CLASS_ATTRIBUTE, "rf-ds-nmb-btn rf-ds-act " + styleClass, null);
            } else {
                out.startElement(HtmlConstants.A_ELEMENT, component);
                out.writeAttribute(HtmlConstants.CLASS_ATTRIBUTE, "rf-ds-nmb-btn " + styleClass, null);
                out.writeAttribute(HtmlConstants.HREF_ATTR, "javascript:void(0);", null);
            }

            if (null != style) {
                out.writeAttribute(HtmlConstants.STYLE_ATTRIBUTE, style, null);
            }

            String page = Integer.toString(i + 1);
            String id = clientId + "_ds_" + page;

            out.writeAttribute(HtmlConstants.ID_ATTRIBUTE, id, null);

            digital.put(id, page);

            out.writeText(page, null);

            if (isCurrentPage) {
                out.endElement(HtmlConstants.SPAN_ELEM);
            } else {
                out.endElement(HtmlConstants.A_ELEMENT);
            }
        }

        return digital;
    }

    public Map<String, Map<String, String>> getControls(FacesContext context, UIComponent component, ControlsState controlsState) {

        Map<String, Map<String, String>> controls = new HashMap<String, Map<String, String>>();
        Map<String, String> right = new HashMap<String, String>();
        Map<String, String> left = new HashMap<String, String>();

        String clientId = component.getClientId(context);

        if (controlsState.getFirstRendered() && controlsState.getFirstEnabled()) {
            left.put(clientId + "_ds_f", AbstractDataScroller.FIRST_FACET_NAME);
        }

        if (controlsState.getFastRewindRendered() && controlsState.getFastRewindEnabled()) {
            left.put(clientId + "_ds_fr", AbstractDataScroller.FAST_REWIND_FACET_NAME);
        }

        if (controlsState.getPreviousRendered() && controlsState.getPreviousEnabled()) {
            left.put(clientId + "_ds_prev", AbstractDataScroller.PREVIOUS_FACET_NAME);
        }

        if (controlsState.getFastForwardRendered() && controlsState.getFastForwardEnabled()) {
            right.put(clientId + "_ds_ff", AbstractDataScroller.FAST_FORWARD_FACET_NAME);
        }

        if (controlsState.getNextRendered() && controlsState.getNextEnabled()) {
            right.put(clientId + "_ds_next", AbstractDataScroller.NEXT_FACET_NAME);
        }

        if (controlsState.getLastRendered() && controlsState.getLastEnabled()) {
            right.put(clientId + "_ds_l", AbstractDataScroller.LAST_FACET_NAME);
        }

        if (!left.isEmpty()) {
            controls.put("left", left);
        }

        if (!right.isEmpty()) {
            controls.put("right", right);
        }
        return controls;
    }

    public void buildScript(ResponseWriter writer, FacesContext context, UIComponent component, Map<?, ?> buttons, Map<?, ?> digitals)
        throws IOException {

        JSFunction function = new JSFunction("new RichFaces.ui.DataScroller");
        function.addParameter(component.getClientId(context));

        Map<String, Object> options = new HashMap<String, Object>();
        options.put("buttons", buttons);
        options.put("digitals", digitals);
        options.put("currentPage", (Integer) component.getAttributes().get("page"));

        function.addParameter(getSubmitFunction(context, component));
        function.addParameter(options);

        writer.write(function.toString());
    }

    public JSFunctionDefinition getSubmitFunction(FacesContext facesContext, UIComponent component) {
        JSFunctionDefinition definition = new JSFunctionDefinition(JSReference.EVENT, new JSReference("element"),
            new JSReference("data"));

        AjaxFunction function = AjaxRendererUtils.buildAjaxFunction(facesContext, component);

        Map<String, Object> parameters = function.getOptions().getParameters();
        parameters.put(component.getClientId(facesContext) + ":page", new JSLiteral("data.page"));

        definition.addToBody(function.toScript());
        return definition;
    }

    public void encodeFacet(FacesContext context, UIComponent component) throws IOException {
        component.encodeAll(context);
    }

    public boolean getRendersChildren() {
        return true;
    }
}
