/*
 * License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt
 */
package edu.caltech.ipac.firefly.ui.searchui;
/**
 * User: roby
 * Date: 1/28/14
 * Time: 1:18 PM
 */


import com.google.gwt.event.logical.shared.BeforeSelectionEvent;
import com.google.gwt.event.logical.shared.BeforeSelectionHandler;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.DeckLayoutPanel;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.Widget;
import edu.caltech.ipac.firefly.data.DataSetInfo;
import edu.caltech.ipac.firefly.data.Param;
import edu.caltech.ipac.firefly.data.ServerParams;
import edu.caltech.ipac.firefly.data.SpacialType;
import edu.caltech.ipac.firefly.ui.FieldDefCreator;
import edu.caltech.ipac.firefly.ui.GwtUtil;
import edu.caltech.ipac.firefly.ui.PopupUtil;
import edu.caltech.ipac.firefly.ui.SimpleTargetPanel;
import edu.caltech.ipac.firefly.ui.input.AsyncInputFieldGroup;
import edu.caltech.ipac.firefly.ui.input.SimpleInputField;
import edu.caltech.ipac.firefly.ui.table.TabPane;
import edu.caltech.ipac.firefly.util.WebAssert;
import edu.caltech.ipac.firefly.util.WebClassProperties;
import edu.caltech.ipac.util.dd.EnumFieldDef;
import edu.caltech.ipac.util.dd.ValidationException;

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

import static edu.caltech.ipac.firefly.data.SpacialType.AllSky;
import static edu.caltech.ipac.firefly.data.SpacialType.Box;
import static edu.caltech.ipac.firefly.data.SpacialType.Cone;
import static edu.caltech.ipac.firefly.data.SpacialType.Elliptical;
import static edu.caltech.ipac.firefly.data.SpacialType.IbeMultiPrevSearch;
import static edu.caltech.ipac.firefly.data.SpacialType.IbeMultiTableUpload;
import static edu.caltech.ipac.firefly.data.SpacialType.IbeSingleImage;
import static edu.caltech.ipac.firefly.data.SpacialType.MultiPoints;
import static edu.caltech.ipac.firefly.data.SpacialType.MultiPrevSearch;
import static edu.caltech.ipac.firefly.data.SpacialType.MultiTableUpload;
import static edu.caltech.ipac.firefly.data.SpacialType.Polygon;

/**
 * @author Trey Roby
 */
public class SpacialSelectUI extends Composite implements AsyncInputFieldGroup {

    private static final int NORMAL_HEIGHT_REQUIRED= 225;
    private static final int IBE_HEIGHT_REQUIRED= 250;
    private static final int ALL_SKY_HEIGHT_REQUIRED= 120;
    private static final int POLYGON_HEIGHT_REQUIRED= 205;
    private static final int PADDING_TOP= 12;

    private static final String CONE_KEY = "cone";
    private static final String ELLIPTICAL_KEY = "elliptical";
    private static final String BOX_KEY = "box";



    private static final int CONE_IDX= 0;
    private static final int ELLIPTICAL_IDX= 1;
    private static final int BOX_IDX= 2;


    private static final String UPLOAD_KEY = "upload";
    private static final String PREV_SEARCH_KEY = "prevSearch";
    private static final String COORDS_KEY = "coords";


    private static final int UPLOAD_IDX= 0;
    private static final int PREV_SEARCH_IDX= 1;
    private static final int COORDS_IDX= 2;


    private final Map<SpacialType,SpatialOps> spacialOpsMap= new HashMap<SpacialType, SpatialOps>(15);

    private static final WebClassProperties _prop = new WebClassProperties(SpacialSelectUI.class);
//    private DataSetInfo dsInfo= null;
    private DataSetInfo.DataTypes currDataType= DataSetInfo.DataTypes.CATALOGS;

    enum TabMode { SINGLE, IBE_SINGLE, MULTI, IBE_MULTI, POLYGON, ALL_SKY}
    FlowPanel mainPanel= new FlowPanel();
    private SimpleInputField  singleSearchMethod = null;
    private final SimplePanel singleSearchMethodWrapper= new SimplePanel();
    private final SimpleInputField multiSearchMethod =
            SimpleInputField.createByProp(
                    _prop.makeBase("multiMethod"), new SimpleInputField.Config("1px"), false);


    private Widget singleTarget;
    private Widget singleTargetIbe; // todo
    private Widget polyTarget;
    private Widget multiTarget;
    private Widget multiTargetIbe; //  todo
    private Widget allSky;
    private Set<SpacialType> stGroup= null;
    private DeckLayoutPanel singleModeOpsPanel= new DeckLayoutPanel();
    private DeckLayoutPanel multiModeOpsPanel= new DeckLayoutPanel();

                  // Tab pane & tabs
    private TabPane<Widget> spacialOpsTabs = new TabPane<Widget>();
    private TabPane.Tab<Widget> singleTab= null;
    private TabPane.Tab<Widget> ibeSingleTab= null;
    private TabPane.Tab<Widget> polyTab= null;
    private TabPane.Tab<Widget> multiTab= null;
    private TabPane.Tab<Widget> ibeMultiTab= null;
    private TabPane.Tab<Widget> allSkyTab= null;

    private int searchMaxArcSec= 1000;
    private boolean tabUpdateOn= true;
    private TabChange tabChangeListener;
    private TabMode selectedMode= TabMode.SINGLE;
    private SimpleTargetPanel targetPanel = new SimpleTargetPanel();
    private SimplePanel targetPanelIbeWrapper = new SimplePanel();
    private SimplePanel targetPanelWrapper = new SimplePanel();
    private CheckBox oneToOneCB= GwtUtil.makeCheckBox("Use 1-to-1 Matching","Use 1-to-1 Matching", false);
    private boolean visible= true;


    SpacialSelectUI(TabChange tabChangeListener) {
        initWidget(mainPanel);
        this.tabChangeListener= tabChangeListener;
        makeUI();

    }

    void setSpacialOptions(Set<SpacialType> stGroup, DataSetInfo.DataTypes dataType) {
        if (stGroup==null) return;
        if (this.stGroup==null || this.stGroup!=stGroup || currDataType!=dataType) {
            currDataType= dataType;
            this.stGroup= stGroup;
            if (this.stGroup.size()==0) {
                visible= false;
                mainPanel.setVisible(false);
                if (tabChangeListener!=null) tabChangeListener.onTabChange();
                return;
            }
            else {
                visible= true;
                mainPanel.setVisible(false);
            }
            boolean singleOp= stGroup.contains(Cone) || stGroup.contains(Box) || stGroup.contains(Elliptical);
            boolean ibeSingleOp= stGroup.contains(IbeSingleImage);
            boolean multiOp= stGroup.contains(MultiPoints) ||
                             stGroup.contains(MultiPrevSearch) ||
                             stGroup.contains(MultiTableUpload);
            boolean ibeMultiOp= stGroup.contains(IbeMultiTableUpload) || stGroup.contains(IbeMultiPrevSearch);
            boolean polyOp= stGroup.contains(Polygon);
            boolean allSkyOp= stGroup.contains(AllSky);
            reinitTabPane(singleOp, ibeSingleOp, multiOp, ibeMultiOp, polyOp, allSkyOp);
            if (singleOp) {
                reinitSingleSearchMethod(stGroup);
                updateSingleTargetDisplay();
            }

            if (multiOp) {
                reinitMultiOp();
            }

            if (ibeMultiOp) {
//                reinitIbeMultiOp(); //todo
            }

            updateSearchMax();
            if (tabUpdateOn) updateSelectedTab();
        }
    }

    public void makeUI() {
        spacialOpsTabs.addSelectionHandler(new SelectionHandler<Integer>() {
            public void onSelection(SelectionEvent<Integer> event) {
                if (tabUpdateOn) updateSelectedTab();
            }
        });

        spacialOpsTabs.addBeforeSelectionHandler(new BeforeSelectionHandler<Integer>() {
            public void onBeforeSelection(BeforeSelectionEvent<Integer> ev) {
                preSelectedTab(ev.getItem());
            }
        });

        makeSingle();
        makeIbeSingle();
        makeMulti();
        makeIbeMulti();
        makePolygon();
        makeAllSky();

        reinitTabPane(true, true, true, true, true, true);
        mainPanel.add(spacialOpsTabs);
        spacialOpsTabs.setSize("100%", "100%");
//        mainPanel.setSize("700px", "245px");
        mainPanel.setSize("600px", NORMAL_HEIGHT_REQUIRED+"px");
        GwtUtil.setStyle(mainPanel,"paddingTop", PADDING_TOP+"px");
    }

    private int getTabPaneSize() {
        int height;
        if (!visible) return 10;
        switch (selectedMode) {
            case POLYGON:
                height= POLYGON_HEIGHT_REQUIRED;
                break;
            case ALL_SKY:
                height= ALL_SKY_HEIGHT_REQUIRED;
                break;
            case IBE_SINGLE:
            case IBE_MULTI:
                height= IBE_HEIGHT_REQUIRED;
                break;
            case MULTI:
            case SINGLE:
            default:
                height= NORMAL_HEIGHT_REQUIRED;
                break;
        }
        return height;

    }

    public int getHeightRequired() {
        return getTabPaneSize()+PADDING_TOP;
    }

    public String getSpacialDesc() {
        StringBuilder sb= new StringBuilder(100);
        switch (computeSpacialType()) {
            case Cone:
                sb.append("Cone, ");
                sb.append(targetPanel.getTargetName());
                break;
            case Elliptical:
                sb.append("Elliptical, ");
                sb.append(targetPanel.getTargetName());
                break;
            case Box:
                sb.append("Box, ");
                sb.append(targetPanel.getTargetName());
                break;
            case IbeSingleImage:
                sb.append("Search, ");
                sb.append(targetPanel.getTargetName());
                break;
            case Polygon:
                sb.append("Polygon");
                break;
            case MultiTableUpload:
                sb.append("Upload");
                break;
            case MultiPrevSearch:
                sb.append("PrevSearch");
                break;
            case IbeMultiTableUpload:
                sb.append("Upload");
                break;
            case IbeMultiPrevSearch:
                sb.append("PrevSearch");
                break;
            case MultiPoints:
                sb.append("Multi object");
                break;
            case AllSky:
                sb.append("AllSky");
                break;
        }
        return sb.toString();

    }

    private void reinitTabPane(boolean singleOp,
                               boolean ibeSingleOp,
                               boolean multiOp,
                               boolean ibeMultiTabOp,
                               boolean polyOp,
                               boolean allSkyOp) {
        tabUpdateOn= false;
        TabPane.Tab<Widget> selectedTab= spacialOpsTabs.getSelectedTab();

        checkAndRemoveTab(selectedTab,singleTab,TabMode.SINGLE);
        checkAndRemoveTab(selectedTab,ibeSingleTab,TabMode.IBE_SINGLE);
        checkAndRemoveTab(selectedTab,polyTab,TabMode.POLYGON);
        checkAndRemoveTab(selectedTab,multiTab,TabMode.MULTI);
        checkAndRemoveTab(selectedTab,ibeMultiTab,TabMode.IBE_MULTI);
        checkAndRemoveTab(selectedTab,allSkyTab,TabMode.ALL_SKY);
        singleTab= null;
        ibeSingleTab= null;
        polyTab= null;
        multiTab= null;
        ibeMultiTab= null;
        allSkyTab= null;

        singleTab   = checkAndAddTab(singleOp,singleTarget,"Single");
        ibeSingleTab= checkAndAddTab(ibeSingleOp,singleTargetIbe,"Single");
        polyTab     = checkAndAddTab(polyOp,polyTarget,"Polygon");
        multiTab    = checkAndAddTab(multiOp,multiTarget,"Multi");
        ibeMultiTab = checkAndAddTab(ibeMultiTabOp,multiTargetIbe,"Multi");
        allSkyTab   = checkAndAddTab(allSkyOp,allSky,"All Sky");

        tabUpdateOn= true;
        selectedTab= findSelectedTabFromMode();
        preSelectedTab(selectedTab);
        spacialOpsTabs.selectTab(selectedTab);

    }

    private void checkAndRemoveTab(TabPane.Tab<Widget> selectedTab, TabPane.Tab<Widget> testTab, TabMode mode) {
        if (testTab!=null) {
            if (selectedTab==testTab) selectedMode= mode;
            spacialOpsTabs.removeTab(testTab);
        }
    }

    private TabPane.Tab<Widget> checkAndAddTab(boolean op, Widget w, String desc) {
        TabPane.Tab<Widget> retTab= null;
        if (op)  retTab= spacialOpsTabs.addTab(w,desc);
        return retTab;
    }


    private TabPane.Tab<Widget> findSelectedTabFromMode() {
         TabPane.Tab<Widget> retval;
        switch (selectedMode) {
            case SINGLE:
                retval= singleTab;
                break;
            case IBE_SINGLE:
                retval= ibeSingleTab;
                break;
            case MULTI:
                retval= multiTab;
                break;
            case IBE_MULTI:
                retval= ibeMultiTab;
                break;
            case POLYGON:
                retval= polyTab;
                break;
            case ALL_SKY:
                retval= allSkyTab;
                break;
            default:
                retval= null;
                break;
        }
        if (!spacialOpsTabs.isTabVisible(retval)) retval= spacialOpsTabs.getVisibleTab(0);
        return retval;

    }


    private void preSelectedTab(int nextIdx) {
        TabPane.Tab<Widget> preTab= spacialOpsTabs.getVisibleTab(nextIdx);
        preSelectedTab(preTab);
    }

    private void preSelectedTab(TabPane.Tab<Widget> preTab) {
        if (preTab!=null) {
            if      (preTab==singleTab) {
                targetPanelWrapper.setWidget(targetPanel);
            }
            else if (preTab==ibeSingleTab) {
                targetPanelIbeWrapper.setWidget(targetPanel);
            }
        }
    }



    private void updateSelectedTab() {
        TabPane.Tab<Widget> selectedTab= spacialOpsTabs.getSelectedTab();
        if      (selectedTab==singleTab) selectedMode= TabMode.SINGLE;
        else if (selectedTab==ibeSingleTab) selectedMode= TabMode.IBE_SINGLE;
        else if (selectedTab==multiTab) selectedMode= TabMode.MULTI;
        else if (selectedTab==ibeMultiTab) selectedMode= TabMode.IBE_MULTI;
        else if (selectedTab==polyTab) selectedMode= TabMode.POLYGON;
        else if (selectedTab==allSkyTab) selectedMode= TabMode.ALL_SKY;
        updateSearchMax();
        mainPanel.setSize("600px", getTabPaneSize()+"px");
        if (tabChangeListener!=null) tabChangeListener.onTabChange();
    }


    private void makeMulti() {

        SpacialBehaviorPanel.TableUpload upload=  new SpacialBehaviorPanel.TableUpload();
        SpacialBehaviorPanel.PrevSearch prevSearch= new SpacialBehaviorPanel.PrevSearch();
        SpacialBehaviorPanel.UserEnteredCoords coords= new SpacialBehaviorPanel.UserEnteredCoords();

        multiSearchMethod.getField().addValueChangeHandler(new ValueChangeHandler<String>() {
            public void onValueChange(ValueChangeEvent ev) {
                updateMultiTargetDisplay();
            }
        });

        multiModeOpsPanel.add(upload.makePanel());
        multiModeOpsPanel.add(prevSearch.makePanel());
        multiModeOpsPanel.add(coords.makePanel());
        multiModeOpsPanel.showWidget(0);
        multiModeOpsPanel.setSize("220px", "70px");


        FlowPanel methodContainer= new FlowPanel();
        methodContainer.add(multiModeOpsPanel);
//        methodContainer.add(multiSearchMethod); // todo comment when we support more methods
        GwtUtil.setStyles(multiSearchMethod,
                          "display", "inline-block",
                          "verticalAlign", "top",
                          "padding", "8px 0 0 20px");
        GwtUtil.setStyles(multiModeOpsPanel, "display", "inline-block",
                          "padding", "0 0 0 30px");

        SpacialBehaviorPanel.Cone multiCone=  new SpacialBehaviorPanel.Cone();
        FlowPanel container= new FlowPanel();


        Widget multiConePanel= multiCone.makePanel();
        SimplePanel oneToOneWrapper= new SimplePanel(oneToOneCB);

        container.add(methodContainer);
        container.add(multiConePanel);
        container.add(oneToOneWrapper);


        GwtUtil.setStyles(oneToOneWrapper,
                          "padding", "25px 0 0 0"
                          );

        GwtUtil.setStyles(methodContainer,
//                          "padding", "6px 0 0 0",
//                          "borderTop", "1px solid rgba(0,0,0,.35)",
//                          "marginTop", "10px",
                          "width", "84%"
        );

        GwtUtil.setStyles(multiConePanel,
//                          "padding", "6px 0 0 0",
                          "borderTop", "1px solid rgba(0,0,0,.35)",
//                          "marginTop", "10px",
                          "width", "84%"
        );

        multiTarget= container;
        spacialOpsMap.put(SpacialType.MultiTableUpload,
                          new SpatialOps.TableUpload(multiCone.getField(),
                                                     upload.getUploadField(),multiCone,oneToOneCB));
        spacialOpsMap.put(SpacialType.MultiPrevSearch,
                          new SpatialOps.PrevSearch(multiCone.getField(), multiCone,oneToOneCB));
        spacialOpsMap.put(SpacialType.MultiPoints,
                          new SpatialOps.MultiPoint(multiCone.getField(), multiCone,oneToOneCB));

        reinitMultiOp();

    }


    private void makeIbeMulti() {

        SpacialBehaviorPanel.IbeTableUpload ibeTab=  new SpacialBehaviorPanel.IbeTableUpload();
        Widget ibeTabUploadPanel= ibeTab.makePanel();
        //todo

        spacialOpsMap.put(SpacialType.IbeMultiTableUpload, new SpatialOps.IbeTableUpload(ibeTab.getIntersect(),
                                                                                         ibeTab.getSize(),
                                                                                         ibeTab.getSubSize(),
                                                                                         ibeTab.getMCenter(),
                                                                                         ibeTab.getUploadField()) );
        multiTargetIbe= ibeTabUploadPanel;
    }



    private void reinitMultiOp() {
        if (currDataType!=null && stGroup!=null) {
            boolean hidden= !stGroup.contains(SpacialType.MultiSupportsOneToOneAttribute);
            GwtUtil.setHidden(oneToOneCB,hidden);
            if (hidden) oneToOneCB.setValue(false);
        }
    }

    private void makePolygon() {
        FlowPanel panel= new FlowPanel();
        SpacialBehaviorPanel.Polygon p = new SpacialBehaviorPanel.Polygon();
        panel.add(p.makePanel());
        polyTarget= panel;
        spacialOpsMap.put(SpacialType.Polygon, new SpatialOps.Polygon(p.getPolygonField()));

    }




    private void makeAllSky() {
        allSky= new Label("Search all the sky");
        spacialOpsMap.put(SpacialType.AllSky, new SpatialOps.AllSky());
    }

    private void makeSingle() {

        SpacialBehaviorPanel.Cone cone=  new SpacialBehaviorPanel.Cone();
        SpacialBehaviorPanel.Elliptical elliptical= new SpacialBehaviorPanel.Elliptical();
        SpacialBehaviorPanel.Box box= new SpacialBehaviorPanel.Box();

        reinitSingleSearchMethod(new HashSet<SpacialType>(Arrays.asList(Cone,Box,Elliptical)));
        singleModeOpsPanel.add(cone.makePanel());
        singleModeOpsPanel.add(elliptical.makePanel());
        singleModeOpsPanel.add(box.makePanel());
        singleModeOpsPanel.showWidget(0);
        singleModeOpsPanel.setSize("300px", "130px");

        FlowPanel methodContainer= new FlowPanel();
        methodContainer.add(singleSearchMethodWrapper);
        methodContainer.add(singleModeOpsPanel);
        GwtUtil.setStyles(singleSearchMethodWrapper,
                          "display", "inline-block",
                          "verticalAlign", "top",
                          "padding", "8px 45px 0 0px");
        GwtUtil.setStyles(singleModeOpsPanel, "display", "inline-block",
                          "padding", "0 0 0 30px");

        FlowPanel container= new FlowPanel();
        container.add(targetPanelWrapper);
        container.add(methodContainer);

        GwtUtil.setStyles(methodContainer,
                          "padding", "6px 0 0 0",
                          "borderTop", "1px solid rgba(0,0,0,.35)",
                          "marginTop", "10px",
                          "width", "90%"
        );

        targetPanelWrapper.setWidget(targetPanel);
        singleTarget= container;

        spacialOpsMap.put(SpacialType.Cone, new SpatialOps.Cone(cone.getField(), cone));
        spacialOpsMap.put(SpacialType.Box, new SpatialOps.Box(box.getField(), cone));
        spacialOpsMap.put(SpacialType.Elliptical, new SpatialOps.Elliptical(elliptical.getAxisField(),
                                                                            elliptical.getPaField(),
                                                                            elliptical.getRatioField(), cone ));
    }


    private void makeIbeSingle() {
        SpacialBehaviorPanel.IbeSingle ibeSingle=  new SpacialBehaviorPanel.IbeSingle();
        Widget ibeSinglePanel= ibeSingle.makePanel();
        FlowPanel container= new FlowPanel();
        container.add(targetPanelIbeWrapper);
        container.add(ibeSinglePanel);

        GwtUtil.setStyles(ibeSinglePanel,
                          "padding", "6px 0 0 0",
                          "borderTop", "1px solid rgba(0,0,0,.35)",
                          "marginTop", "10px",
                          "width", "95%"
        );
        //todo

        spacialOpsMap.put(SpacialType.IbeSingleImage, new SpatialOps.IbeSingle(ibeSingle.getIntersect(),
                                                                               ibeSingle.getSize(),
                                                                               ibeSingle.getSubSize(),
                                                                               ibeSingle.getMCenter()));
        singleTargetIbe= container;
    }



    private void reinitSingleSearchMethod(Set <SpacialType> spacialTypes) {
        singleSearchMethodWrapper.clear();
        String prevValue= singleSearchMethod!=null ? singleSearchMethod.getValue() : null;
        EnumFieldDef fd= (EnumFieldDef)FieldDefCreator.makeFieldDef(_prop.makeBase("singleTargetMethod"));
        if (!spacialTypes.contains(Cone)) {
            fd.removeItem(CONE_KEY);
            if (CONE_KEY.equals(prevValue)) prevValue= null;
        }
        if (!spacialTypes.contains(Box)) {
            fd.removeItem(BOX_KEY);
            if (BOX_KEY.equals(prevValue)) prevValue= null;
        }
        if (!spacialTypes.contains(Elliptical)) {
            fd.removeItem(ELLIPTICAL_KEY);
            if (ELLIPTICAL_KEY.equals(prevValue)) prevValue= null;
        }

        singleSearchMethod = SimpleInputField.createByDef(fd, new SimpleInputField.Config("1px"), false);
        singleSearchMethod.getField().addValueChangeHandler(new ValueChangeHandler<String>() {
            public void onValueChange(ValueChangeEvent ev) {
                updateSingleTargetDisplay();
            }
        });
        singleSearchMethodWrapper.setWidget(singleSearchMethod);
        if (prevValue!=null) singleSearchMethod.setValue(prevValue);
    }

    private void updateSingleTargetDisplay() {
        String mode= singleSearchMethod.getValue();
        singleModeOpsPanel.showWidget(convertSingleModeToIdx(mode));
    }

    private void updateMultiTargetDisplay() {
        String mode= multiSearchMethod.getValue();
        multiModeOpsPanel.showWidget(convertMultiModeToIdx(mode));
    }


    private SpacialType convertSingleModeToSpacialType(String singleMode) {
        SpacialType retval= Cone;
        if      (singleMode.equals(CONE_KEY))        retval= Cone;
        else if (singleMode.equals(ELLIPTICAL_KEY))  retval= Elliptical;
        else if (singleMode.equals(BOX_KEY))         retval= Box;
        else  WebAssert.argTst(false, "unknown mode for SpacialSelectUI single target:"+singleMode);
        return retval;
    }

    private int convertSingleModeToIdx(String singleMode) {
        int retval= 0;
        if      (singleMode.equals(CONE_KEY))        retval= CONE_IDX;
        else if (singleMode.equals(ELLIPTICAL_KEY))  retval= ELLIPTICAL_IDX;
        else if (singleMode.equals(BOX_KEY))         retval= BOX_IDX;
        else  WebAssert.argTst(false, "unknown mode for SpacialSelectUI single target:"+singleMode);
        return retval;
    }
    private int convertSingleModeToIdx(SpacialType singleMode) {
        int retval;
        if      (singleMode== SpacialType.Cone)        retval= CONE_IDX;
        else if (singleMode== SpacialType.Elliptical)  retval= ELLIPTICAL_IDX;
        else if (singleMode== SpacialType.Box)         retval= BOX_IDX;
        else  retval= 0;
        return retval;
    }
    private String convertSingleSpacialTypeToMode(SpacialType singleSpacialType) {
        String retval= CONE_KEY;
        switch (singleSpacialType) {
            case Cone:       retval= CONE_KEY;
                break;
            case Elliptical: retval= ELLIPTICAL_KEY;
                break;
            case Box:        retval= BOX_KEY;
                break;
            case Polygon:
            case MultiTableUpload:
            case MultiPrevSearch:
            case MultiPoints:
            case AllSky:
            default:
                WebAssert.argTst(false, "spacialType should only be single target:"+singleSpacialType.toString());
                break;
        }
        return retval;
    }

    private int convertMultiModeToIdx(String multiMode) {
        int retval= 0;
        if      (multiMode.equals(UPLOAD_KEY))        retval= UPLOAD_IDX;
        else if (multiMode.equals(PREV_SEARCH_KEY))  retval= PREV_SEARCH_IDX;
        else if (multiMode.equals(COORDS_KEY))         retval= COORDS_IDX;
        else  WebAssert.argTst(false, "unknown mode for SpacialSelectUI single target:"+multiMode);
        return retval;
    }

    private SpacialType convertMultiModeToSpacialType(String multiMode) {
        SpacialType retval= MultiTableUpload;
        if      (multiMode.equals(UPLOAD_KEY))        retval= MultiTableUpload;
        else if (multiMode.equals(PREV_SEARCH_KEY))  retval= MultiPrevSearch;
        else if (multiMode.equals(COORDS_KEY))         retval= MultiPoints;
        else  WebAssert.argTst(false, "unknown mode for SpacialSelectUI multi mode:"+multiMode);
        return retval;
    }

    private String convertMultiSpacialTypeToMode(SpacialType multiSpacialType) {

        String retval= UPLOAD_KEY;
        switch (multiSpacialType) {
            case MultiTableUpload: retval= UPLOAD_KEY;
                break;
            case MultiPrevSearch:  retval= PREV_SEARCH_KEY;
                break;
            case MultiPoints:      retval= COORDS_KEY;
                break;
            case Cone:
            case Elliptical:
            case Box:
            case Polygon:
            case AllSky:
            default:
                WebAssert.argTst(false, "spacialType should only be single target:"+multiSpacialType.toString());
                break;
        }
        return retval;
    }

    public void updateSearchMax(int maxArcSec) {
        searchMaxArcSec= maxArcSec;
        updateSearchMax();
    }

    public void updateSearchMax() {
        spacialOpsMap.get(computeSpacialType()).updateMax(searchMaxArcSec);
    }

    public boolean validate() {
        if (!visible) return true;
        try {
            boolean tValid= true;
            if (selectedMode==TabMode.SINGLE || selectedMode==TabMode.IBE_SINGLE) {
                tValid= targetPanel.validate();
            }

            return tValid && spacialOpsMap.get(computeSpacialType()).validate();
        } catch (ValidationException e) {
            PopupUtil.showError("Error", e.getMessage());
            return false;
        }
    }

    public SpacialType computeSpacialType() {
        SpacialType  spacialType= SpacialType.Cone;
        switch (selectedMode) {
            case SINGLE:
                spacialType= convertSingleModeToSpacialType(singleSearchMethod.getValue());
                break;
            case IBE_SINGLE:
                spacialType= IbeSingleImage;
                break;
            case MULTI:
                spacialType= convertMultiModeToSpacialType(multiSearchMethod.getValue());
                break;
            case POLYGON:
                spacialType= Polygon;
                break;
            case ALL_SKY:
                spacialType= AllSky;
                break;
        }
        return spacialType;
    }

    public void defineSpacialType(SpacialType sType) {
        switch (sType) {
            case Cone:
            case Elliptical:
            case Box:
                spacialOpsTabs.selectTab(singleTab);
                singleSearchMethod.setValue(convertSingleSpacialTypeToMode(sType));
                break;
            case IbeSingleImage:
                spacialOpsTabs.selectTab(ibeSingleTab);
                break;
            case IbeMultiPrevSearch:
            case IbeMultiTableUpload:
                spacialOpsTabs.selectTab(ibeSingleTab);
                break;
            case MultiTableUpload:
            case MultiPrevSearch:
            case MultiPoints:
                spacialOpsTabs.selectTab(multiTab);
                multiSearchMethod.setValue(convertMultiSpacialTypeToMode(sType));
                break;
            case Polygon:
                spacialOpsTabs.selectTab(polyTab);
                break;
            case AllSky:
                spacialOpsTabs.selectTab(allSkyTab);
                break;
        }
    }


    public void getFieldValuesAsync(final AsyncCallback<List<Param>> cb) {
        SpatialOps ops= spacialOpsMap.get(computeSpacialType());
        if (ops.getRequireUpload()) {
            ops.doUpload(new AsyncCallback<String>() {
                public void onFailure(Throwable caught) {
                    cb.onFailure(caught);
                }

                public void onSuccess(String s) {
                    cb.onSuccess(getFieldValues());
                }
            });

        }
        else {
            cb.onSuccess(getFieldValues());
        }
    }

    public boolean isAsyncCallRequired() {
        SpatialOps ops= spacialOpsMap.get(computeSpacialType());
        return ops.getRequireUpload();
    }

    public List<Param> getFieldValues() {
        ArrayList<Param> list= new ArrayList<Param>(10);
        SpacialType sType= computeSpacialType();
        SpatialOps ops= spacialOpsMap.get(sType);
        list.add(new Param(ServerParams.SPACIAL_TYPE, ops.getSpacialType().toString()));
        list.addAll(ops.getParams());

        if (ops.getRequireTarget()) {
            list.addAll(targetPanel.getFieldValues());
        }
        return list;
    }

    public void setFieldValues(List<Param> list) {
        SpacialType st= SpacialType.Cone;
        for(Param p : list) {
            if (p.getName().equalsIgnoreCase(ServerParams.SPACIAL_TYPE)) {
                try {
                    st= Enum.valueOf(SpacialType.class, p.getValue());
                } catch (Exception e) {
                    st= SpacialType.Cone;
                }
                defineSpacialType(st);
                break;
            }
        }
        SpatialOps ops= spacialOpsMap.get(st);
        ops.setParams(list);
    }

//====================================================================
//  implementing HasWidget
//====================================================================

    public void add(Widget w) { throw new UnsupportedOperationException("operation not allowed"); }
    public void clear() { throw new UnsupportedOperationException("operation not allowed"); }
    public boolean remove(Widget w) { throw new UnsupportedOperationException("operation not allowed"); }

    public Iterator<Widget> iterator() {
        return new ArrayList<Widget>().iterator(); // todo decide what goes here
        //return new ArrayList<Widget>(Arrays.asList(posField, resolveByField)).iterator();
    }


    public interface TabChange {
        void onTabChange();
    }

}