/*
 * Copyright
 *   2007 axYus - www.axyus.com
 *
 * This file is part of XEMELIOS.
 *
 * XEMELIOS is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * XEMELIOS is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with XEMELIOS; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package fr.gouv.finances.dgfip.xemelios.web.gwt.client.widget.search;

import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.ChangeListener;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Widget;
import fr.gouv.finances.dgfip.xemelios.web.gwt.client.dto.CritereItemDTO;
import fr.gouv.finances.dgfip.xemelios.web.gwt.client.dto.CritereTypeDTO;
import fr.gouv.finances.dgfip.xemelios.web.gwt.client.dto.Property;
import fr.gouv.finances.dgfip.xemelios.web.gwt.client.dto.SearchInitParametersDTO;
import fr.gouv.finances.dgfip.xemelios.web.gwt.client.dto.SearchParameterDTO;
import fr.gouv.finances.dgfip.xemelios.web.gwt.client.service.ServiceLocator;
import fr.gouv.finances.dgfip.xemelios.web.gwt.client.utils.GWTUtils;
import fr.gouv.finances.dgfip.xemelios.web.gwt.client.widget.ImageButton;
import fr.gouv.finances.dgfip.xemelios.web.gwt.client.widget.search.criteria.CritereWidget;
import fr.gouv.finances.dgfip.xemelios.web.gwt.client.widget.search.criteria.CritereWidgetFactory;
import fr.gouv.finances.dgfip.xemelios.web.gwt.client.widget.search.criteria.CritereLineContainer;
import fr.gouv.finances.dgfip.xemelios.web.gwt.client.widget.search.criteria.InputCritereWidget;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author CBO
 */
public class CriteriaTable extends Composite {

    private SearchWidget searchWidget;
    private FlexTable flexTable;

    private SearchInitParametersDTO searchInitParameters;

    public CriteriaTable(SearchWidget searchWidget) {
        this.searchWidget = searchWidget;
        this.flexTable = new FlexTable();
//        flexTable.getElement().setId("prout");
        initWidget(this.flexTable);
        getElement().setId("Subnav");
    }

    public void init(SearchInitParametersDTO searchInitParameters) {
        this.searchInitParameters = searchInitParameters;
        removeAllCriteres();
        for (CritereTypeDTO critereType : searchInitParameters.getMandatoryCritereTypes()) {
            addEmptyCriterion(critereType);
        }
    }

    public String getPluginId() {
        return searchInitParameters.getPluginId();
    }

    public SearchWidget getMainWidget() {
        return searchWidget;
    }

    public void removeAllCriteres() {
        while (getRowCount() > 0) {
            removeRow(getRowCount() - 1);
        }
    }

    public SelectCritereWidget getSelectCritereWidget(int row) {
        return (SelectCritereWidget) getWidget(row, SelectCritereWidget.class);
    }

    public int addEmptyCriterion(PostRunnable... runner) {
        return addEmptyCriterion(null, runner);
    }

    private int addEmptyCriterion(CritereTypeDTO critereType, PostRunnable... runner) {
        final int newCriteriaRowIndex = getRowCount();
        if (critereType != null && newCriteriaRowIndex == 0) {
            // Cas du premier critere de type plugin
            List<CritereTypeDTO> singletonList = new ArrayList<CritereTypeDTO>();
            singletonList.add(critereType);
            SelectCritereWidget selectCritereWidget = new SelectCritereWidget(singletonList, true);
            setWidget(newCriteriaRowIndex, 0, selectCritereWidget);
            flexTable.getFlexCellFormatter().setColSpan(newCriteriaRowIndex, 0, 4);
        } else {
            ImageButton removeCriterionButton = new ImageButton(GWTUtils.getContextPath() + "/img/remove.gif", "Supprimer");
            AndOrChooserWidget andOrChooserWidget = new AndOrChooserWidget();
            if(critereType!=null) {
                List<Property> props = critereType.getProperties();
                if(props!=null) {
                    Window.alert(props.toString());
                    for(Property p:props) {
                        if("AND_OR".equals(p.getName())) {
                            andOrChooserWidget.setSelectedValue(p.getValue());
                            break;
                        }
                    }
                }
            }
            if (newCriteriaRowIndex <= 0 || critereType != null) {
                GWTUtils.hide(removeCriterionButton);
                GWTUtils.hide(andOrChooserWidget);
            } else {
                removeCriterionButton.addClickListener(new ClickListener() {

                    public void onClick(Widget widget) {
                        removeRow(getRow(widget));
                    }
                });
            }
            setWidget(newCriteriaRowIndex, 0, removeCriterionButton);
            setWidget(newCriteriaRowIndex, 1, andOrChooserWidget);
            SelectCritereWidget selectCritereWidget = null;
            if (critereType == null) {
                selectCritereWidget = new SelectCritereWidget(searchInitParameters.getOptionalCritereTypes(), false);
            } else {
                List<CritereTypeDTO> singletonList = new ArrayList<CritereTypeDTO>();
                singletonList.add(critereType);
                selectCritereWidget = new SelectCritereWidget(singletonList, true);
            }
            setWidget(newCriteriaRowIndex, 2, selectCritereWidget);
            selectCritereWidget.addChangeListener(new ChangeListener() {

                public void onChange(Widget widget) {
                    initCriteria(getRow(widget));
                }
            });
            initCriteria(newCriteriaRowIndex,runner);
        }
        return newCriteriaRowIndex;
    }

    public List<SearchParameterDTO> getSearchParameters() {
        List<SearchParameterDTO> toReturn = new ArrayList<SearchParameterDTO>();
        for (int i = 0; i < getRowCount(); i++) {
            toReturn.add(getSearchParameter(i));
        }
        return toReturn;
    }

    public boolean checkValidity() {
        // Il faut au moins un champ :
        if (getRowCount() <= 0) {
            Window.alert(new HTML("La recherche doit comporter au moins un crit&egrave;re").getHTML());
            return false;
        }
        // Champs obligatoires :
        for (int row = 0; row < getRowCount(); row++) {
            CritereLineContainer criteriaLineContainer = (CritereLineContainer) getWidget(row, CritereLineContainer.class);
            if (criteriaLineContainer != null) {
                for (InputCritereWidget inputCritereWidget : criteriaLineContainer.getInputCriteriaWidgets()) {
                    if (!inputCritereWidget.isFilled()) {
                        Window.alert(new HTML("Le champ d&eacute;sign&eacute; par le curseur doit &#234;tre rempli.").getHTML());
                        inputCritereWidget.setFocus();
                        return false;
                    }
                }
            }
        }
        // Validit� des champs :
        for (int row = 0; row < getRowCount(); row++) {
            CritereLineContainer criteriaLineContainer = (CritereLineContainer) getWidget(row, CritereLineContainer.class);
            if (criteriaLineContainer != null) {
                for (InputCritereWidget inputCritereWidget : criteriaLineContainer.getInputCriteriaWidgets()) {
                    try {
                        inputCritereWidget.checkValidity();
                    } catch(InputCritereWidget.ValidityException ve) {
                        Window.alert(new HTML(ve.getMessageToDisplay() + ", veuillez corriger le champ champ d&eacute;sign&eacute; par le curseur.").getHTML());
                        inputCritereWidget.setFocus();
                        return false;
                    }
                }
            }
        }
        return true;
    }

    private void setWidget(int row, int column, Widget widget) {
        this.flexTable.setWidget(row, column, widget);
    }

    private void removeRow(int row) {
        this.flexTable.removeRow(row);
    }

    private void clearCell(int row, int column) {
        if (this.flexTable.getCellCount(row) > column) {
            this.flexTable.clearCell(row, column);
        }
    }

    private int getRowCount() {
        return this.flexTable.getRowCount();
    }

    private int getRow(Widget widget) {
        for (int i = 0; i < this.flexTable.getRowCount(); i++) {
            for (int j = 0; j < this.flexTable.getCellCount(i); j++) {
                if (widget == this.flexTable.getWidget(i, j)) {
                    return i;
                }
            }
        }
        return -1;
    }

    public void initCriteria(final int row, CritereTypeDTO critere) {
        getSelectCritereWidget(row).setSelectedCritere(critere);
        final SelectCritereWidget selectCritereWidget = getSelectCritereWidget(row);
        clearCell(row, 3);
        final List<Property> properties = critere.getProperties();
        ServiceLocator.getXemeliosService().getCritereItems(
                getMainWidget().getSearchSessionId(),
                getMainWidget().getCollectiviteId(),
                getMainWidget().getBudgetId(),
                selectCritereWidget.getSelectedCritere().getId(),
                searchInitParameters.getPluginId(),
                new AsyncCallback<List<CritereItemDTO>>() {

                    public void onFailure(Throwable throwable) {
                        GWTUtils.handleRPCError(throwable);
                    }

                    public void onSuccess(List<CritereItemDTO> result) {
                        if (result != null && !result.isEmpty()) {
                            CritereLineContainer criteriaLineContainer = new CritereLineContainer(CriteriaTable.this, selectCritereWidget.getSelectedCritere().getId());
                            for (CritereItemDTO critereItem : result) {
                                CritereWidget cw = CritereWidgetFactory.createWidget(criteriaLineContainer, critereItem,properties);
                                if (cw != null) {
                                    criteriaLineContainer.add(cw);
                                }
                            }
                            setWidget(row, 3, criteriaLineContainer);
                        }
                    }
                });

    }
    public void initCriteria(final int row, final PostRunnable... runner) {
        final SelectCritereWidget selectCritereWidget = getSelectCritereWidget(row);
        clearCell(row, 3);
        ServiceLocator.getXemeliosService().getCritereItems(
                getMainWidget().getSearchSessionId(),
                getMainWidget().getCollectiviteId(),
                getMainWidget().getBudgetId(),
                selectCritereWidget.getSelectedCritere().getId(),
                searchInitParameters.getPluginId(),
                new AsyncCallback<List<CritereItemDTO>>() {

                    public void onFailure(Throwable throwable) {
                        GWTUtils.handleRPCError(throwable);
                    }

                    public void onSuccess(List<CritereItemDTO> result) {
                        List<PostRunnable> runners = Arrays.asList(runner);
                        PostRunnable firstRunnable = runners.isEmpty() ? null : runners.get(0);
                        CritereTypeDTO critereType = firstRunnable!=null ? (CritereTypeDTO)firstRunnable.getAdditionalObject("critere") : null;
                        if (result != null && !result.isEmpty()) {
                            CritereLineContainer criteriaLineContainer = new CritereLineContainer(CriteriaTable.this, selectCritereWidget.getSelectedCritere().getId());
                            for (CritereItemDTO critereItem : result) {
                                CritereWidget cw = null;
                                if(critereType==null) {
                                    cw = CritereWidgetFactory.createWidget(criteriaLineContainer, critereItem);
                                } else {
                                    cw = CritereWidgetFactory.createWidget(criteriaLineContainer, critereItem, critereType.getProperties());
                                    for(Property p:critereType.getProperties()) {
                                        if("AND_OR".equals(p.getName())) {
                                            AndOrChooserWidget andOrChooserWidget = (AndOrChooserWidget)getWidget(row, AndOrChooserWidget.class);
                                            if (andOrChooserWidget != null && row != 0) {
                                                andOrChooserWidget.setSelectedValue(p.getValue());
                                                break;
                                            }
                                        }
                                    }

                                }
                                if (cw != null) {
                                    criteriaLineContainer.add(cw);
                                }
                            }
                            setWidget(row, 3, criteriaLineContainer);
                        }
                        for(PostRunnable pr:runners) {
                            pr.run();
                        }
                    }
                });
    }

    private SearchParameterDTO getSearchParameter(int row) {
        SearchParameterDTO toReturn = new SearchParameterDTO();
        AndOrChooserWidget andOrChooserWidget = (AndOrChooserWidget)getWidget(row, AndOrChooserWidget.class);
        if (andOrChooserWidget != null && row != 0) {
            toReturn.setOperator(andOrChooserWidget.getSelectedValue());
        }
        toReturn.setCritereModelId(getSelectCritereWidget(row).getSelectedCritere().getId());
        CritereLineContainer criteriaLineContainer = (CritereLineContainer) getWidget(row, CritereLineContainer.class);
        if (criteriaLineContainer != null) {
            for (InputCritereWidget inputCritereWidget : criteriaLineContainer.getInputCriteriaWidgets()) {
                toReturn.putInputValue(inputCritereWidget.getInputId(), inputCritereWidget.getInputValue());
            }
        }
        return toReturn;
    }

    private Widget getWidget(int row, Class clazz) {
        for (int i = 0; i < this.flexTable.getCellCount(row); i++) {
            Widget widget = this.flexTable.getWidget(row, i);
            if (widget != null && widget.getClass().equals(clazz)) {
                return widget;
            }
        }
        return null;
    }
}
