/*
 * Copyright 2003 - 2014 The eFaps Team
 *
 * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Revision:        $Rev$
 * Last Changed:    $Date$
 * Last Changed By: $Author$
 */

package org.efaps.ui.wicket.models.field.factories;

import org.apache.wicket.Component;
import org.efaps.admin.datamodel.Type;
import org.efaps.admin.datamodel.ui.TypeUI;
import org.efaps.ui.wicket.models.field.AbstractUIField;
import org.efaps.util.EFapsException;

/**
 * TODO comment!
 *
 * @author The eFaps Team
 * @version $Id$
 */
// CHECKSTYLE:OFF
public final class TypeUIFactory
    extends AbstractUIFactory
// CHECKSTYLE:ON
{

    /**
     * Factory Instance.
     */
    private static TypeUIFactory FACTORY;

    /**
     * Singelton.
     */
    private TypeUIFactory()
    {
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Component getEditable(final String _wicketId,
                                 final AbstractUIField _uiField)
        throws EFapsException
    {
        Component ret = null;
        if (applies(_uiField)) {
           //there is no default editable field for TypeUI, teherfor give the chance to UIType
           ret = UITypeFactory.get().getEditable(_wicketId, _uiField);
        }
        return ret;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Component getHidden(final String _wicketId,
                               final AbstractUIField _uiField)
        throws EFapsException
    {
        Component ret = null;
        if (applies(_uiField)) {
           //there is no default hidden field for TypeUI, teherfor give the chance to UIType
           ret = UITypeFactory.get().getEditable(_wicketId, _uiField);
        }
        return ret;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean applies(final AbstractUIField _uiField)
        throws EFapsException
    {
        return _uiField.getValue().getUIProvider() instanceof TypeUI;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected String getReadOnlyValue(final AbstractUIField _uiField)
        throws EFapsException
    {
        Object ret = _uiField.getValue().getReadOnlyValue(_uiField.getParent().getMode());
        if (ret instanceof Type) {
            ret = ((Type) ret).getLabel();
        }
        return ret == null ? "" : String.valueOf(ret);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getKey()
    {
        return TypeUIFactory.class.getName();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getPickListValue(final AbstractUIField _uiField)
        throws EFapsException
    {
        return getReadOnlyValue(_uiField);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Comparable<?> getCompareValue(final AbstractUIField _uiField)
        throws EFapsException
    {
        return getReadOnlyValue(_uiField);
    }

    /**
     * @return IComponentFactory instance
     */
    public static IComponentFactory get()
    {
        if (TypeUIFactory.FACTORY == null) {
            TypeUIFactory.FACTORY = new TypeUIFactory();
        }
        return TypeUIFactory.FACTORY;
    }
}


/*
 * Copyright 2003 - 2018 The eFaps Team
 *
 * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package org.efaps.ui.wicket.components;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.fileupload.FileItem;
import org.apache.wicket.RestartResponseAtInterceptPageException;
import org.apache.wicket.ajax.form.AjaxFormSubmitBehavior.AjaxFormSubmitter;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.IFormSubmitter;
import org.apache.wicket.protocol.http.servlet.MultipartServletWebRequest;
import org.apache.wicket.request.IRequestParameters;
import org.apache.wicket.request.cycle.RequestCycle;
import org.apache.wicket.util.string.StringValue;
import org.apache.wicket.util.string.StringValueConversionException;
import org.efaps.db.Context;
import org.efaps.ui.wicket.EFapsSession.FileParameter;
import org.efaps.ui.wicket.components.date.DateTimePanel;
import org.efaps.ui.wicket.components.date.IDateListener;
import org.efaps.ui.wicket.components.gridx.GridXComponent;
import org.efaps.ui.wicket.components.values.DropDownField;
import org.efaps.ui.wicket.components.values.IValueConverter;
import org.efaps.ui.wicket.models.field.IUIElement;
import org.efaps.ui.wicket.models.field.set.UIFieldSet;
import org.efaps.ui.wicket.models.objects.AbstractUIObject;
import org.efaps.ui.wicket.models.objects.DropDownOption;
import org.efaps.ui.wicket.models.objects.ISelectedRowObject;
import org.efaps.ui.wicket.models.objects.UIFieldForm;
import org.efaps.ui.wicket.models.objects.UIForm;
import org.efaps.ui.wicket.models.objects.UIForm.Element;
import org.efaps.ui.wicket.models.objects.UIForm.ElementType;
import org.efaps.ui.wicket.models.objects.UIForm.FormRow;
import org.efaps.ui.wicket.models.objects.grid.GridRow;
import org.efaps.ui.wicket.models.objects.grid.UIGrid;
import org.efaps.ui.wicket.pages.error.ErrorPage;
import org.efaps.ui.wicket.request.EFapsRequest;
import org.efaps.ui.wicket.request.EFapsRequestParametersAdapter;
import org.efaps.util.EFapsException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Class for a form. Needed for file upload.
 *
 * @author The eFaps Team
 */
public class FormContainer
    extends Form<Object>
    implements IDateListener
{

    /**
     * Needed for serialization.
     */
    private static final long serialVersionUID = 1L;

    /**
     * Logging instance used in this class.
     */
    private static final Logger LOG = LoggerFactory.getLogger(FormContainer.class);

    /**
     * Url for the action that must be called.
     */
    private String actionUrl;

    /**
     * Set contains the date components of this formpanel.
     */
    private final Set<DateTimePanel> dateComponents = new HashSet<>();

    /**
     * Set contains value Converters.
     */
    private final Set<IValueConverter> valueConverters = new LinkedHashSet<>();

    /**
     * Constructor setting the wicket id of this component.
     *
     * @param _wicketId wicket id of this component
     */
    public FormContainer(final String _wicketId)
    {
        super(_wicketId);
    }

    /**
     * On component tag.
     *
     * @param _tag the tag
     *
     * @see org.apache.wicket.markup.html.form.Form
     *      #onComponentTag(org.apache.wicket.markup.ComponentTag)
     */
    @Override
    protected void onComponentTag(final ComponentTag _tag)
    {
        if (this.actionUrl == null) {
            this.actionUrl = urlFor(getRequestCycle().getActiveRequestHandler()).toString();
        }
        super.onComponentTag(_tag);
        if (getPage().getDefaultModelObject() != null) {
            // only on SearchMode we want normal submit, in any other case we
            // use AjaxSubmit
            if (getPage().getDefaultModelObject() instanceof AbstractUIObject
                            && !((AbstractUIObject) getPage().getDefaultModelObject()).isSearchMode()) {
                //_tag.put("onSubmit", "return false;");
                _tag.put("action", "");
            }
        }
    }

    /**
     * This is the getter method for the instance variable {@link #actionUrl}.
     *
     * @return value of instance variable {@link #actionUrl}
     */
    @Override
    public String getActionUrl()
    {
        return this.actionUrl;
    }

    /**
     * Add a date component.
     *
     * @param _dateTimePanel date picker
     */
    @Override
    public void addDateComponent(final DateTimePanel _dateTimePanel)
    {
        this.dateComponents.add(_dateTimePanel);
    }

    /**
     * Getter method for instance variable {@link #dateComponents}.
     *
     * @return instance variable {@link #dateComponents}
     */
    @Override
    public Set<DateTimePanel> getDateComponents()
    {
        return this.dateComponents;
    }

    /**
     * @param _valueConverter value Converter to be added
     */
    public void addValueConverter(final IValueConverter _valueConverter)
    {
        this.valueConverters.add(_valueConverter);
    }

    /**
     * Getter method for the instance variable {@link #valueConverters}.
     *
     * @return value of instance variable {@link #valueConverters}
     */
    public Set<IValueConverter> getValueConverters()
    {
        return this.valueConverters;
    }

    /**
     * @param _valueConverter valueConverter to be removed
     */
    public void removeValueConverter(final IValueConverter _valueConverter)
    {
        this.valueConverters.remove(_valueConverter);
    }

    /**
     * Handle the multipart to store the files and parameters in the context also.
     * @return true if multipart
     */
    @Override
    protected boolean handleMultiPart()
    {
        final boolean ret = super.handleMultiPart();
        try {
            if (isMultiPart() && getRequest() instanceof MultipartServletWebRequest) {
                for (final Entry<String, List<FileItem>> entry : ((MultipartServletWebRequest) getRequest()).getFiles()
                                .entrySet()) {
                    int i = -1;
                    if (entry.getValue().size() > 1) {
                        i = 0;
                    }
                    for (final FileItem fileItem : entry.getValue()) {
                        final String key = i > -1 ? entry.getKey() + "_" + i : entry.getKey();
                        final FileParameter parameter = new FileParameter(key, fileItem);
                        Context.getThreadContext().getFileParameters().put(key, parameter);
                        if (i > -1) {
                            i++;
                        }
                    }
                }

                final Map<String, String[]> parameters = new HashMap<>();
                final IRequestParameters reqPara = getRequest().getRequestParameters();
                for (final String name : reqPara.getParameterNames()) {
                    final List<StringValue> values = reqPara.getParameterValues(name);
                    final String[] valArray = new String[values.size()];
                    int i = 0;
                    for (final StringValue value : values) {
                        valArray[i] = value.toString();
                        i++;
                    }
                    parameters.put(name, valArray);
                }
                Context.getThreadContext().getParameters().putAll(parameters);
            }
        } catch (final EFapsException e) {
            throw new RestartResponseAtInterceptPageException(new ErrorPage(e));
        }
        return ret;
    }

    @Override
    public void process(final IFormSubmitter _submittingComponent)
    {
        // if their is a GridXComponent convert the ids to oids
        visitChildren(GridXComponent.class, (_gridX, _visit) -> {
            try {
                final EFapsRequestParametersAdapter parameters = (EFapsRequestParametersAdapter) getRequest()
                                .getRequestParameters();
                final List<StringValue> selectedRows = parameters.getParameterValues("selectedRow");
                if (CollectionUtils.isNotEmpty(selectedRows)) {
                    final List<StringValue> newValues = new ArrayList<>();
                    for (final StringValue value : selectedRows) {
                        final UIGrid uiGrid = (UIGrid) _gridX.getDefaultModelObject();
                        final GridRow row = uiGrid.getRow4Id(value.toString());
                        newValues.add(StringValue.valueOf(row.getInstance().getOid()));
                    }
                    parameters.setParameterValues("selectedRow", newValues);
                }
            } catch (final StringValueConversionException | EFapsException e) {
                FormContainer.LOG.error("Catched exeption", e);
            }
        });

        // for a dropdown add the previous value as a parameter
        if (_submittingComponent instanceof AjaxFormSubmitter && ((AjaxFormSubmitter) _submittingComponent)
                        .getFormSubmittingComponent() != null && ((AjaxFormSubmitter) _submittingComponent)
                                        .getFormSubmittingComponent() instanceof DropDownField) {
            final Object object = ((DropDownField) ((AjaxFormSubmitter) _submittingComponent)
                            .getFormSubmittingComponent()).getDefaultModelObject();
            if (object instanceof DropDownOption) {
                final String key = ((DropDownField) ((AjaxFormSubmitter) _submittingComponent)
                                .getFormSubmittingComponent()).getInputName();
                ((EFapsRequestParametersAdapter) ((EFapsRequest) RequestCycle.get().getRequest())
                                .getRequestParameters()).addParameterValue(key + "_eFapsPrevious",
                                                ((DropDownOption) object).getValue());
            }
        }
        if (getPage().getDefaultModelObject() instanceof ISelectedRowObject
                        && ((ISelectedRowObject) getPage().getDefaultModelObject()).hasSelected()) {
            final EFapsRequestParametersAdapter parameters = (EFapsRequestParametersAdapter) getRequest()
                            .getRequestParameters();
            if (parameters.getParameterValues("selectedRow") == null) {
                parameters.setParameterValues("selectedRow",
                                ((ISelectedRowObject) getPage().getDefaultModelObject()).getSelected());
            }
        }
        super.process(_submittingComponent);
        // it must be ensured that the counter for sets is rested or we have big problems
        resetSetCounter();
    }

    /**
     * Reset the counters for sets.
     */
    private void resetSetCounter()
    {
        if (getPage().getDefaultModelObject() instanceof UIForm) {
            for (final Element element : ((UIForm) getPage().getDefaultModelObject()).getElements()) {
                if (element.getType().equals(ElementType.FORM)) {
                    final Iterator<FormRow> iter = ((UIForm.FormElement) element.getElement()).getRowModels();
                    while (iter.hasNext()) {
                        final FormRow row = iter.next();
                        for (final IUIElement uiElement : row.getValues()) {
                            if (uiElement instanceof UIFieldSet) {
                                ((UIFieldSet) uiElement).resetIndex();
                            }
                        }
                    }
                } else if (element.getType().equals(ElementType.SUBFORM)) {
                    for (final Element nElement : ((UIFieldForm) element.getElement()).getElements()) {
                        if (nElement.getType().equals(ElementType.FORM)) {
                            final Iterator<FormRow> iter = ((UIForm.FormElement) nElement.getElement()).getRowModels();
                            while (iter.hasNext()) {
                                final FormRow row = iter.next();
                                for (final IUIElement uiElement : row.getValues()) {
                                    if (uiElement instanceof UIFieldSet) {
                                        ((UIFieldSet) uiElement).resetIndex();
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}


/*
 * Copyright 2003 - 2014 The eFaps Team
 *
 * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Revision:        $Rev$
 * Last Changed:    $Date$
 * Last Changed By: $Author$
 */

package org.efaps.ui.filter;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.efaps.db.Context;
import org.efaps.jaas.AppAccessHandler;
import org.efaps.jaas.LoginHandler;
import org.efaps.util.EFapsException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author The eFaps Team
 * @version $Id$
 */
public abstract class AbstractAuthenticationFilter
    extends AbstractFilter
{
    /**
     * Logging instance used in this class.
     */
    private static final Logger LOG = LoggerFactory.getLogger(AbstractAuthenticationFilter.class);

    /**
     * Login handler used to check the login in method {@link #checkLogin}.
     *
     * @see #init
     * @see #checkLogin
     */
    private LoginHandler loginHandler = null;

    /**
     * Called by the web container to indicate to a filter that it is being
     * placed into service. The servlet container calls the init method exactly
     * once after instantiating the filter. The init method must complete
     * successfully before the filter is asked to do any filtering work.
     *
     * The web container cannot place the filter into service if the init method
     * either 1.Throws a ServletException 2.Does not return within a time period
     * defined by the web container // sets the login handler
     *
     * @param _filterConfig filter configuration instance
     * @see #INIT_PARAM_TITLE
     * @see #title
     * @see #INIT_PARAM_APPLICATION
     * @see #loginhandler
     * @throws ServletException on error
     */
    @Override
    public void init(final FilterConfig _filterConfig)
        throws ServletException
    {
        super.init(_filterConfig);
        try {
            this.loginHandler = new LoginHandler(AppAccessHandler.getApplicationKey());
        } catch (final EFapsException e) {
            AbstractAuthenticationFilter.LOG.error("AppAccessHandler", e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void destroy()
    {
        super.destroy();
        this.loginHandler = null;
    }

    /**
     * If the current user is already logged in, nothing is filtered.
     * @param _request      HttpServletRequest
     * @param _response     HttpServletResponse
     * @param _chain        FilterChain
     * @see #doAuthenticate
     * @throws IOException on error
     * @throws ServletException on error
     */
    @Override
    protected void doFilter(final HttpServletRequest _request,
                            final HttpServletResponse _response,
                            final FilterChain _chain)
        throws IOException, ServletException
    {
        if (isLoggedIn(_request)) {
            _chain.doFilter(_request, _response);
        } else {
            doAuthenticate(_request, _response, _chain);
        }
    }

    /**
     * Authentication method.
     * @param _request      HttpServletRequest
     * @param _response     HttpServletResponse
     * @param _chain        FilterChain
     * @see #doAuthenticate
     * @throws IOException on error
     * @throws ServletException on error
     */
    protected abstract void doAuthenticate(final HttpServletRequest _request,
                                           final HttpServletResponse _response,
                                           final FilterChain _chain)
        throws IOException, ServletException;

    /**
     * Checks if the user with given name and password is allowed to login.
     *
     * @param _name user name to check
     * @param _passwd password to check
     * @return true if the user is allowed to login (name and password is
     *         correct), otherweise false
     * @see #loginhandler
     * @see #doFilter
     */
    protected boolean checkLogin(final String _name,
                                 final String _passwd)
    {

        boolean loginOk = false;

        Context context = null;
        try {
            context = Context.begin();

            boolean ok = false;

            try {
                if (this.loginHandler.checkLogin(_name, _passwd) != null) {
                    loginOk = true;
                }
                ok = true;
            } finally {

                if (ok && Context.isTMActive()) {
                    Context.commit();
                } else {
                    if (Context.isTMMarkedRollback()) {
                        AbstractAuthenticationFilter.LOG.error("transaction is marked to roll back");
                    } else {
                        AbstractAuthenticationFilter.LOG.error("transaction manager in undefined status");
                    }
                    Context.rollback();
                }
            }
        } catch (final EFapsException e) {
            AbstractAuthenticationFilter.LOG.error("could not check name and password", e);
        } finally {
            context.close();
        }
        return loginOk;
    }
}


/*
 * Copyright 2003 - 2016 The eFaps Team
 *
 * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package org.efaps.ui.wicket.components.form.command;

import java.util.List;
import java.util.Map;

import org.apache.wicket.RestartResponseException;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.model.IModel;
import org.efaps.admin.event.Return;
import org.efaps.admin.event.Return.ReturnValues;
import org.efaps.ui.wicket.components.button.AjaxButton;
import org.efaps.ui.wicket.models.field.UICmdField;
import org.efaps.ui.wicket.models.objects.AbstractUIPageObject;
import org.efaps.ui.wicket.pages.error.ErrorPage;
import org.efaps.ui.wicket.resources.EFapsContentReference;
import org.efaps.util.EFapsException;

/**
 * TODO comment!
 *
 * @author The eFaps Team
 */
public class AjaxExecuteLink
    extends AjaxButton<UICmdField>
{
    /**
    * Needed for serialization.
    */
    private static final long serialVersionUID = 1L;

    /**
     * Instantiates a new ajax execute link.
     *
     * @param _wicketId wicket id for this component
     * @param _model the model
     * @param _reference the reference
     * @param _label the label
     */
    public AjaxExecuteLink(final String _wicketId,
                           final IModel<UICmdField> _model,
                           final EFapsContentReference _reference,
                           final String _label)
    {
        super(_wicketId, _model, _reference, _label);
    }

    @Override
    protected boolean getDefaultProcessing()
    {
        return false;
    }

    /**
     * On submit.
     *
     * @param _target   AjaxRequestTarget
     */
    @Override
    public void onRequest(final AjaxRequestTarget _target)
    {
        final UICmdField uiObject = (UICmdField) getDefaultModelObject();
        final StringBuilder snip = new StringBuilder();
        try {
            final AbstractUIPageObject pageObject = (AbstractUIPageObject) getPage().getDefaultModelObject();
            final Map<String, String> uiID2Oid = pageObject == null ? null : pageObject.getUiID2Oid();
            final List<Return> returns = uiObject.executeEvents(null, uiID2Oid);
            for (final Return oneReturn : returns) {
                if (oneReturn.contains(ReturnValues.SNIPLETT)) {
                    snip.append(oneReturn.get(ReturnValues.SNIPLETT));
                }
            }
        } catch (final EFapsException e) {
            throw new RestartResponseException(new ErrorPage(e));
        }
        _target.appendJavaScript(snip.toString());
    }
}


/*
 * Copyright 2003 - 2019 The eFaps Team
 *
 * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package org.efaps.ui.wicket.components.gridx;

import com.github.openjson.JSONArray;
import com.github.openjson.JSONObject;
import com.github.openjson.JSONTokener;

import java.text.DecimalFormat;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.StringEscapeUtils;
import org.apache.wicket.IRequestListener;
import org.apache.wicket.Page;
import org.apache.wicket.RestartResponseException;
import org.apache.wicket.ajax.AjaxEventBehavior;
import org.apache.wicket.ajax.attributes.CallbackParameter;
import org.apache.wicket.behavior.Behavior;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.MarkupStream;
import org.apache.wicket.markup.head.CssHeaderItem;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.html.WebComponent;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.wicket.request.resource.CssResourceReference;
import org.apache.wicket.request.resource.ResourceReference;
import org.apache.wicket.util.string.StringValue;
import org.apache.wicket.util.string.StringValueConversionException;
import org.efaps.admin.ui.AbstractCommand;
import org.efaps.admin.ui.AbstractCommand.Target;
import org.efaps.admin.ui.AbstractMenu;
import org.efaps.admin.ui.Menu;
import org.efaps.api.ci.UITableFieldProperty;
import org.efaps.api.ui.FilterBase;
import org.efaps.api.ui.HRef;
import org.efaps.db.Context;
import org.efaps.ui.wicket.behaviors.dojo.AbstractDojoBehavior;
import org.efaps.ui.wicket.components.efapscontent.StaticImageComponent;
import org.efaps.ui.wicket.components.gridx.behaviors.CheckoutBehavior;
import org.efaps.ui.wicket.components.gridx.behaviors.OpenInOpenerBehavior;
import org.efaps.ui.wicket.components.gridx.behaviors.OpenModalBehavior;
import org.efaps.ui.wicket.components.gridx.behaviors.PrintBehavior;
import org.efaps.ui.wicket.components.gridx.behaviors.ReloadBehavior;
import org.efaps.ui.wicket.components.gridx.behaviors.SubmitBehavior;
import org.efaps.ui.wicket.components.gridx.behaviors.SubmitModalBehavior;
import org.efaps.ui.wicket.components.menutree.CallUpdateTreeMenuBehavior;
import org.efaps.ui.wicket.models.objects.PagePosition;
import org.efaps.ui.wicket.models.objects.UIForm;
import org.efaps.ui.wicket.models.objects.UITable;
import org.efaps.ui.wicket.models.objects.grid.CacheKey;
import org.efaps.ui.wicket.models.objects.grid.GridCell;
import org.efaps.ui.wicket.models.objects.grid.GridColumn;
import org.efaps.ui.wicket.models.objects.grid.GridRow;
import org.efaps.ui.wicket.models.objects.grid.UIFieldGrid;
import org.efaps.ui.wicket.models.objects.grid.UIGrid;
import org.efaps.ui.wicket.pages.content.form.FormPage;
import org.efaps.ui.wicket.pages.content.grid.GridPage;
import org.efaps.ui.wicket.pages.content.structurbrowser.StructurBrowserPage;
import org.efaps.ui.wicket.pages.content.table.TablePage;
import org.efaps.ui.wicket.pages.contentcontainer.ContentContainerPage;
import org.efaps.ui.wicket.pages.error.ErrorPage;
import org.efaps.ui.wicket.resources.EFapsContentReference;
import org.efaps.ui.wicket.util.Configuration;
import org.efaps.ui.wicket.util.Configuration.ConfigAttribute;
import org.efaps.ui.wicket.util.DateUtil;
import org.efaps.ui.wicket.util.DojoClass;
import org.efaps.ui.wicket.util.DojoClasses;
import org.efaps.ui.wicket.util.DojoWrapper;
import org.efaps.util.EFapsException;
import org.efaps.util.RandomUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author The eFaps Team
 */
public class GridXComponent
    extends WebComponent
    implements IRequestListener
{

    /**
     * Reference to the stylesheet.
     */
    public static final ResourceReference CSS = new CssResourceReference(AbstractDojoBehavior.class,
                    "gridx/resources/claro/Gridx.css");

    /** The Constant serialVersionUID. */
    private static final long serialVersionUID = 1L;

    /**
     * Logger for this class.
     */
    private static final Logger LOG = LoggerFactory.getLogger(GridXComponent.class);

    /**
     * Instantiates a new grid component.
     *
     * @param _wicketId the wicket id
     * @param _model the model
     */
    public GridXComponent(final String _wicketId,
                          final IModel<UIGrid> _model)
    {
        super(_wicketId, _model);
        setOutputMarkupId(true);
    }

    @Override
    public void renderHead(final IHeaderResponse _response)
    {
        super.renderHead(_response);
        _response.render(CssHeaderItem.forReference(GridXComponent.CSS));
    }

    @Override
    @SuppressWarnings("checkstyle:MethodLength")
    public void onComponentTagBody(final MarkupStream _markupStream,
                                   final ComponentTag _openTag)
    {
        try {
            final UIGrid uiGrid = (UIGrid) getDefaultModelObject();
            final boolean isField = uiGrid instanceof UIFieldGrid;

            final Set<DojoClass> dojoClasses = new HashSet<>();

            Collections.addAll(dojoClasses, DojoClasses.array, DojoClasses.lang, DojoClasses.json, DojoClasses.aspect,
                            DojoClasses.query, DojoClasses.domConstruct, DojoClasses.win, DojoClasses.domStyle,
                            DojoClasses.ready, DojoClasses.on, DojoClasses.registry, DojoClasses.Memory,
                            DojoClasses.Grid, DojoClasses.VirtualVScroller, DojoClasses.ColumnResizer,
                            DojoClasses.HScroller, DojoClasses.SingleSort, DojoClasses.SyncCache,
                            DojoClasses.HeaderDialog, DojoClasses.MoveColumn, DojoClasses.SelectColumn,
                            DojoClasses.SelectCell, DojoClasses.DnDColumn, DojoClasses.HiddenColumns,
                            DojoClasses.GridConfig, DojoClasses.GridSort, DojoClasses.Summary, DojoClasses.GridTree,
                            DojoClasses.GridQuickFilter, DojoClasses.GridAggregate,
                            DojoClasses.Bar, DojoClasses.Persist, DojoClasses.Filter, DojoClasses.FilterBar,
                            DojoClasses.DropDownButton, DojoClasses.TextBox, DojoClasses.TooltipDialog,
                            DojoClasses.domGeom, DojoClasses.ColumnLock, DojoClasses.DateLocale);

            final DecimalFormat format = (DecimalFormat) DecimalFormat.getInstance(
                            Context.getThreadContext().getLocale());
            final char decSep = format.getDecimalFormatSymbols().getDecimalSeparator();
            final char grpSep = format.getDecimalFormatSymbols().getGroupingSeparator();

            final StringBuilder js = new StringBuilder()
                .append("var cpn = function(_attr, _itemA, _itemB) {\n")
                .append("    var valA = _itemA.hasOwnProperty(_attr + '_sort')")
                    .append(" ? _itemA[_attr + '_sort'] : parseFloat(String(_itemA[_attr]).replace('")
                    .append(grpSep).append("',''").append(").replace('").append(decSep).append("','.'").append("));\n")
                .append("    var valB = _itemB.hasOwnProperty(_attr + '_sort') ")
                    .append(" ? _itemB[_attr + '_sort'] : parseFloat(String(_itemB[_attr]).replace('")
                    .append(grpSep).append("',''").append(").replace('").append(decSep).append("','.'").append("));\n")
                .append("    return valA < valB ? -1 : (valA > valB ? 1 : 0);\n")
                .append("}\n")
                .append("var cp = function(_attr, _itemA, _itemB) {\n")
                .append("    var strA = _itemA.hasOwnProperty(_attr + '_sort')")
                    .append(" ? _itemA[_attr + '_sort'] : _itemA[_attr];\n")
                .append("    var strB = _itemB.hasOwnProperty(_attr + '_sort') ")
                    .append("? _itemB[_attr + '_sort'] : _itemB[_attr];\n")
                .append("    return strA < strB ? -1 : (strA > strB ? 1 : 0);\n")
                .append("}\n")
                .append("var dp = function(_value){ \n")
                    .append("return dateLocale.parse(_value, { datePattern: \"").append(DateUtil.getDatePattern())
                        .append("\", selector: \"date\" });")
                    .append("}\n")
                .append("var dtp = function(_value){ \n")
                    .append("return dateLocale.parse(_value, { datePattern: \"").append(DateUtil.getDateTimePattern())
                        .append("\", selector: \"date\", am: \"AM\", pm: \"PM\" });")
                .append("}\n")
                .append("var cpd = function(_attr, _itemA, _itemB) {\n")
                    .append("    return dp(_itemA[_attr]) - dp(_itemB[_attr]);\n")
                    .append("}\n")
                .append("var cpdt = function(_attr, _itemA, _itemB) {\n")
                    .append("    return dtp(_itemA[_attr]) - dtp(_itemB[_attr]);\n")
                    .append("}\n")
                .append("var store = new Memory({\n")
                .append("data: ")
                .append(GridXComponent.getDataJS(uiGrid))
                .append("});\n");

            if (uiGrid.isStructureTree()) {
                js.append("store.hasChildren = function(id, item){\n")
                    .append("return item && item.children && item.children.length;\n")
                    .append("};\n")
                    .append("store.getChildren = function(item, req){\n")
                    .append("return item.children;\n")
                    .append("};\n");
            }

            js.append("var structure = [\n");

            boolean first = true;
            boolean aggregate = false;
            int j = 0;
            final Set<Long> checkOutCols = new HashSet<>();
            final Set<Long> linkCols = new HashSet<>();

            for (final GridColumn column : uiGrid.getColumns()) {
                if (first) {
                    first = false;
                } else {
                    js.append(",");
                }
                js.append("{ id:'").append(column.getField().getId()).append("',")
                    .append(" field:'").append(column.getFieldName()).append("',")
                    .append(" name:'").append(column.getLabel()).append("'\n");

                if (uiGrid.isStructureTree() && uiGrid.getStructurBrowserField().equals(column.getFieldName())) {
                    js.append(",expandLevel:'all'");
                }

                if (!"left".equals(column.getField().getAlign())) {
                    js.append(", style:'text-align:right'");
                }
                if (column.getFieldConfig().getField().getReference() != null
                                && StringUtils.containsIgnoreCase(column.getFieldConfig().getField().getReference(),
                                                HRef.CHECKOUT.toString())) {
                    final StaticImageComponent icon = new StaticImageComponent("icon",
                                    new EFapsContentReference(column.getFieldConfig().getField().getIcon()));
                    js.append(", decorator: function(data, rowId, visualIndex, cell){\n")
                        .append("return '' + data + '';\n")
                        .append("}\n");
                    checkOutCols.add(column.getFieldConfig().getField().getId());
                } else if (column.getFieldConfig().getField().getReference() != null) {
                    switch (uiGrid.getPagePosition()) {
                        case POPUP:
                            linkCols.add(column.getFieldConfig().getField().getId());
                            js.append(", decorator: function(data, rowId, visualIndex, cell){\n")
                                .append("return '' + data + '';\n")
                                .append("}\n");
                            break;
                        case CONTENT:
                        case TREE:
                            js.append(", decorator: function(data, rowId, visualIndex, cell){\n")
                                .append("return '' + data + '';\n")
                                .append("}\n");
                            break;
                        case CONTENTMODAL:
                        case TREEMODAL:
                        default:
                            break;
                    }
                }
                if (FilterBase.DATABASE.equals(column.getFilter().getBase())) {
                    js.append(", dialog: 'fttd_").append(column.getField().getId())
                        .append("', headerClass:'eFapsFiltered'\n");
                }
                if (column.getDataType() != null) {
                    js.append(", dataType: '").append(column.getDataType()).append("'\n");
                    switch (column.getDataType()) {
                        case "date":
                            js.append(", dateParser: dp,\n")
                                .append("comparator: cpd\n");
                            break;
                        case "datetime":
                            js.append(", datetimeParser: dtp,\n")
                                .append("comparator: cpdt\n");
                            break;
                        case "number":
                            js.append(", comparator: cpn\n");
                            break;
                        case "enum":
                            js.append(", enumOptions: ['").append(StringUtils.join(column.getEnumValues(), "','"))
                                .append("']").append(", comparator: cp\n");
                            break;
                        default:
                            js.append(", comparator: cp\n");
                            break;
                    }
                } else {
                    js.append(", comparator: cp\n");
                }

                if (column.getField().containsProperty(UITableFieldProperty.AGGREGATE)) {
                    aggregate = true;
                    js.append(", aggregate: '")
                        .append(column.getField().getProperty(UITableFieldProperty.AGGREGATE))
                        .append("'");
                }
                js.append("}");
                j++;
            }

            js.append("];\n")
                .append("").append(getMenu(dojoClasses))
                .append("var grid = Grid({")
                .append("id: '").append(getGridId()).append("',")
                .append("cacheClass: Cache,")
                .append("store: store,")
                .append("structure: structure,\n");

            if (isField) {
                js.append("autoHeight: true,\n");
            } else {
                js.append("barTop: [\n");

                if (dojoClasses.contains(DojoClasses.MenuBar)) {
                    js.append("{plugin: pMenuBar, style: 'text-align: left;'}, \n");
                }

                js.append("{pluginClass: GridQuickFilter, style: 'text-align: center;'}, \n")
                    .append("{ pluginClass: \"efaps/GridConfig\", style: 'text-align: right;', printItems: [")
                    .append(getPrintMenuItems(dojoClasses)).append("],\n")
                    .append("reload : ").append(getBehavior(ReloadBehavior.class).getCallbackFunction())
                    .append("} \n")
                    .append("],\n")
                    .append("barBottom: [\n")
                    .append("{pluginClass: Summary, style: 'text-align: right;'}\n")
                    .append("],\n");
            }

            js.append("modules: [")
                .append("VirtualVScroller, ColumnLock, ColumnResizer, SingleSort, MoveColumn, SelectColumn, ")
                .append("SelectCell, DnDColumn, HeaderDialog, Bar, HScroller, HiddenColumns, Persist");

            if (uiGrid.isStructureTree()) {
                js.append(", Tree");
            }

            if (!isField) {
                js.append(", Filter, FilterBar");
            }

            if (aggregate) {
                js.append(", GridAggregate");
            }

            if (uiGrid.isShowCheckBoxes()) {
                Collections.addAll(dojoClasses, DojoClasses.IndirectSelect, DojoClasses.RowHeader,
                                DojoClasses.ExtendedSelectRow);
                js.append(", IndirectSelect, SelectRow, RowHeader");
            }

            js.append("],\n")
                .append(getPersistenceScript(uiGrid))
                .append("modelExtensions: [\n")
                    .append("GridSort\n")
                    .append("]\n")
                .append("});")
                .append("grid.placeAt('").append(getMarkupId(true)).append("');\n");

            for (final Long checkOutCol : checkOutCols) {
                js.append("aspect.after(grid.body,'onAfterRow', function(_row){\n")
                    .append("var rowId = _row.id;\n")
                    .append("var cell =  _row.cell('").append(checkOutCol).append("', false);\n")
                    .append("var colId = cell.column.index();\n")
                    .append("query(\".eFapsCheckout\", cell.node()).on(\"click\", function(e) {\n")
                    .append(getBehavior(CheckoutBehavior.class).getCallbackFunctionBody(
                                CallbackParameter.explicit("rowId"), CallbackParameter.explicit("colId")))
                    .append("});\n")
                    .append("},true);\n");
            }

            for (final Long linkCol : linkCols) {
                js.append("aspect.after(grid.body,'onAfterRow', function(_row){\n")
                    .append("var rowId = _row.id;\n")
                    .append("var cell =  _row.cell('").append(linkCol).append("', false);\n")
                    .append("var colId = cell.column.index();\n")
                    .append("query(\"a\", cell.node()).on(\"click\", function(e) {\n")
                    .append(getBehavior(OpenInOpenerBehavior.class).getCallbackFunctionBody(
                                CallbackParameter.explicit("rowId"), CallbackParameter.explicit("colId")))
                    .append("});\n")
                    .append("},true);\n");
            }

            js.append("grid.startup();\n")
                .append("grid.comparators = {\n")
                .append("date : cpd,\n")
                .append("numeric : cpn,\n")
                .append("datetime : cpdt,\n")
                .append("string : cp\n")
                .append("}\n");

            if (!isField) {
                js.append("var rg = function() {\n")
                    .append("var bar = query('.eFapsFrameTitle') [0];\n")
                    .append("var pos = domGeom.position(bar);\n")
                    .append("var vs = win.getBox();\n")
                    .append("var hh = vs.h - pos.h - pos.y;\n")
                    .append("var ft = query('.eFapsFooter')[0];\n")
                    .append("if (typeof ft != 'undefined') {\n")
                    .append("var ftPos = domGeom.position(ft);\n")
                    .append("hh = hh - ftPos.h;\n")
                    .append("\n")
                    .append("}\n")
                    .append("registry.byId('").append(getGridId()).append("').resize({h:hh});")
                    .append("}\n")
                    .append("ready(function(){")
                    .append("rg();\n")
                    .append("});")
                    .append("on(window, 'resize', function() {\n")
                    .append("rg();\n")
                    .append("});\n");
            }

            if (uiGrid.isShowCheckBoxes()) {
                js.append("aspect.after(grid.select.row, 'onSelectionChange', function (_defferd) {\n")
                    .append("query(\"input[name='selectedRow']\").forEach(domConstruct.destroy);\n")
                    .append("array.forEach(registry.byId('").append(getGridId())
                        .append("').select.row.getSelected(), function (_item) {\n")
                    .append("domConstruct.create('input', {\n")
                    .append("type: 'hidden',\n")
                    .append("name:'selectedRow',\n")
                    .append("value: _item\n")
                    .append("},\n")
                    .append("'").append(findParent(Form.class).getMarkupId(true)).append("');\n")
                    .append("});\n")
                    .append("});\n")
                    .append("grid.prevSelected = [];\n")
                    .append("var ftb = function () {\n")
                    .append("registry.byId('").append(getGridId())
                        .append("').prevSelected = registry.byId('").append(getGridId())
                        .append("').select.row.getSelected();\n")
                    .append("};\n")
                    .append("var fta = function () {\n")
                    .append("array.forEach(registry.byId('").append(getGridId())
                        .append("').prevSelected, function (_item) {\n")
                    .append("registry.byId('").append(getGridId()).append("').select.row.selectById(_item);\n")
                    .append("});\n")
                    .append("};\n");
                if (!isField) {
                    js.append("aspect.before(grid.filter, 'setFilter', ftb);\n")
                        .append("aspect.before(grid.filter, 'clearFilter', ftb);\n")
                        .append("aspect.after(grid.filter, 'setFilter', fta);\n")
                        .append("aspect.after(grid.filter, 'clearFilter', fta);\n");
                }
            }

            final StringBuilder html = new StringBuilder().append("");

            replaceComponentTagBody(_markupStream, _openTag, html);
        } catch (final EFapsException e) {
            GridXComponent.LOG.error("Catched error", e);
        }
    }

    /**
     * Gets the persistence script.
     *
     * @param _uiGrid the ui grid
     * @return the persistence script
     * @throws EFapsException the e faps exception
     */
    protected CharSequence getPersistenceScript(final UIGrid _uiGrid)
        throws EFapsException
    {
        final StringBuilder ret = new StringBuilder();
        ret.append("persistGet: function(_key) {");
        if (Context.getThreadContext().containsUserAttribute(_uiGrid.getCacheKey(CacheKey.GRIDX))) {
            final Set<Long> colIds = _uiGrid.getColumns()
                            .stream()
                            .map(col -> col.getField().getId())
                            .collect(Collectors.toSet());
            boolean add = true;
            final JSONObject json = new JSONObject(new JSONTokener(Context.getThreadContext().getUserAttribute(
                            _uiGrid.getCacheKey(CacheKey.GRIDX))));
            final JSONArray columnsArray = json.getJSONArray("column");
            for (int i = 0; i < columnsArray.length(); i++) {
                final JSONObject colObj = (JSONObject) columnsArray.get(i);
                final Long colid = colObj.getLong("id");
                if (!colIds.contains(colid)) {
                    add = false;
                    break;
                }
            }
            if (add && !json.isNull("filterBar")) {
                final JSONObject filterBar =  json.getJSONObject("filterBar");
                if (!filterBar.isNull("conditions")) {
                    final JSONArray conditionsArray = filterBar.getJSONArray("conditions");
                    for (int i = 0; i < conditionsArray.length(); i++) {
                        final JSONObject colObj = (JSONObject) conditionsArray.get(i);
                        if (colObj.has("colId")) {
                            final Long colid = colObj.optLong("colId");
                            if (colid > 0 && !colIds.contains(colid)) {
                                add = false;
                                break;
                            }
                        }
                    }
                }
            }
            if (add) {
                ret.append("return ").append(json.toString());
            }
        }
        ret.append("},\n")
            .append("persistPut: function(_key, _value, _options) {\n")
            .append("  var value;")
            .append("  if(_value && lang.isObject(_value)){\n")
            .append("    value = json.toJson(_value);\n")
            .append("  }else{\n")
            .append("    value = {expires: -1};\n")
            .append("  }\n")
            .append("top.eFaps.persistUserAttr('")
                .append(_uiGrid.getCacheKey(CacheKey.GRIDX)).append("', value);")
            .append("},\n");
        return ret;
    }

    /**
     * Gets the prints the menu items.
     *
     * @param _dojoClasses the dojo classes
     * @return the prints the menu items
     */
    protected CharSequence getPrintMenuItems(final Set<DojoClass> _dojoClasses)
    {
        final StringBuilder ret = new StringBuilder();
        Collections.addAll(_dojoClasses, DojoClasses.MenuItem);
        final PrintBehavior printBehavior = (PrintBehavior) getBehavior(PrintBehavior.class);
        final String[] mimes = new String[] {"PDF", "XLS"};
        for (final String mime : mimes) {
            if (ret.length() > 0) {
                ret.append(",");
            }
            ret.append("new MenuItem({\n")
                .append("label: \"").append(mime).append("\",\n")
                .append("iconClass:\"eFapsMenuIcon eFapsMenuIcon").append(mime.toUpperCase()).append("\",\n")
                .append("onClick: function(event) {\n")
                    .append("var g = registry.byId('grid');\n")
                    .append("var sr = g.select.row.getSelected();\n")
                    .append("if (sr.length == 0) {\n")
                    .append("array.forEach(g.rows(), function (_item, _id) {\n")
                    .append("sr[_id] = _item.id;\n")
                    .append("});\n")
                    .append("}\n")
                    .append("var cm = [];\n")
                    .append("array.forEach(g.columns(), function (_item, _id) {\n")
                    .append("cm[_id] = _item.id;\n")
                    .append("});\n")
                    .append(printBehavior.getCallbackFunctionBody(
                            CallbackParameter.resolved("MIME", "\"" + mime + "\""), CallbackParameter.explicit("sr"),
                            CallbackParameter.explicit("cm")))
                    .append("}\n")
                .append("})\n");
        }
        return ret;
    }

    /**
     * Gets the menu.
     *
     * @param _dojoClasses the dojo classes
     * @return the menu
     * @throws EFapsException on error
     */
    protected CharSequence getMenu(final Set<DojoClass> _dojoClasses)
        throws EFapsException
    {
        final StringBuilder ret = new StringBuilder();
        final UIGrid uiGrid = (UIGrid) getDefaultModelObject();
        if (!(uiGrid instanceof UIFieldGrid) && uiGrid.getCommand().getTargetMenu() != null) {
            Collections.addAll(_dojoClasses, DojoClasses.MenuBar, DojoClasses.DropDownMenu, DojoClasses.MenuItem,
                            DojoClasses.PopupMenuBarItem, DojoClasses.MenuBarItem);

            ret.append("var pMenuBar = new MenuBar({});\n");
            for (final AbstractCommand child : uiGrid.getCommand().getTargetMenu().getCommands()) {
                if (child.hasAccess(uiGrid.getCommand().getTargetMode(), uiGrid.getInstance())) {
                    if (child instanceof AbstractMenu) {
                        ret.append(getSubMenu((AbstractMenu) child, "pMenuBar"));
                    } else {
                        ret.append("pMenuBar.addChild(").append(getMenuItem(child, true)).append(");\n");
                    }
                }
            }
        }
        return ret;
    }

    /**
     * Gets the sub menu.
     *
     * @param _menu the menu
     * @param _parent the parent
     * @return the sub menu
     * @throws EFapsException on error
     */
    protected CharSequence getSubMenu(final AbstractMenu _menu,
                                      final String _parent)
        throws EFapsException
    {
        final String var = RandomUtil.randomAlphabetic(4);
        final StringBuilder js = new StringBuilder();
        js.append("var ").append(var).append(" = new DropDownMenu({});\n");
        final UIGrid uiGrid = (UIGrid) getDefaultModelObject();
        for (final AbstractCommand child : _menu.getCommands()) {
            if (child.hasAccess(uiGrid.getCommand().getTargetMode(), uiGrid.getInstance())) {
                if (child instanceof AbstractMenu) {
                    js.append(getSubMenu((AbstractMenu) child, var));
                } else {
                    js.append(var).append(".addChild(")
                        .append(getMenuItem(child, false))
                        .append(");\n");
                }
            }
        }
        js.append(_parent).append(".addChild(new PopupMenuBarItem({\n")
            .append("label: \"").append(StringEscapeUtils.escapeEcmaScript(_menu.getLabelProperty())).append("\",\n")
            .append(" popup: ").append(var).append("\n")
            .append(" }));\n");
        return js;
    }

    /**
     * Gets the menu item.
     *
     * @param _cmd the cmd
     * @param _menuBar the menu bar
     * @return the menu item
     */
    protected CharSequence getMenuItem(final AbstractCommand _cmd,
                                       final boolean _menuBar)
    {
        final UIGrid uiGrid = (UIGrid) getDefaultModelObject();
        final String rid = uiGrid.getRandom4ID(_cmd.getId());
        final StringBuilder js = new StringBuilder();
        if (_menuBar) {
            js.append("new MenuBarItem({\n");
        } else {
            js.append("new MenuItem({\n");
        }
        js.append(" label: \"")
                .append(StringEscapeUtils.escapeEcmaScript(_cmd.getLabelProperty())).append("\",\n")
                .append("onClick: function(event) {\n")
                .append("var rid = \"").append(rid).append("\";\n");

        if (Target.MODAL.equals(_cmd.getTarget())) {
            if (_cmd.isSubmit()) {
                js.append(getBehavior(SubmitModalBehavior.class).getCallbackFunctionBody(
                                CallbackParameter.explicit("rid")));
            } else {
                js.append(getBehavior(OpenModalBehavior.class).getCallbackFunctionBody(
                                CallbackParameter.explicit("rid")));
            }
        } else if (_cmd.isSubmit()) {
            js.append(getBehavior(SubmitBehavior.class).getCallbackFunctionBody(CallbackParameter.explicit("rid")));
        }
        js.append("}\n").append("})\n");
        return js;
    }

    /**
     * Gets the behavior.
     *
     * @param _class the class
     * @return the behavior
     */
    protected AjaxEventBehavior getBehavior(final Class<? extends Behavior> _class)
    {
        final GridXPanel panel = (GridXPanel) getParent();
        return panel.visitChildren(MenuItem.class, (_item,
         _visit) -> {
            final List<? extends Behavior> behaviors = _item.getBehaviors(_class);
            if (CollectionUtils.isNotEmpty(behaviors)) {
                _visit.stop((AjaxEventBehavior) behaviors.get(0));
            } else {
                _visit.stop();
            }
        });
    }

    @Override
    public void onRequest()
    {
        final StringValue rowId = getRequest().getRequestParameters().getParameterValue("rowId");
        final StringValue colId = getRequest().getRequestParameters().getParameterValue("colId");

        try {
            final UIGrid uiGrid = (UIGrid) getDefaultModelObject();
            final GridRow row = uiGrid.getRow4Id(rowId.toString());
            final GridCell cell = row.get(colId.toInt());

            if (cell.getInstance() != null) {
                Menu menu = null;
                try {
                    menu = Menu.getTypeTreeMenu(cell.getInstance().getType());
                } catch (final EFapsException e) {
                    throw new RestartResponseException(new ErrorPage(e));
                }
                if (menu == null) {
                    final Exception ex = new Exception("no tree menu defined for type "
                                    + cell.getInstance() == null ? "??"
                                                    : cell.getInstance().getType().getName());
                    throw new RestartResponseException(new ErrorPage(ex));
                }

                Page page;
                try {
                    switch (uiGrid.getPagePosition()) {
                        case TREE:
                            if (menu.getTargetTable() != null) {
                                if (menu.getTargetStructurBrowserField() == null) {
                                    if ("GridX".equals(Configuration.getAttribute(
                                                    ConfigAttribute.TABLE_DEFAULTTYPETREE))) {
                                        page = new GridPage(Model.of(UIGrid.get(menu.getUUID(), PagePosition.TREE)
                                                        .setCallInstance(cell.getInstance())));
                                    } else {
                                        final UITable uiTable = new UITable(menu.getUUID(), cell.getInstance().getOid())
                                                        .setPagePosition(PagePosition.TREE);
                                        page = new TablePage(Model.of(uiTable));
                                    }
                                } else {
                                    if ("GridX".equals(Configuration.getAttribute(
                                                    ConfigAttribute.STRUCBRWSR_DEFAULTTYPETREE))) {
                                        page = new GridPage(Model.of(UIGrid.get(menu.getUUID(), PagePosition.TREE)
                                                        .setCallInstance(cell.getInstance())));
                                    } else {
                                        page = new StructurBrowserPage(menu.getUUID(), cell.getInstance().getOid());
                                    }
                                }
                            } else {
                                final UIForm uiForm = new UIForm(menu.getUUID(), cell.getInstance().getOid())
                                                .setPagePosition(PagePosition.TREE);
                                page = new FormPage(Model.of(uiForm));
                            }
                            page.add(new CallUpdateTreeMenuBehavior(cell.getInstance()));
                            break;
                        case CONTENT:
                        default:
                            page = new ContentContainerPage(menu.getUUID(), cell.getInstance().getKey(),
                                            uiGrid.isStructureTree());
                            break;
                    }
                } catch (final EFapsException e) {
                    page = new ErrorPage(e);
                }
                this.setResponsePage(page);
            }
        } catch (final StringValueConversionException | EFapsException e) {
            GridXComponent.LOG.error("Catched error", e);
        }
    }

    /**
     * Gets the grid id.
     *
     * @return the grid id
     * @throws EFapsException on error
     */
    protected String getGridId()
        throws EFapsException
    {
        return ((UIGrid) getDefaultModelObject()).getMarkupId();
    }

    /**
     * Gets the data JS.
     *
     * @param _uiGrid the ui grid
     * @return the data JS
     * @throws EFapsException on error
     */
    public static CharSequence getDataJS(final UIGrid _uiGrid)
        throws EFapsException
    {
        final StringBuilder ret = new StringBuilder().append(" [\n");
        int i = 0;
        for (final GridRow row : _uiGrid.getValues()) {
            if (i > 0) {
                ret.append(",\n");
            }
            ret.append(getRowJS(row, String.valueOf(i)));
            i++;
        }
        ret.append("]\n");
        return ret;
    }

    public static CharSequence getRowJS(final GridRow _row, final String _id)
    {
        final StringBuilder ret = new StringBuilder();
        ret.append("{ id:'").append(_id).append("'");
        for (final GridCell cell : _row) {
            ret.append(",").append(cell.getFieldConfig().getName()).append(":").append("'")
                .append(StringEscapeUtils.escapeEcmaScript(StringEscapeUtils.escapeHtml4(cell.getValue())))
                .append("'");

            final Comparable<?> orderObject = (Comparable<?>) cell.getSortValue();
            if (orderObject != null) {
                final String orderVal;
                final boolean ps;
                if (orderObject instanceof Number) {
                    orderVal = ((Number) orderObject).toString();
                    ps = false;
                } else {
                    orderVal = String.valueOf(orderObject);
                    ps = true;
                }
                if (cell.getValue() != null && !cell.getValue().equals(orderVal)) {
                    ret.append(",").append(cell.getFieldConfig().getName()).append("_sort:")
                                    .append(ps ? "'" : "")
                                    .append(StringEscapeUtils.escapeEcmaScript(orderVal))
                                    .append(ps ? "'" : "");
                }
            }
        }
        if (CollectionUtils.isNotEmpty(_row.getChildren())) {
            ret.append(",children: [");
            int j = 0;
            for (final GridRow row : _row.getChildren()) {
                if (j > 0) {
                    ret.append(",");
                }
                ret.append(getRowJS(row, _id + "-" + j));
                j++;
            }
            ret.append("]");
        }
        ret.append("}");
        return ret;
    }

    /**
     * Gets the javascript.
     *
     * @param _uiGrid the ui grid
     * @return the javascript
     * @throws EFapsException on error
     */
    public static CharSequence getDataReloadJS(final UIGrid _uiGrid)
        throws EFapsException
    {
        final StringBuilder js = new StringBuilder()
            .append("var grid = registry.byId('grid');\n")
            .append("var items = ").append(GridXComponent.getDataJS(_uiGrid));

        final StringBuilder dialogJs = new StringBuilder();
        if (!_uiGrid.isColumnsUpToDate()) {
            // lazy setting of data type when first time data
            _uiGrid.setColumnsUpToDate(true);
            js.append("array.forEach(grid.structure, function(entry){\n");
            for (final GridColumn column : _uiGrid.getColumns()) {
                if (column.getDataType() != null) {
                    js.append("if ('").append(column.getField().getId()).append("'== entry.id) {\n")
                        .append("entry.dataType='").append(column.getDataType()).append("';\n")
                        .append("entry.comparator = grid.comparators.").append(column.getDataType()).append(";\n")
                        .append("}\n");
                }
                if (_uiGrid.getFilterList().stream()
                                .filter(filter -> filter.getFieldId() == column.getField().getId())
                                .findFirst().isPresent()) {
                    // to prevent jumping of the modal filter dialog, close and open it
                    final String varName = RandomUtil.randomAlphabetic(4);
                    dialogJs.append("var ").append(varName)
                        .append(" = registry.byId('").append("fttd_" + column.getField().getId()).append("');\n")
                        .append("if (").append(varName).append(" && !(").append(varName)
                            .append(".domNode.offsetHeight == 0 && ")
                            .append(varName).append(".domNode.offsetWidth == 0)) {\n")
                        .append(varName).append(".onBlur();\n")
                        .append("var nl = query(\".gridxHeaderMenuBtn\", dom.byId('grid-")
                            .append(column.getField().getId()).append("'));\n")
                        .append("nl[0].click();\n")
                        .append("}\n");
                }
            }
            js.append("});\n")
                .append("grid.setColumns(grid.structure);\n")
                .append(dialogJs);
        }
        js.append("grid.model.clearCache();\n")
            .append("grid.model.store.setData(items);\n")
            .append("grid.body.refresh();\n");
        return DojoWrapper.require(js, DojoClasses.registry, DojoClasses.array, DojoClasses.dom, DojoClasses.query);
    }
}


/*
 * Copyright 2003 - 2014 The eFaps Team
 *
 * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Revision:        $Rev$
 * Last Changed:    $Date$
 * Last Changed By: $Author$
 */

package org.efaps.ui.wicket.behaviors;

import java.util.UUID;

import org.apache.commons.text.StringEscapeUtils;
import org.apache.wicket.Component;
import org.apache.wicket.behavior.Behavior;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.head.JavaScriptHeaderItem;
import org.efaps.admin.ui.Command;
import org.efaps.db.Context;
import org.efaps.message.MessageStatusHolder;
import org.efaps.util.EFapsException;
import org.efaps.util.cache.CacheReloadException;

/**
 * TODO comment!
 *
 * @author The eFaps Team
 * @version $Id$
 */
public class SetMessageStatusBehavior
    extends Behavior
{

    /**
     * Needed for serialization.
     */
    private static final long serialVersionUID = 1L;

    /**
     * Render to the web response whatever the component wants to contribute to
     * the head section.
     *
     * @param _component    Component this behavior belongs to
     * @param _response     Response object
     */
    @Override
    public void renderHead(final Component _component,
                           final IHeaderResponse _response)
    {
        try {
            _response.render(JavaScriptHeaderItem.forScript(SetMessageStatusBehavior.getScript(),
                            SetMessageStatusBehavior.class.getName()));
        } catch (final EFapsException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    /**
     *
     * @return script
     * @throws EFapsException on error
     */
    private static String getScript()
        throws EFapsException
    {
        final StringBuilder js = new StringBuilder();
        final long usrId = Context.getThreadContext().getPersonId();
        if (MessageStatusHolder.hasUnreadMsg(usrId) || MessageStatusHolder.hasReadMsg(usrId)) {
            js.append("top.postMessage('").append(StringEscapeUtils.escapeEcmaScript(
                                SetMessageStatusBehavior.getLabel(
                                                MessageStatusHolder.getUnReadCount(usrId),
                                                MessageStatusHolder.getReadCount(usrId))))
                .append("', '*');");
        } else {
            js.append("top.postMessage('").append("', '*');");
        }
        return js.toString();
    }

    /**
     * @return UUID of the command used for the alert button
     */
    public static UUID getCmdUUD()
    {
        // Admin_Common_SystemMessageAlert
        return UUID.fromString("5a6f2d4a-df81-4211-b7ed-18ae83608c81");
    }

    /**
     * @param _unread unread messages
     * @param _read read messages
     * @return string
     * @throws CacheReloadException on error
     */
    public static String getLabel(final int _unread,
                                  final int _read)
        throws CacheReloadException
    {
        final StringBuilder ret = new StringBuilder()
                        .append("<span")
                        .append(_unread > 0 ? "class=\"unread\"" : "").append(">")
                        .append(String.format(Command.get(SetMessageStatusBehavior.getCmdUUD()).getLabelProperty(),
                                        _unread, _read))
                        .append("</span>");
        return ret.toString();
    }
}


/*
 * Copyright 2015-2016 Hewlett-Packard Development Company, L.P.
 * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
 */

package com.hp.autonomy.hod.client.api.textindex.query.content;

import com.fasterxml.jackson.databind.JavaType;
import com.hp.autonomy.hod.client.api.authentication.EntityType;
import com.hp.autonomy.hod.client.api.authentication.TokenType;
import com.hp.autonomy.hod.client.api.resource.ResourceIdentifier;
import com.hp.autonomy.hod.client.api.textindex.query.search.Document;
import com.hp.autonomy.hod.client.api.textindex.query.search.QueryResults;
import com.hp.autonomy.hod.client.config.HodServiceConfig;
import com.hp.autonomy.hod.client.config.Requester;
import com.hp.autonomy.hod.client.error.HodErrorException;
import com.hp.autonomy.hod.client.token.TokenProxy;

import java.io.Serializable;
import java.util.List;

/**
 * Default implementation of GetContentService
 * <p/>
 * If a custom return type is not required, use the {@link #documentsService(HodServiceConfig)} static factory method
 * @param <T> The desired return type of the services methods
 */
@SuppressWarnings("WeakerAccess")
public class GetContentServiceImpl<T extends Serializable> implements GetContentService<T> {

    private final GetContentBackend getContentBackend;
    private final Requester<?, TokenType.Simple> requester;
    private final JavaType returnType;

    /**
     * Create a new GetContentServiceImpl of the desired parameterized type
     * @param config The configuration to use
     * @param documentType The desired document type for service methods. This type must have the correct Jackson annotations
     * to read HP Haven OnDemand responses
     */
    public GetContentServiceImpl(final HodServiceConfig<?, TokenType.Simple> config, final Class<T> documentType) {
        getContentBackend = config.getRestAdapter().create(GetContentBackend.class);
        requester = config.getRequester();
        returnType = config.getObjectMapper().getTypeFactory().constructParametrizedType(QueryResults.class, QueryResults.class, documentType);
    }

    /**
     * Create a new GetContentServiceImpl of type {@link QueryResults}
     * @param hodServiceConfig The configuration to use
     * @return A new {@literal GetContentService<Document>}
     */
    public static GetContentServiceImpl<Document> documentsService(final HodServiceConfig<?, TokenType.Simple> hodServiceConfig) {
        return new GetContentServiceImpl<>(hodServiceConfig, Document.class);
    }

    @Override
    public QueryResults<T> getContent(final List<String> indexReference, final ResourceIdentifier index, final GetContentRequestBuilder params) throws HodErrorException {
        return requester.unsafeMakeRequest(returnType, getBackendCaller(indexReference, index, params));
    }

    @Override
    public QueryResults<T> getContent(final TokenProxy<?, TokenType.Simple> tokenProxy, final List<String> indexReference, final ResourceIdentifier index, final GetContentRequestBuilder params) throws HodErrorException {
        return requester.unsafeMakeRequest(tokenProxy, returnType, getBackendCaller(indexReference, index, params));
    }

    private Requester.BackendCaller<EntityType, TokenType.Simple> getBackendCaller(final List<String> indexReference, final ResourceIdentifier indexes, final GetContentRequestBuilder params) {
        return authenticationToken -> getContentBackend.getContent(authenticationToken, indexReference, indexes, params.build());
    }
}


/*
 * Copyright 2003 - 2016 The eFaps Team
 *
 * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package org.efaps.ui.wicket.components.modalwindow;

import org.apache.wicket.Page;
import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
import org.apache.wicket.model.Model;
import org.apache.wicket.request.IRequestParameters;
import org.apache.wicket.request.cycle.RequestCycle;
import org.efaps.ui.wicket.models.objects.ICmdUIObject;
import org.efaps.ui.wicket.models.objects.PagePosition;
import org.efaps.ui.wicket.models.objects.UIForm;
import org.efaps.ui.wicket.models.objects.UIStructurBrowser;
import org.efaps.ui.wicket.models.objects.UITable;
import org.efaps.ui.wicket.pages.content.form.FormPage;
import org.efaps.ui.wicket.pages.content.structurbrowser.StructurBrowserPage;
import org.efaps.ui.wicket.pages.content.table.TablePage;
import org.efaps.ui.wicket.pages.dialog.DialogPage;
import org.efaps.ui.wicket.pages.error.ErrorPage;
import org.efaps.util.EFapsException;

/**
 * Thic Class is used to create a page inside a modal window lazily.
 *
 * @author The eFaps Team
 */
public class ModalWindowAjaxPageCreator
    implements ModalWindow.PageCreator
{

    /**
     * Needed foer serialization.
     */
    private static final long serialVersionUID = 1L;

    /**
     * Model for the page to be created.
     */
    private final ICmdUIObject uiObject;

    /**
     * The modal window the page will be created in.
     */
    private final ModalWindowContainer modalWindow;

    /** The page position. */
    private final PagePosition pagePosition;

    /**
     * Constructor.
     *
     * @param _uiObject object for the page to create
     * @param _modalWindow modal window the page will be created in
     * @param _pagePosition the page position
     */
    public ModalWindowAjaxPageCreator(final ICmdUIObject _uiObject,
                                      final ModalWindowContainer _modalWindow,
                                      final PagePosition _pagePosition)
    {
        this.uiObject = _uiObject;
        this.modalWindow = _modalWindow;
        this.pagePosition = _pagePosition;
    }

    /**
     * Method that creates the page.
     *
     * @return new Page
     */
    @Override
    public Page createPage()
    {
        Page ret = null;
        try {
            final String instKey = this.uiObject.getInstance() == null ? null : this.uiObject.getInstance().getKey();

            if (this.uiObject.getCommand().getTargetTable() == null) {
                final UIForm uiform = new UIForm(this.uiObject.getCommand().getUUID(), instKey)
                                .setPagePosition(this.pagePosition);
                uiform.setPicker(this.uiObject);
                if (!uiform.isInitialized()) {
                    uiform.execute();
                }
                if (uiform.getElements().isEmpty()) {
                    ret = new DialogPage(this.modalWindow.getPage().getPageReference(),
                                    uiform.getCommand().getName() + ".InvalidInstance", false, false);
                } else {
                    if (this.uiObject.getCommand().isSubmit()) {
                        final IRequestParameters parameters = RequestCycle.get().getRequest().getRequestParameters();
                        uiform.setSelected(parameters.getParameterValues("selectedRow"));
                    }
                    ret = new FormPage(Model.of(uiform), this.modalWindow, this.modalWindow.getPage()
                                    .getPageReference());
                }
            } else {
                if (this.uiObject.getCommand().getTargetStructurBrowserField() == null) {
                    final UITable uitable = new UITable(this.uiObject.getCommand().getUUID(), instKey)
                                    .setPagePosition(this.pagePosition);
                    uitable.setPicker(this.uiObject);
                    ret = new TablePage(Model.of(uitable), this.modalWindow, this.modalWindow.getPage()
                                    .getPageReference());
                } else {
                    final UIStructurBrowser uiPageObject = new UIStructurBrowser(this.uiObject.getCommand().getUUID(),
                                    instKey);
                    uiPageObject.setPicker(this.uiObject);
                    ret = new StructurBrowserPage(Model.of(uiPageObject), this.modalWindow,
                                    this.modalWindow.getPage().getPageReference());
                }
            }
        } catch (final EFapsException e) {
            ret = new ErrorPage(e);
        }
        return ret;
    }
}


/*
 * Copyright 2003 - 2016 The eFaps Team
 *
 * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package org.efaps.ui.wicket.components.gridx.behaviors;

import org.apache.wicket.ajax.AjaxEventBehavior;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.IAjaxIndicatorAware;
import org.efaps.ui.wicket.components.modalwindow.ModalWindowContainer;

/**
 * The Class OpenModalBehavior.
 *
 * @author The eFaps Team
 */
public class OpenModalBehavior
    extends AjaxEventBehavior
    implements IAjaxIndicatorAware
{

    /**
     * Needed for serialization.
     */
    private static final long serialVersionUID = 1L;

    /**
     * Constructor.
     */
    public OpenModalBehavior()
    {
        super("click");
    }

    /**
     * Show the modal window.
     *
     * @param _target AjaxRequestTarget
     */
    @Override
    protected void onEvent(final AjaxRequestTarget _target)
    {
        final ModalWindowContainer modal = getComponent().getPage().visitChildren(ModalWindowContainer.class,
                        new ModalVisitor());
        modal.show(_target);
    }

    @Override
    public String getAjaxIndicatorMarkupId()
    {
        return "eFapsVeil";
    }
}


/*
 * Copyright 2003 - 2016 The eFaps Team
 *
 * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package org.efaps.ui.wicket.models.field.set;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.efaps.admin.datamodel.Attribute;
import org.efaps.admin.datamodel.ui.UIValue;
import org.efaps.admin.ui.field.Field;
import org.efaps.db.Instance;
import org.efaps.ui.wicket.models.field.FieldConfiguration;
import org.efaps.ui.wicket.models.field.IUIElement;
import org.efaps.ui.wicket.models.objects.AbstractUIModeObject;
import org.efaps.ui.wicket.models.objects.AbstractUIObject;
import org.efaps.util.EFapsException;
import org.efaps.util.cache.CacheReloadException;

/**
 * TODO comment!
 *
 * @author The eFaps Team
 */
public class UIFieldSet
    extends AbstractUIModeObject
    implements IUIElement
{

    /** The Constant serialVersionUID. */
    private static final long serialVersionUID = 1L;

    /**
     * Headers for this set of cells.
     */
    private final List<UIFieldSetColHeader> headers = new ArrayList<UIFieldSetColHeader>();

    /**
     * Rows fot this set.
     */
    private final List<UIFieldSetRow> rows = new ArrayList<UIFieldSetRow>();

    /**
     * Mapping of y-coordinate to x-coordinate 2 value.
     */
    private final Map<String, UIFieldSetRow> instKey2row = new HashMap<String, UIFieldSetRow>();

    /**
     * Mapping of field name to index.
     */
    private final Map<String, Integer> indexes = new HashMap<String, Integer>();

    /** The parent. */
    private final AbstractUIObject parent;

    /**
     * Configuration of the related field.
     */
    private final FieldConfiguration fieldConfiguration;

    /** The value. */
    private final UIValue value;

    /**
     * Instantiates a new UI field set.
     *
     * @param _parent the parent
     * @param _instance the _instance
     * @param _uiValue the ui value
     * @throws CacheReloadException the cache reload exception
     */
    public UIFieldSet(final AbstractUIObject _parent,
                      final Instance _instance,
                      final UIValue _uiValue)
        throws CacheReloadException
    {
        super(_instance == null ? null : _instance.getKey());
        this.parent = _parent;
        this.value = _uiValue;
        this.fieldConfiguration = new FieldConfiguration(_uiValue.getField().getId());
    }

    /**
     * @param _uiHeader Header to be added
     * @throws CacheReloadException on error
     */
    public void addHeader(final UIFieldSetColHeader _uiHeader)
        throws CacheReloadException
    {
        this.headers.add(_uiHeader);
    }

    /**
     * @param _rowInstance instance for the row to be added
     */
    public void addRow(final Instance _rowInstance)
    {
        final UIFieldSetRow row = new UIFieldSetRow(_rowInstance, this);
        this.rows.add(row);
        this.instKey2row.put(_rowInstance.getKey(), row);
    }

    /**
     * Adds the value.
     *
     * @param _rowInstance instance for the row to be added
     * @param _uiFieldSetValue the _ui field set value
     */
    public void addValue(final Instance _rowInstance,
                         final UIFieldSetValue _uiFieldSetValue)
    {
        this.instKey2row.get(_rowInstance.getKey()).add(_uiFieldSetValue);
    }

    @Override
    public Instance getInstanceFromManager()
        throws EFapsException
    {
        return getInstance();
    }

    @Override
    public boolean hasInstanceManager()
        throws EFapsException
    {
        return false;
    }

    /**
     * Gets the index.
     *
     * @param _inputName the _input name
     * @return the index
     */
    public int getIndex(final String _inputName)
    {
        Integer ret = 0;
        if (this.indexes.containsKey(_inputName)) {
            ret = this.indexes.get(_inputName) + 1;
        }
        this.indexes.put(_inputName, ret);
        return ret;
    }

    /**
     * Reset index.
     */
    public void resetIndex()
    {
        this.indexes.clear();
    }

    /**
     * Getter method for the instance variable {@link #headers}.
     *
     * @return value of instance variable {@link #headers}
     */
    public List<UIFieldSetColHeader> getHeaders()
    {
        return this.headers;
    }

    /**
     * Getter method for the instance variable {@link #rows}.
     *
     * @return value of instance variable {@link #rows}
     */
    public List<UIFieldSetRow> getRows()
    {
        return this.rows;
    }

    /**
     * Add a new Row.
     *
     * @throws EFapsException on error
     */
    public void addNewRow()
        throws EFapsException
    {
        final UIFieldSetRow row = new UIFieldSetRow(this);
        this.rows.add(row);
        for (final UIFieldSetColHeader header : getHeaders()) {
            final UIValue uiValue = UIValue
                            .get(Field.get(header.getFieldId()), Attribute.get(header.getAttrId()), null);
            final UIFieldSetValue cellSetValue = new UIFieldSetValue(getParent(), null, this, uiValue);
            row.add(cellSetValue);
        }
    }

    /**
     * Getter method for the instance variable {@link #parent}.
     *
     * @return value of instance variable {@link #parent}
     */
    public AbstractUIObject getParent()
    {
        return this.parent;
    }

    /**
     * Getter method for the instance variable {@link #fieldConfiguration}.
     *
     * @return value of instance variable {@link #fieldConfiguration}
     */
    public FieldConfiguration getFieldConfiguration()
    {
        return this.fieldConfiguration;
    }

    /**
     * Gets the label.
     *
     * @return the label
     * @throws EFapsException the e faps exception
     */
    public String getLabel()
        throws EFapsException
    {
        return getFieldConfiguration().evalLabel(getValue(), getInstance());
    }

    /**
     * Getter method for the instance variable {@link #value}.
     *
     * @return value of instance variable {@link #value}
     */
    public UIValue getValue()
    {
        return this.value;
    }
}


/*
 * Copyright 2003 - 2014 The eFaps Team
 *
 * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Revision:        $Rev$
 * Last Changed:    $Date$
 * Last Changed By: $Author$
 */

package org.efaps.ui.wicket.behaviors.dojo;

import org.apache.wicket.Component;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.head.JavaScriptHeaderItem;

/**
 * TODO comment!
 *
 * @author The eFaps Team
 */
public class RequireBehavior
    extends AbstractDojoBehavior
{

    /**
     * Needed for serialization.
     */
    private static final long serialVersionUID = 1L;
    private final String[] packages;

    public RequireBehavior(final String... _packages)
    {
        this.packages = _packages;
    }
    /**
     * Render the links for the head.
     *
     * @param _component component the header will be rendered for
     * @param _response resonse to add
     */
    @Override
    public void renderHead(final Component _component,
                           final IHeaderResponse _response)
    {
        super.renderHead(_component, _response);
        final StringBuilder js = new StringBuilder()
            .append("require([");
        for (int i = 0; i < this.packages.length; i++) {
            if (i > 0) {
                js.append(",");
            }
            js.append("\"").append(this.packages[i]).append("\"");
        }
        js.append("]);");
        _response.render(JavaScriptHeaderItem.forScript(js, _component.getId()));
    }
}


/*
 * Copyright 2003 - 2014 The eFaps Team
 *
 * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Revision:        $Rev$
 * Last Changed:    $Date$
 * Last Changed By: $Author$
 */

package org.efaps.ui.wicket.components.classification;

import java.util.Iterator;
import java.util.Properties;

import org.apache.wicket.Component;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.extensions.markup.html.repeater.tree.NestedTree;
import org.apache.wicket.extensions.markup.html.repeater.tree.content.CheckedFolder;
import org.apache.wicket.extensions.markup.html.repeater.tree.theme.HumanTheme;
import org.apache.wicket.extensions.markup.html.repeater.tree.theme.WindowsTheme;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.efaps.admin.datamodel.Type;
import org.efaps.ui.wicket.models.objects.UIClassification;
import org.efaps.ui.wicket.util.Configuration;
import org.efaps.ui.wicket.util.Configuration.ConfigAttribute;
import org.efaps.util.EFapsException;
import org.efaps.util.cache.CacheReloadException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Renders the tree for selecting a clqssification.
 *
 * @author The eFaps Team
 * @version $Id$
 */
public class ClassificationTree
    extends NestedTree<UIClassification>
{

    /**
     * /** Needed for serialization.
     */
    private static final long serialVersionUID = 1L;

    /**
     * Logging instance used in this class.
     */
    private static final Logger LOG = LoggerFactory.getLogger(ClassificationTree.class);

    /**
     * @param _wicketId wicketId of this component
     * @param _model model for this component
     * @throws CacheReloadException on error
     */
    public ClassificationTree(final String _wicketId,
                              final IModel<UIClassification> _model)
        throws CacheReloadException
    {
        super(_wicketId, new ClassificationTreeProvider(_model));
        setOutputMarkupId(true);
        if ("human".equals(Configuration.getAttribute(ConfigAttribute.CLASSTREE_CLASS))) {
            add(new HumanTheme());
        } else if ("windows".equals(Configuration.getAttribute(ConfigAttribute.CLASSTREE_CLASS))) {
            add(new WindowsTheme());
        }

        Properties properties;
        try {
            properties = Configuration.getAttributeAsProperties(ConfigAttribute.CLASSTREE_EXPAND);
        } catch (final EFapsException e) {
            ClassificationTree.LOG.error("cannot read Properties from Configuration.");
            properties = new Properties();
        }
        final Iterator<? extends UIClassification> iter = getProvider().getRoots();
        while (iter.hasNext()) {
            final UIClassification clazz = iter.next();
            final String expand = properties.getProperty(Type.get(clazz.getClassificationUUID()).getName(), "false");
            addChildren(clazz, "true".equalsIgnoreCase(expand));
        }
    }

    /**
     * Recursive method to add child classifications.
     *
     * @param _uiClass  classification to add
     * @param _force    force expanded
     */
    private void addChildren(final UIClassification _uiClass,
                             final boolean _force)
    {
        if (_force || _uiClass.isExpanded()) {
            getModelObject().add(_uiClass);
            for (final UIClassification child : _uiClass.getChildren()) {
                addChildren(child, _force);
            }
        }
    }

    @Override
    public void expand(final UIClassification _uiStrBrws)
    {
        super.expand(_uiStrBrws);
        _uiStrBrws.setExpanded(true);
    }

    /**
     * Collapse the given node, tries to update the affected branch if the
     * change happens on an {@link AjaxRequestTarget}.
     *
     * @param _uiStrBrws    the object to collapse
     */
    @Override
    public void collapse(final UIClassification _uiStrBrws)
    {
        super.collapse(_uiStrBrws);
        _uiStrBrws.setExpanded(false);
    }

    /*
     * (non-Javadoc)
     * @see org.apache.wicket.extensions.markup.html.repeater.tree.AbstractTree#
     * newContentComponent(java.lang.String, org.apache.wicket.model.IModel)
     */
    @Override
    protected Component newContentComponent(final String _id,
                                            final IModel<UIClassification> _model)
    {
        return new CheckedFolder<UIClassification>(_id, this, _model)
        {

            private static final long serialVersionUID = 1L;

            @Override
            protected void onUpdate(final AjaxRequestTarget _target)
            {
                final boolean selected = getModelObject().isSelected();
                final ClassificationTreePanel parent = findParent(ClassificationTreePanel.class);
                // Don't do that if it is a filter
                if (parent != null && !getModelObject().isMultipleSelect() && !selected) {
                    // select event ensure that there are no other objects selected by cleaning it totally
                    UIClassification uiClass = getModelObject();
                    while (!uiClass.isBase()) {
                        uiClass = uiClass.getParent();
                    }
                    toggleDown(uiClass, false);
                }

                // unselect event ensure that no children are selected
                if (selected) {
                    toggleDown(getModelObject(), false);
                } else {
                    toggleUp(getModelObject(), true);
                }
                _target.add(ClassificationTree.this);
                if (parent != null) {
                    parent.modelChanged();
                }
            }

            private void toggleUp(final UIClassification _uiClass,
                                  final boolean _selected)
            {
                UIClassification uiClass = _uiClass;
                uiClass.setSelected(_selected);
                while (!uiClass.isBase()) {
                    uiClass = uiClass.getParent();
                    uiClass.setSelected(_selected);
                }
            }

            private void toggleDown(final UIClassification _uiClass,
                                    final boolean _selected)
            {
                _uiClass.setSelected(_selected);
                for (final UIClassification child : _uiClass.getChildren()) {
                    toggleDown(child, _selected);
                }
            }

            @Override
            protected IModel<Boolean> newCheckBoxModel(final IModel<UIClassification> _model)
            {
                return Model.of(_model.getObject().isSelected());
            }
        };
    }
}