/*
 * Copyright
 *   2007 axYus - www.axyus.com
 *   2010 C.Marchand - christophe.marchand@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.navigate;

import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ChangeListener;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import fr.gouv.finances.dgfip.xemelios.web.gwt.client.dto.CollectiviteTreeDTO;
import fr.gouv.finances.dgfip.xemelios.web.gwt.client.dto.LabelValueDTO;
import fr.gouv.finances.dgfip.xemelios.web.gwt.client.dto.ParentCollectiviteDTO;
import fr.gouv.finances.dgfip.xemelios.web.gwt.client.dto.SpecialKeyDefDTO;
import fr.gouv.finances.dgfip.xemelios.web.gwt.client.service.ServiceLocator;
import fr.gouv.finances.dgfip.xemelios.web.gwt.client.utils.GWTUtils;
import java.util.ArrayList;
import java.util.List;

public class DlgSelectNavigationTarget extends DialogBox implements ClickListener, ChangeListener {
    private String documentId;
    private VerticalPanel vPane;
    private Grid container;
    private int insertedRows = 0;
    private Button pbOk, pbCancel;
    private Label lblCollectivite;
    protected ListBox cbxCollectivite, cbxBudget;
    private ArrayList<PnlCollectivite> addedCollectivites;
    protected ArrayList<ListBox> listBoxes;
    private TextArea logger = null;
    private boolean debug = false;
    private boolean onChangeEnabled = true;
    private int calls = 0;

    public DlgSelectNavigationTarget(String documentId) {
        super(false,true);
        setText(new HTML(getDialogTitle()).getHTML());
        this.documentId = documentId;
        listBoxes = new ArrayList<ListBox>();
        initComponents();
    }

    protected void initComponents() {
        vPane = new VerticalPanel();
        container = new Grid(2,3);
        container.setWidget(0,1,new Image(GWTUtils.getContextPath() + "/img/searching.gif"));
        vPane.add(container);
        container.setWidget(1,0,lblCollectivite = new Label(new HTML("Collectivit&eacute;").getHTML()));
        cbxCollectivite = new ListBox();
        listBoxes.add(cbxCollectivite);
        cbxCollectivite.setVisibleItemCount(1);
        cbxCollectivite.setName("collectivite");
        container.setWidget(1,1,cbxCollectivite);
        if(shouldIncludeBudgetSK()) {
            // dans tous les cas, on met le budget
            addRow("Budget",cbxBudget=new ListBox());
            listBoxes.add(cbxBudget);
            cbxBudget.setVisibleItemCount(1);
            cbxBudget.setName("budget");
            cbxCollectivite.addChangeListener(this);
            cbxCollectivite.addClickListener(this);
        }
        HorizontalPanel buttonPane = new HorizontalPanel();
        buttonPane.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_RIGHT);
        pbOk = new Button(new HTML("Ok").getHTML(), this);
        pbCancel = new Button(new HTML("Annuler").getHTML(),this);
        buttonPane.add(pbCancel);
        buttonPane.add(pbOk);
        vPane.add(buttonPane);
        if(debug) {
            vPane.add(logger = new TextArea());
            logger.setWidth("100%");
            logger.setCharacterWidth(150);
            logger.setHeight("200px");
            logger.setReadOnly(true);
        }
        center();
        setWidget(vPane);
        setVisible(true);
        initializeData();
    }
    protected void log(String msg) {
        if(debug) {
            String original = logger.getText();
            if(original==null) original = "";
            if(original.length()>0) original = original.concat("\r\n");
            logger.setText(original.concat(msg));
            logger.setCursorPos(original.length()+msg.length()-1);
        }
    }
    private int getCalls()  { return calls; }
    protected void initializeData() {
        ServiceLocator.getXemeliosService().getCollectiviteTree(documentId, shouldIncludeBudgetSK(), new AsyncCallback<CollectiviteTreeDTO>() {
            public void onFailure(Throwable caught) {
                log("Not supported yet.");
            }
            public void onSuccess(CollectiviteTreeDTO result) {
                lblCollectivite.setText(new HTML(result.getLibelleCollectivite()).getHTML());
                addedCollectivites = new ArrayList<PnlCollectivite>();
                boolean firstInsert = true;
                if(result.getCollectivitesParentModelCount()>0) {
                    for(int i=result.getCollectivitesParentModelCount()-1;i>=0;i--) {
                        PnlCollectivite[] parentItems = new PnlCollectivite[addedCollectivites.size()];
                        addedCollectivites.toArray(parentItems);
                        PnlCollectivite pnl = new PnlCollectivite(result.getParentModel(i),parentItems,i);
                        if(firstInsert) {
                            container.setWidget(0, 0, pnl.getLabel());
                            container.setWidget(0, 1, pnl.getTextBox());
                            container.setWidget(0, 2, pnl.getButton());
                            insertedRows++;
                            firstInsert = false;
                        } else {
                            insertRow(pnl);
                        }
                        addedCollectivites.add(pnl);
                        if(i==0) {
                            // on ajoute un Listener pour remplir les collectivites
                            pnl.setListener(new Listener() {
                                public void run() {
                                    updateCollectiviteList();
                                }
                            });
                        }
                    }
                } else {
                    // pour masquer l'image du chargement
                    container.setWidget(0, 1, new HTML());
                }
                // ajout des special keys si presentes
                if(shouldIncludeBudgetSK() && result.getSpecialKeys()!=null) {
                    cbxBudget.addChangeListener(DlgSelectNavigationTarget.this);
                    cbxBudget.addClickListener(DlgSelectNavigationTarget.this);
                    for(SpecialKeyDefDTO sk:result.getSpecialKeys()) {
                        SpecialKeyListBox cbx = new SpecialKeyListBox(sk);
                        addRow(sk.getLibelle(), cbx);
                        if(!listBoxes.isEmpty()) {
                            ListBox previous = listBoxes.get(listBoxes.size()-1);
                            if(previous!=cbx) {
                                previous.addChangeListener(DlgSelectNavigationTarget.this);
                                previous.addClickListener(DlgSelectNavigationTarget.this);
                            } else {
                                log("trying to add a listener to last listBox");
                            }
                        }
                        listBoxes.add(cbx);
                    }
                }
                // si pas de parent, on charge la liste des collectivites
                if(addedCollectivites.isEmpty()) {
                    updateCollectiviteList();
                }
            }
        });
    }
    protected void clearFields(PnlCollectivite sender) {
        boolean found = false;
        for(PnlCollectivite pnl:addedCollectivites) {
            if(!found) {
                found = (pnl==sender);
            } else {
                if(found) {
                    pnl.clearData();
                }
            }
        }
        if(found) {
            clearListBoxesDescendant(cbxCollectivite);
            cbxCollectivite.clear();
        }
    }

    protected void updateCollectiviteList() {
        String[] parentCollectivites = new String[addedCollectivites.size()];
        for(int i=0;i<addedCollectivites.size();i++) {
            PnlCollectivite pnl = addedCollectivites.get(i);
            if(pnl.getKey()!=null && pnl.getKey().length()>0) {
                parentCollectivites[pnl.parentModel.getLevel()] = pnl.getKey();
            }
        }
//        StringBuffer sb = new StringBuffer();
        ServiceLocator.getXemeliosService().getCollectivites(documentId, parentCollectivites, new AsyncCallback<List<LabelValueDTO>>() {
            public void onFailure(Throwable caught) {
                log("Not supported yet.");
            }
            public void onSuccess(List<LabelValueDTO> result) {
                while(cbxCollectivite.getItemCount()>0) cbxCollectivite.removeItem(0);
                for(LabelValueDTO v:result) {
                    cbxCollectivite.addItem(v.getLibelle(), v.getId());
                }
                if(cbxCollectivite.getItemCount()>=1) cbxCollectivite.setSelectedIndex(0);
                onChange(cbxCollectivite);
            }
        });
    }

    protected void insertRow(PnlCollectivite pnl) {
        container.resizeRows(container.getRowCount()+1);
        for(int i=container.getRowCount()-1;i>insertedRows;i--) {
            container.setWidget(i, 0, container.getWidget(i-1, 0));
            container.setWidget(i, 1, container.getWidget(i-1, 1));
            container.setWidget(i, 2, container.getWidget(i-1, 2));
        }
        container.setWidget(insertedRows, 0, pnl.getLabel());
        container.setWidget(insertedRows, 1, pnl.getTextBox());
        container.setWidget(insertedRows, 2, pnl.getButton());
        insertedRows++;
    }
    protected void addRow(String text, ListBox cbx) {
        int lastRow = container.getRowCount();
        container.resizeRows(lastRow+1);
        container.setWidget(lastRow, 0, new Label(new HTML(text).getHTML()));
        container.setWidget(lastRow, 1, cbx);
    }

    public boolean shouldIncludeBudgetSK() { return true; }

    public void onClick(Widget sender) {
        if(sender==pbCancel) {
            hide();
        } else if(sender==pbOk) {
            pbOkClicked();
        } else if(sender instanceof ListBox) {
            onChange(sender);
        }
    }

    public void pbOkClicked() {
        for(ListBox cbx:listBoxes) {
            if(cbx.getSelectedIndex()<0) {
                Window.alert("Vous devez renseigner cette valeur.");
                cbx.setFocus(true);
                return;
            }
        }
        ServiceLocator.getXemeliosService().getDefaultBrowsableEtat(documentId, new AsyncCallback<String>() {
            public void onFailure(Throwable caught) {
                log("Not supported yet.");
            }
            public void onSuccess(String result) {
                final String etatId = result;
                ServiceLocator.getXemeliosService().getDefaultBrowsableElement(documentId, etatId, new AsyncCallback<String>() {
                    public void onFailure(Throwable caught) {
                        log("Not supported yet.");
                    }
                    public void onSuccess(String result) {
                        final String elementId = result;
                        StringBuffer sb = new StringBuffer();
                        sb.append(GWTUtils.getContextPath()).append("/displayDocument.do?documentId=").append(documentId);
                        sb.append("&etatId=").append(etatId).append("&elementId=").append(elementId);
                        sb.append("&coll-code=").append(cbxCollectivite.getValue(cbxCollectivite.getSelectedIndex()));
                        sb.append("&budg-code=").append(cbxBudget.getValue(cbxBudget.getSelectedIndex()));
                        ListBox cbx = findNextListBox(cbxBudget);
                        while(cbx!=null) {
                            SpecialKeyListBox sklb = (SpecialKeyListBox)cbx;
                            sb.append("&sp").append(sklb.getSpecialKeyDef().getPos()).append("=").append(sklb.getValue(sklb.getSelectedIndex()));
                            cbx = findNextListBox(sklb);
                        }
                        GWTUtils.openUrlInNewWindow(sb.toString());
                        hide();
                    }
                });
            }
        });
    }

    public String getDialogTitle() { return "Choisir une collectivit&eacute;, un budget ..."; }

    public void onChange(Widget sender) {
        if(!onChangeEnabled) {
            log("skipping onChange");
            return;
        }
        setOnChangeEnabled(false);
        clearListBoxesDescendant((ListBox)sender);
        setOnChangeEnabled(true);
        if(sender==cbxCollectivite) {
            log("index: "+cbxCollectivite.getSelectedIndex());
            if(cbxCollectivite.getSelectedIndex()>=0 && shouldIncludeBudgetSK()) {
                String value = cbxCollectivite.getValue(cbxCollectivite.getSelectedIndex());
                ServiceLocator.getXemeliosService().getBudgetsFromDocument(documentId, value, new AsyncCallback<List<LabelValueDTO>>() {
                    public void onFailure(Throwable caught) {
                        log("Not supported yet.");
                    }
                    public void onSuccess(List<LabelValueDTO> result) {
                        for(LabelValueDTO lbl:result) {
                            cbxBudget.addItem(lbl.getLibelle(), lbl.getId());
                        }
                        if(cbxBudget.getItemCount()>=1) cbxBudget.setSelectedIndex(0);
                        onChange(cbxBudget);
                    }
                });
            }
        } else {
            // y a-t-il des special keys ?
            ListBox lstSender = (ListBox)sender;
            ListBox theOneToUpdate = findNextListBox(lstSender);
            if(theOneToUpdate!=null && lstSender.getSelectedIndex()>=0) {
                if(theOneToUpdate instanceof SpecialKeyListBox) {
                    final SpecialKeyListBox skCbx = (SpecialKeyListBox)theOneToUpdate;
                    int pos = skCbx.getSpecialKeyDef().getPos();
                    switch(pos) {
                        case 1: {
                            String collectivite = cbxCollectivite.getValue(cbxCollectivite.getSelectedIndex());
                            String budget = lstSender.getValue(lstSender.getSelectedIndex());
//                            Window.alert("calling getSpecialKeys1(...)");
                            ++calls;
                            final int localCalls = calls;
                            log("calling getSpecialKeys1-"+localCalls+"(...)");
                            ServiceLocator.getXemeliosService().getSpecialKeys1(documentId, collectivite, budget, new AsyncCallback<List<LabelValueDTO>>() {
                                public void onFailure(Throwable caught) {
                                    log("Not supported yet.");
                                }
                                public void onSuccess(List<LabelValueDTO> result) {
                                    final int inCalls = getCalls();
                                    if(inCalls!=localCalls) {
                                        log("skipping old call ret ("+inCalls+")");
                                        return;
                                    }
                                    StringBuffer sb = new StringBuffer();
                                    for(LabelValueDTO l:result) sb.append(l.getId()).append(",");
                                    
                                    log("onSuccess-"+localCalls+"("+sb.toString()+")");
                                    setOnChangeEnabled(false);
                                    skCbx.clear();
                                    for(LabelValueDTO l:result) {
                                        skCbx.addItem(l.getLibelle(), l.getId());
                                    }
                                    setOnChangeEnabled(true);
                                }
                            });
                        }
                        case 2: {
                            if(listBoxes.size()>=4) {
                                String collectivite = cbxCollectivite.getValue(cbxCollectivite.getSelectedIndex());
                                String budget = cbxBudget.getValue(cbxBudget.getSelectedIndex());
                                String sk1 = lstSender.getValue(lstSender.getSelectedIndex());
                                ++calls;
                                final int localCalls = calls;
                                log("calling getSpecialKeys2-"+localCalls+"(...)");
                                ServiceLocator.getXemeliosService().getSpecialKeys2(documentId, collectivite, budget, sk1, new AsyncCallback<List<LabelValueDTO>>() {
                                    public void onFailure(Throwable caught) {
                                        log("Not supported yet.");
                                    }
                                    public void onSuccess(List<LabelValueDTO> result) {
                                        final int inCalls = getCalls();
                                        if(inCalls!=localCalls) {
                                            log("skipping old call ret("+inCalls+")");
                                            return;
                                        }
                                        setOnChangeEnabled(false);
                                        skCbx.clear();
                                        for(LabelValueDTO l:result) {
                                            skCbx.addItem(l.getLibelle(), l.getId());
                                        }
                                        setOnChangeEnabled(true);
                                    }
                                });
                            } else {
                                log("skipping getSpecialKeys2 because of non existence of SP2");
                            }
                        }
                    }
                }
            }
        }
//        setOnChangeEnabled(true);
    }
    private void setOnChangeEnabled(boolean enable) { onChangeEnabled=enable; }

    private ListBox findNextListBox(ListBox current) {
        boolean found = false;
        for(ListBox cbx:listBoxes) {
            if(found) {
                return cbx;
            } else {
                found = (cbx==current);
            }
        }
        return null;
    }

    protected void clearListBoxesDescendant(ListBox parent) {
        boolean found = false;
        for(ListBox cbx:listBoxes) {
            if(found) {
                while(cbx.getItemCount()>0) cbx.removeItem(0);
            } else {
                if(cbx==parent) found = true;
            }
        }
    }

    protected class PnlCollectivite extends HorizontalPanel implements ClickListener {
        private ParentCollectiviteDTO parentModel;
        PnlCollectivite[] parentInputs;
        private TextBox input;
        private Button pb;
        private Label lbl;
        private String key, value;
        private Listener listener;
        public PnlCollectivite(ParentCollectiviteDTO parent, PnlCollectivite[] parentInputs,int count) {
            super();
            this.parentModel=parent;
            this.parentInputs = parentInputs;
            initComponents(count);
        }
        protected void initComponents(int count) {
            add(lbl = new Label(new HTML(parentModel.getLibelle()).getHTML()));
            lbl.getElement().setId("lbl"+count);
            input = new TextBox();
            input.setMaxLength(60);
            input.setReadOnly(true);
            input.getElement().setId("input"+count);
            input.setWidth("100%");
            add(input);
            pb = new Button("...");
            add(pb);
            pb.addClickListener(this);
            pb.getElement().setId("pb"+count);
        }
        public Button getButton() { return pb; }
        public Label getLabel() { return lbl; }
        public TextBox getTextBox() { return input; }

        public void onClick(Widget sender) {
            int parentFilledCount = 0;
            for(int i=0;i<parentInputs.length;i++) {
                if(parentInputs[i].getKey()!=null && parentInputs[i].getKey().length()>0)
                    parentFilledCount++;
            }
            final String[] parents = new String[parentFilledCount];
            int index = 0;
            if(parentFilledCount>0) {
                for(int i=parentInputs.length-1;i>=0;i--) {
                if(parentInputs[i].getKey()!=null && parentInputs[i].getKey().length()>0)
                    parents[index] = parentInputs[i].getKey();
                index++;
                }
            }
            final DlgChooseOnePair dlg = new DlgChooseOnePair(lbl.getText());
            dlg.show();
            dlg.center();
            final String docId = parentModel.getDocumentId();
            ServiceLocator.getXemeliosService().getParentCollectivites(docId, parentModel.getLevel()+1, parents, new AsyncCallback<List<LabelValueDTO>>() {

                public void onFailure(Throwable caught) {
                    throw new UnsupportedOperationException("Not supported yet.");
                }

                public void onSuccess(List<LabelValueDTO> result) {
                    dlg.setItems(result);
                    dlg.addSelectListener(new DlgChooseOnePair.SelectListener() {
                        public void onSelect(Object selectedItem) {
                            LabelValueDTO obj = (LabelValueDTO)selectedItem;
                            key = obj.getId();
                            value = obj.getLibelle();
                            input.setText(value);
                            clearFields(PnlCollectivite.this);
                            if(listener!=null) {
                                listener.run();
                            }
                        }
                    });
                }
            });
        }
        public String getKey() { return key; }
        public void setListener(Listener l) { this.listener = l; }
        public void clearData() { 
            input.setText("");
            key = null;
            value = null;
        }
    }

    protected static interface Listener {
        public void run();
    }

    protected class SpecialKeyListBox extends ListBox {
        private SpecialKeyDefDTO specialKeyDef;
        public SpecialKeyListBox(SpecialKeyDefDTO specialKeyDef) {
            super();
            this.specialKeyDef=specialKeyDef;
            setVisibleItemCount(1);
            setName("special-key"+specialKeyDef.getPos());
        }

        public SpecialKeyDefDTO getSpecialKeyDef() {
            return specialKeyDef;
        }

        public void setSpecialKeyDef(SpecialKeyDefDTO specialKeyDef) {
            this.specialKeyDef = specialKeyDef;
        }
        
    }
}
