/*
 * Copyright
 *   2008 axYus - www.axyus.com
 *   2008 C.Bosquet - charles.bosquet@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 General Public License as published by
 * the Free Software Foundation; either version 2 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 General Public License for more details.
 *
 * You should have received a copy of the GNU 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.WindowCloseListener;
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.DockPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.VerticalSplitPanel;
import com.google.gwt.user.client.ui.Widget;
import fr.gouv.finances.dgfip.xemelios.web.gwt.client.dto.CritereTypeDTO;
import fr.gouv.finances.dgfip.xemelios.web.gwt.client.dto.InitParametersDTO;
import fr.gouv.finances.dgfip.xemelios.web.gwt.client.dto.ListResultatDTO;
import fr.gouv.finances.dgfip.xemelios.web.gwt.client.dto.PjRefInfoDTO;
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.dto.SearchResultDTO;
import fr.gouv.finances.dgfip.xemelios.web.gwt.client.dto.StoredSearchDTO;
import fr.gouv.finances.dgfip.xemelios.web.gwt.client.service.ServiceLocator;
import fr.gouv.finances.dgfip.xemelios.web.gwt.client.utils.DoneListener;
import fr.gouv.finances.dgfip.xemelios.web.gwt.client.utils.GWTUtils;
import fr.gouv.finances.dgfip.xemelios.web.gwt.client.widget.ChooseOneItemDialog;
import fr.gouv.finances.dgfip.xemelios.web.gwt.client.widget.DlgSearchParams;
import fr.gouv.finances.dgfip.xemelios.web.gwt.client.widget.PaginationWidget;
import fr.gouv.finances.dgfip.xemelios.web.gwt.client.widget.PaginationWidget.ChangePageRequestListener;
import fr.gouv.finances.dgfip.xemelios.web.gwt.client.widget.SaveRequestDialog;
import fr.gouv.finances.dgfip.xemelios.web.gwt.client.widget.exports.DlgSelectExport;
import fr.gouv.finances.dgfip.xemelios.web.gwt.client.widget.search.ButtonsBarWidget.ClickListenerType;
import java.util.List;

/**
 * Widget principal (permettant la recherche)
 * @author CBO
 * @author chm
 */
public class SearchWidget extends Composite {

    static private SearchWidget searchWidgetInstance = null;

    private InitParametersDTO initParameters;
    private SearchInitParametersDTO searchInitParameters;
    private DockPanel mainPanel;
    private ColAndBudChooserWidget colAndBudChooserWidget;
    private ButtonsBarWidget buttonsBarWidget;
    private VerticalSplitPanel verticalSplitPanel;
    private CriteriaTable criteriaTable;
    private HTML resultTableContainer;
    private StatusBar statusBar;
    private boolean exportPossible = false;

    public SearchWidget() {
        this.mainPanel = new DockPanel();
        this.mainPanel.setSize("100%", "100%");
        this.mainPanel.getElement().setId("mainPanel");
        this.buttonsBarWidget = new ButtonsBarWidget();
        this.colAndBudChooserWidget = new ColAndBudChooserWidget(this);
        HorizontalPanel hp = new HorizontalPanel();
        hp.add(colAndBudChooserWidget);
        hp.add(buttonsBarWidget);
        this.mainPanel.add(hp, DockPanel.NORTH);
        hp.getElement().setId("Topnav");
        this.mainPanel.setCellHeight(hp, "44px");
        this.statusBar = new StatusBar();
        this.mainPanel.add(this.statusBar, DockPanel.SOUTH);
        this.mainPanel.setCellHeight(statusBar, "65px");
        this.verticalSplitPanel = new VerticalSplitPanel();
        this.criteriaTable = new CriteriaTable(this);
        this.verticalSplitPanel.setTopWidget(criteriaTable);
        this.resultTableContainer = new HTML();
        this.resultTableContainer.getElement().setId("resultTableContainer"); // resultTableContainer
        this.verticalSplitPanel.setBottomWidget(resultTableContainer);
        this.verticalSplitPanel.setSplitPosition("87px");  // 87px
        this.verticalSplitPanel.setSize("100%", "100%");
        this.mainPanel.add(this.verticalSplitPanel, DockPanel.CENTER);
        this.mainPanel.setCellWidth(this.verticalSplitPanel, "100%");
        this.mainPanel.setCellHeight(this.verticalSplitPanel, "100%");

        initWidget(this.mainPanel);
        criteriaTable.getElement().getParentElement().setId("parentSubnav");
        getElement().setId("SearchWidget");
    }

    /**
     * Méthode permettant de charger les données d'initialisation de la recherche
     * @param documentId
     * @param etatId
     * @param elementId
     * @param doneListener
     */
    public void init(String documentId, String etatId, String elementId, final DoneListener doneListener) {
        ServiceLocator.getXemeliosService().initSearchWindow(documentId, etatId, elementId, new AsyncCallback<InitParametersDTO>() {

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

            @SuppressWarnings("unchecked")
            public void onSuccess(InitParametersDTO result) {
                SearchWidget.this.initParameters = result;
                colAndBudChooserWidget.init(result.getCollectivites(), result.getFirstCollectiviteBudgets());
                colAndBudChooserWidget.addChangeListener(new ChangeListener() {
                    public void onChange(Widget arg0) {
                        collectiviteOrBudgetChange();
                    }
                });
                buttonsBarWidget.addClickListener(ClickListenerType.ADD_CRITERE, new AddCriteriaClickListener(SearchWidget.this));
                buttonsBarWidget.addClickListener(ClickListenerType.SEARCH, new ClickListener() {
                    public void onClick(Widget arg0) {
                        search();
                    }
                });
                buttonsBarWidget.addClickListener(ClickListenerType.RESET_CRITERES, new ClickListener() {
                    public void onClick(Widget arg0) {
                        resetCriteres();
                    }
                });
                buttonsBarWidget.addClickListener(ClickListenerType.OPEN_SEARCH, new ClickListener() {
                    public void onClick(Widget arg0) {
                        openSearch();
                    }
                });
                buttonsBarWidget.addClickListener(ClickListenerType.SAVE_REQUEST, new ClickListener() {
                    public void onClick(Widget sender) {
                        saveSearch();
                    }
                });
                buttonsBarWidget.addClickListener(ClickListenerType.SEARCH_PARAMS, new ClickListener() {
                    public void onClick(Widget sender) {
                        searchParams();
                    }
                });
                buttonsBarWidget.addClickListener(ClickListenerType.REPOSITORY, new ClickListener() {
                    public void onClick(Widget sender) {
                        showRepository();
                    }
                });
                statusBar.getPaginationWidget().addChangePageRequestListener(new ChangePageRequestListener() {
                    public void onChangePageRequest(PaginationWidget sender, int requestedPageNumber) {
                        loadPage(requestedPageNumber);
                    }
                });
                SearchWidget.searchWidgetInstance = SearchWidget.this;
                defineJSMethod();
                Window.addWindowCloseListener(new WindowCloseListener() {
                    public String onWindowClosing() {
                        return null;
                    }
                    public void onWindowClosed() {
                        releaseSearchSession();
                    }
                });
                setSearchInitParameters(initParameters.getSearchInitParameters());
                doneListener.done(result);
            }
        });
    }

    public void addCritere() {
        this.criteriaTable.addEmptyCriterion();
    }

    public void resetCriteres() {
        setSearchInitParameters(this.initParameters.getSearchInitParameters());
    }

    public void search() {
        if (criteriaTable.checkValidity()) {
            startSearch();
            if(getSearchInitParameters().getPluginId()==null) {
                ServiceLocator.getXemeliosService().search(getSearchSessionId(), getCollectiviteId(), getBudgetId(), criteriaTable.getSearchParameters(), getSearchInitParameters().getPluginId(), new AsyncCallback<SearchResultDTO>() {

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

                    public void onSuccess(SearchResultDTO result) {
                        handleSearchResult(result);
                    }
                });
            } else {
                if(getSearchInitParameters().getPluginType()==StoredSearchDTO.TYPE_SEARCH) {
                    ServiceLocator.getXemeliosService().search(getSearchSessionId(), getCollectiviteId(), getBudgetId(), criteriaTable.getSearchParameters(), getSearchInitParameters().getPluginId(), new AsyncCallback<SearchResultDTO>() {

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

                        public void onSuccess(SearchResultDTO result) {
                            handleSearchResult(result);
                        }
                    });
                } else {
                    ServiceLocator.getXemeliosService().processPluginExport(getSearchSessionId(), getCollectiviteId(), getBudgetId(), getSearchInitParameters().getPluginId(), criteriaTable.getSearchParameters(), new AsyncCallback<String>() {

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

                        public void onSuccess(String result) {
                            resultTableContainer.setHTML("");
                            downloadUrl(GWTUtils.getContextPath()+result);
                        }
                    });
                }
            }
        }
    }
    public void saveSearch() {
        final SaveRequestDialog dlg = new SaveRequestDialog(this,criteriaTable.getSearchParameters());
        ServiceLocator.getXemeliosService().getSavedPrivateSearchs(getSearchSessionId(), new AsyncCallback<List<StoredSearchDTO>>() {
            public void onFailure(Throwable throwable) {
                GWTUtils.handleRPCError(throwable);
            }
            public void onSuccess(List<StoredSearchDTO> result) {
                dlg.setItems(result);
            }
        });
    }
    public void searchParams() {
        ServiceLocator.getXemeliosService().getListResultatDTO(getSearchSessionId(), new AsyncCallback<ListResultatDTO>() {
            public void onFailure(Throwable caught) {
                Window.alert(caught.getMessage());
                GWTUtils.handleRPCError(caught);
            }
            public void onSuccess(ListResultatDTO result) {
                DlgSearchParams dsp = new DlgSearchParams(SearchWidget.this, result);
                dsp.show();
            }
        });
    }
    public void showRepository() {
        ServiceLocator.getXemeliosService().getDocumentType(getSearchSessionId(), new AsyncCallback<String>() {
            public void onFailure(Throwable caught) { }
            public void onSuccess(String result) {
                showRepository(result, getCollectiviteId(), GWTUtils.getContextPath());
            }
        });
    }
    public void executeSaveRequest(List<SearchParameterDTO> parameters, String requestName, String requestId) {
        if(requestId==null || requestId.length()==0) {
            ServiceLocator.getXemeliosService().saveRequest(getSearchSessionId(), parameters, requestName, new AsyncCallback<String>() {
                public void onFailure(Throwable caught) {
                    Window.alert(caught.getMessage());
                }
                public void onSuccess(String result) {
                    if(!"OK".equals(result)) {
                        Window.alert("Erreur lors de l'enregistrement de la requete.");
                    }
                }
            });
        } else {
            ServiceLocator.getXemeliosService().saveRequest(getSearchSessionId(), parameters, requestName, requestId, new AsyncCallback<String>() {
                public void onFailure(Throwable caught) {
                    Window.alert(caught.getMessage());
                }
                public void onSuccess(String result) {
                    if(!"OK".equals(result)) {
                        Window.alert("Erreur lors de l'enregistrement de la requete.");
                    }
                }
            });
        }
    }

    public void openSearch() {
        final ChooseOneItemDialog chooseOneItemDialog = new ChooseOneItemDialog("Ouverture d'une recherche");
        chooseOneItemDialog.show();
        chooseOneItemDialog.center();
        ServiceLocator.getXemeliosService().getStoredSearchs(getSearchSessionId(), new AsyncCallback<List<StoredSearchDTO>>() {
            public void onFailure(Throwable throwable) {
                GWTUtils.handleRPCError(throwable);
            }
            public void onSuccess(List<StoredSearchDTO> result) {
                chooseOneItemDialog.setItems(result);
                chooseOneItemDialog.addSelectListener(new ChooseOneItemDialog.SelectListener() {
                    public void onSelect(Object selectedItem) {
                        loadSearch((StoredSearchDTO) selectedItem);
                    }
                });
            }
        });
    }

    private void loadSearch(StoredSearchDTO storedSearch) {
        switch(storedSearch.getType()) {
            case StoredSearchDTO.TYPE_EXPORT:
            case StoredSearchDTO.TYPE_SEARCH: {
                ServiceLocator.getXemeliosService().getSearchInitParameters(getSearchSessionId(), storedSearch != null ? storedSearch.getId() : null, new AsyncCallback<SearchInitParametersDTO>() {
                    public void onFailure(Throwable throwable) {
                        GWTUtils.handleRPCError(throwable);
                    }
                    public void onSuccess(SearchInitParametersDTO result) {
                        setSearchInitParameters(result);
                    }
                });
                break;
            }
            case StoredSearchDTO.TYPE_SAVED: {
                ServiceLocator.getXemeliosService().getSearchInitParametersForSavedSearch(getSearchSessionId(), storedSearch.getId(), new AsyncCallback<SearchInitParametersDTO>() {
                    public void onFailure(Throwable throwable) {
                        GWTUtils.handleRPCError(throwable);
                    }
                    public void onSuccess(SearchInitParametersDTO result) {
                        displaySavedRequest(result);
                    }
                });
            }
        }
    }

    protected void displaySavedRequest(SearchInitParametersDTO params) {
        criteriaTable.removeAllCriteres();
        int i=0;
        for(CritereTypeDTO critere: params.getMandatoryCritereTypes()) {
            PostRunnable pr = new PostRunnable(Integer.toString(i)) {
                boolean alreadyRun =  false;
                @Override
                public void run() {
                    if(!alreadyRun) {
                        criteriaTable.getSelectCritereWidget(getIntValue()).setSelectedCritere((CritereTypeDTO)getAdditionalObject("critere"));
                        criteriaTable.initCriteria(getIntValue(), (PostRunnable)this);
                        alreadyRun = true;
                    }
                }
            };
            pr.setAdditionalObject("critere", critere);
            pr.setAdditionalInt(i);
            int row = criteriaTable.addEmptyCriterion(pr);
            i++;
        }
    }
    public void loadPage(int pageNumber) {
        startSearch();
        ServiceLocator.getXemeliosService().loadPage(getSearchSessionId(), pageNumber, new AsyncCallback<SearchResultDTO>() {
            public void onFailure(Throwable throwable) {
                GWTUtils.handleRPCError(throwable);
            }
            public void onSuccess(SearchResultDTO result) {
                handleSearchResult(result);
            }
        });
    }

    private void releaseSearchSession() {
        ServiceLocator.getXemeliosService().destroySearchSession(getSearchSessionId(), new AsyncCallback() {
            public void onFailure(Throwable throwable) {
                GWTUtils.handleRPCError(throwable);
            }
            /**
             * @TODO : remove search parameters stored in http session
             */
            public void onSuccess(Object nullResult) {

            }
        });
    }

    private void startSearch() {
        resultTableContainer.setHTML("<center><img src=\"" + GWTUtils.getContextPath() + "/img/searching.gif\"/></center>");
    }

    private void handleSearchResult(SearchResultDTO result) {
        resultTableContainer.setHTML(transformXml(result.getXml()));
        statusBar.getPaginationWidget().setPaginationInfo(result.getPaginationInfo());
        if (result.getCountAll() <= 0) {
            statusBar.setText(new HTML("Aucun enregistrement trouv&eacute;").getText());
            exportPossible = false;
        } else if (result.getCountAll() == 1) {
            statusBar.setText(new HTML("1 enregistrement trouv&eacute;").getText());
            exportPossible = true;
        } else {
            statusBar.setText(new HTML("" + result.getCountAll() + " enregistrements trouv&eacute;s").getText());
            exportPossible = true;
        }
    }

    private void collectiviteOrBudgetChange() {
        resetCriteres();
    }

    public String getSearchSessionId() {
        return this.initParameters != null ? initParameters.getSearchSessionId() : null;
    }

    public String getCollectiviteId() {
        return this.colAndBudChooserWidget.getCollectiviteId();
    }

    public String getBudgetId() {
        return this.colAndBudChooserWidget.getBudgetId();
    }

    private SearchInitParametersDTO getSearchInitParameters() {
        return this.searchInitParameters;
    }

    private void setSearchInitParameters(SearchInitParametersDTO searchInitParameters) {
//        this.searchInitParameters = searchInitParameters;
//        criteriaTable.init(getSearchInitParameters());
//        if (getSearchInitParameters().getMandatoryCritereTypes()==null || getSearchInitParameters().getMandatoryCritereTypes().isEmpty()) {
//            addCritere();
//        }
//        boolean shouldAddButtonActivated = false;
//        if(getSearchInitParameters().getPluginId()==null) {
//            shouldAddButtonActivated = true;
//        } else {
//            if(getSearchInitParameters().getOptionalCritereTypes()!=null && !getSearchInitParameters().getOptionalCritereTypes().isEmpty()) {
//                shouldAddButtonActivated = true;
//            } else {
//                shouldAddButtonActivated = false;
//            }
//        }
//        buttonsBarWidget.setAddCritereEnable(shouldAddButtonActivated);
        this.searchInitParameters = searchInitParameters;
        criteriaTable.init(getSearchInitParameters());
        if (getSearchInitParameters().getPluginId() == null) {
            addCritere();
        }
        buttonsBarWidget.setAddCritereEnable(!getSearchInitParameters().getOptionalCritereTypes().isEmpty());
    }

    private native String transformXml(String searchResultAsXml)/*-{
    return $wnd.transformXml(searchResultAsXml);
    }-*/;
    private native void downloadUrl(String url)/*-{
    $wnd.downloadUrl(url);
     }-*/;

    private native void showRepository(String docId, String collectivite,String baseUrl)/*-{
     //$wnd.alert(baseUrl+"/showRepository.do?docId="+docId+"&collectivite="+collectivite);
     $wnd.open(baseUrl+"showRepository.do?docId="+docId+"&collectivite="+collectivite);
     }-*/;
    public static void showPjInfos(String collectivite, String strPj, String strNom, String strSupport) {
//        Window.alert("collectivite="+collectivite+"\nstrPj="+strPj+"\nstrNom="+strNom+"\nstrSupport="+strSupport);
        String[] pjIds = splitString(strPj);
        String[] pjNoms = splitString(strNom);
        String[] pjSupports = splitString(strSupport);
        if(pjIds.length!=pjNoms.length && pjIds.length!=pjSupports.length) {
            Window.alert("pjIds, pjNoms et pjSupports n'ont pas la même taille !");
            return;
        } else if(pjIds.length==0) return;
        else {
            PjRefInfoDTO[] pjs = new PjRefInfoDTO[pjIds.length];
            for(int i=0;i<pjIds.length;i++) {
                pjs[i] = new PjRefInfoDTO(collectivite,pjIds[i],pjNoms[i],pjSupports[i]);
            }
            ShowAvailablesPJsDialogBox db = new ShowAvailablesPJsDialogBox(pjs);
            db.show();
            db.center();
        }
    }

    private static String[] splitString(String s) {
        int splitters = 0;
        for(int i=0;i<s.length();i++)
            if(s.charAt(i)=='|') splitters++;
        String[] ret = new String[splitters+1];
        int found = 0;
        int pos = s.indexOf('|');
        while(pos>=0) {
            ret[found]=s.substring(0, pos);
            found++;
            s = s.substring(pos+1);
            pos = s.indexOf('|');
        }
        ret[found]=s;
        return ret;
    }

    static public boolean _isExportPossible() {
        return SearchWidget.searchWidgetInstance!=null ? SearchWidget.searchWidgetInstance.exportPossible : false;
    }

    static public String _getSearchSessionId() {
        return SearchWidget.searchWidgetInstance != null ? SearchWidget.searchWidgetInstance.getSearchSessionId() : null;
        //return SearchWidget.this.getSearchSessionId();
    }

    static public void _configExport() {
        DlgSelectExport dlg = new DlgSelectExport();
        dlg.show();
    }

    static public void _complexExport(String searchSessionId) {
//        Window.alert("_complexExport("+searchSessionId+")");
        if(_isExportPossible()) {
//            String searchSessionId = _getSearchSessionId();
//            Window.alert("searchSessionId="+searchSessionId);
            DlgSelectExport dlg = new DlgSelectExport(searchSessionId);
            dlg.show();
        }
    }

    public static native void defineJSMethod() /*-{
        $wnd.showPjInfos = function(collectivite,strPj,strNom,strSupport) {
            @fr.gouv.finances.dgfip.xemelios.web.gwt.client.widget.search.SearchWidget::showPjInfos(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;) (collectivite,strPj,strNom,strSupport);
        }

        $wnd.isExportPossible = @fr.gouv.finances.dgfip.xemelios.web.gwt.client.widget.search.SearchWidget::_isExportPossible();
        $wnd.getSearchSessionId = @fr.gouv.finances.dgfip.xemelios.web.gwt.client.widget.search.SearchWidget::_getSearchSessionId();
        $wnd._configExport = @fr.gouv.finances.dgfip.xemelios.web.gwt.client.widget.search.SearchWidget::_configExport();
        $wnd._complexExport = function(searchSessionId) {
            @fr.gouv.finances.dgfip.xemelios.web.gwt.client.widget.search.SearchWidget::_complexExport(Ljava/lang/String;) (searchSessionId);
        }
     }-*/;

    public static class AddCriteriaClickListener implements ClickListener {
        private SearchWidget sw;
        private boolean enabled = true;
        public AddCriteriaClickListener(SearchWidget sw) {
            super();
            this.sw=sw;
        }
        public void onClick(Widget arg0) {
            if(enabled)
                sw.addCritere();
        }
        public void setEnabled(boolean enabled) { this.enabled=enabled; }

    }
}
