/*
 * Copyright 2006-2007 Queplix Corp.
 *
 * Licensed under the Queplix Public License, Version 1.1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.queplix.com/solutions/commercial-open-source/queplix-public-license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 *
 */
package com.queplix.core.client.controls.listbox;

import com.google.gwt.user.client.ui.ChangeListener;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.KeyboardListener;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.Widget;
import com.queplix.core.client.app.vo.BaseFieldMeta;
import com.queplix.core.client.app.vo.ListboxFieldData;
import com.queplix.core.client.app.vo.SubsetData;
import com.queplix.core.client.app.vo.SubsetItemMeta;
import com.queplix.core.client.app.vo.uisettings.DialogUISettings;
import com.queplix.core.client.common.event.Event;
import com.queplix.core.client.common.event.EventListener;
import com.queplix.core.client.common.ui.OkayCancelPopup;
import com.queplix.core.client.common.ui.subsetselect.CheckboxSubsetSelect;

import java.util.ArrayList;

/**
 * QListBoxViewImpl
 *
 * @author
 */
class QListBoxViewImpl extends QListBoxView
        implements EventListener, ClickListener, ChangeListener {
    private static final String EMPTY_ITEM_TEXT = "";
    private static final String EMPTY_ITEM_VALUE = "EMPTY";
    private static final String SEPARATOR_ITEM_TEXT = "________";
    private static final String SEPARATOR_ITEM_VALUE = "SEPARATOR";
    private static final String NULL_ITEM_TEXT = "NULL";
    private static final String NULL_ITEM_VALUE = "NULL";
    private static final String OR_ITEM_TEXT = "Or...";
    private static final String OR_ITEM_VALUE = "OR";

    private static final String DEFAULT_WIDTH = "100%";
    private static final String DEFAULT_HEIGHT = "220px";

    private QListBoxModelImpl model;
    private boolean needEmptyOption = true;
    private boolean needNullOption = false;
    private boolean needOrOption = true;
    private ListBox list;

    private boolean userUpdate;
    private ArrayList changeListeners = new ArrayList();

    private OkayCancelPopup dialog;
    private CheckboxSubsetSelect subset;

    private SubsetItemMeta[] displayedItems;

    public QListBoxViewImpl(QListBoxModelImpl listModel, boolean isEmptyStr,
                            boolean isNullStr, boolean isOrStr, int layout) {
        super(listModel, layout);
        model = listModel;
        needEmptyOption = isEmptyStr;
        needNullOption = isNullStr;
        needOrOption = isOrStr;
        initializeGUI(layout);
        subscribeEvents();
    }

    public void onModelDataChanged() {
        super.onModelDataChanged();
        onListSelectionChanged();
    }

    public void onModelMetaChanged() {
        SubsetItemMeta[] newItems = model.getMeta().getAvailableChoises()
                .getItems();
        if(!BaseFieldMeta.isItemsEquals(displayedItems, newItems)) {
            setupListBox(newItems);
        }
    }

    private void subscribeEvents() {
        list.addClickListener(this);
        list.addChangeListener(this);
        list.addKeyboardListener(new KeyboardListener() {
            public void onKeyDown(Widget sender, char keyCode, int modifiers) {
                if(keyCode == KeyboardListener.KEY_ENTER) {
                    onChange(list);
                }
            }

            public void onKeyPress(Widget sender, char keyCode, int modifiers) {
            }

            public void onKeyUp(Widget sender, char keyCode, int modifiers) {
            }
        });
        dialog.getEventSource().addEventListener(this);
    }

    public void onChange(Widget sender) {
        String currentValue = list.getValue(list.getSelectedIndex());
        if(EMPTY_ITEM_VALUE.equals(currentValue)) { // empty option selected
            fireNoneSelected();
            return;
        }
        if(SEPARATOR_ITEM_VALUE.equals(currentValue)) { // separator selected
            list.setSelectedIndex(0); // reset; separator is not selectable
            return;
        }
        if(NULL_ITEM_VALUE.equals(currentValue)) { // Null option selected
            fireNullSelected();
            return;
        }
        if(OR_ITEM_VALUE.equals(currentValue)) { // Or option selected
            showDialog();
            return;
        }
        fireSingleSelectionChanged();
    }

    public void onListSelectionChanged() {
        if(!userUpdate) {
            int itemCount = model.getData().getItemsSelected()
                    .getSelectedIDs().length;
            if(itemCount > 1) {
                list.setSelectedIndex(
                        list.getItemCount() - 1); // select "Or..."
            } else if(itemCount == 1) {
                if(needNullOption && model.getData().getItemsSelected()
                        .isNullSelected()) {
                    list.setSelectedIndex(
                            list.getItemCount() - 2); // select "NULL"
                } else { //select single item
                    for(int i = 0; i < model.getItemCount(); i++) {
                        if(model.isItemSelected(i)) {
                            if(needEmptyOption) {
                                list.setSelectedIndex(i + 1);
                            } else {
                                list.setSelectedIndex(i);
                            }
                        }
                    }
                }
            } else {//no items selected
                list.setSelectedIndex(0);
            }
        }
    }

    private void fireSingleSelectionChanged() {
        if(needEmptyOption) {
            fireSelectionChanged(new int[]{list.getSelectedIndex() - 1});
        } else {
            fireSelectionChanged(new int[]{list.getSelectedIndex()});
        }
    }

    private void firePopupDemanded() {
        for(int i = 0; i < changeListeners.size(); i++) {
            ListSelectionListener listener
                    = (ListSelectionListener) changeListeners.get(i);
            listener.popupDemanded();
        }
    }

    private void fireNoneSelected() {
        fireSelectionChanged(new int[]{});
    }

    private void fireNullSelected() {
        fireSelectionChanged(new int[]{-1});
    }

    private void fireSelectionChanged(int[] selected) {
        userUpdate = true;
        for(int i = 0; i < changeListeners.size(); i++) {
            ListSelectionListener listener
                    = (ListSelectionListener) changeListeners.get(i);
            listener.selectionChanged(selected);
        }
        userUpdate = false;
    }

    public void addChangeListener(ListSelectionListener listener) {
        changeListeners.add(listener);
    }

    public void removeChangeListener(ListSelectionListener listener) {
        changeListeners.remove(listener);
    }

    private void initializeGUI(int layout) {
        //        initializeBaseUI(layout);
        if(null != getModel().getBaseMeta()) {
            dialog = new OkayCancelPopup(getModel().getBaseMeta().getCaption());
        } else {
            dialog = new OkayCancelPopup("");
        }
        list = new ListBox();
        subset = new CheckboxSubsetSelect(needNullOption);
        setupListBox(model.getMeta().getAvailableChoises().getItems());
        setCaption(model.getMeta().getCaption());

        HorizontalPanel fieldPanel = new HorizontalPanel();
        fieldPanel.add(list);
        fieldPanel.add(emptyButton);

        list.addStyleName("styled_input");

        addToPanel(fieldPanel);
        initPanel();
    }

    private void setupListBox(SubsetItemMeta[] items) {
        list.clear();
        if(needEmptyOption) {
            list.addItem(EMPTY_ITEM_TEXT, EMPTY_ITEM_VALUE);
        }
        for(int idx = 0; idx < model.getItemCount(); idx++) {
            list.addItem(items[idx].getCaption());
        }
        // Add "________" as last list item
        if(needNullOption || needOrOption) {
            list.addItem(SEPARATOR_ITEM_TEXT, SEPARATOR_ITEM_VALUE);
        }
        // Add "Null" as last list item
        if(needNullOption) {
            list.addItem(NULL_ITEM_TEXT, NULL_ITEM_VALUE);
        }
        // Add "Or" as last list item
        if(needOrOption) {
            list.addItem(OR_ITEM_TEXT, OR_ITEM_VALUE);
        }
        displayedItems = items;
    }

    public void setVisible(boolean isVisible) {
        captionLabel.setVisible(isVisible);
        list.setVisible(isVisible);
    }

    public void onEvent(Event event, Widget sender) {
        if(event == OkayCancelPopup.Events.OK) {
            QListBoxModel listBoxModel = (QListBoxModel) getModel();
            ListboxFieldData lfd = listBoxModel.getData();
            lfd.setItemsSelected(subset.getData());
            listBoxModel.setData(lfd);
            onListSelectionChanged();
        } else if(event == OkayCancelPopup.Events.CANCEL) {
            onListSelectionChanged();
        }
    }

    protected void onModeChanged(int newMode) {
        switch(newMode) {
            case MODE_SEARCH: {
                if(!needNullOption || !needOrOption) {
                    list.addItem(SEPARATOR_ITEM_TEXT, SEPARATOR_ITEM_VALUE);
                }
                if(!needNullOption) {
                    list.addItem(NULL_ITEM_TEXT, NULL_ITEM_VALUE);
                    needNullOption = true;
                }
                if(!needOrOption) {
                    list.addItem(OR_ITEM_TEXT, OR_ITEM_VALUE);
                    needOrOption = true;
                }
                break;
            }
            case MODE_NEW:
            case MODE_EDIT: {
                removeOrAndNullOptions();
            }
        }
    }

    protected void setEnabled(boolean isEnabled) {
        list.setEnabled(isEnabled);
    }

    private void removeOrAndNullOptions() {
        if(needNullOption || needOrOption) { // there is a separator
            list.removeItem(list.getItemCount() - 1);
        }
        if(needOrOption) {
            list.removeItem(list.getItemCount() - 1);
            needOrOption = false;
        }
        if(needNullOption) {
            list.removeItem(list.getItemCount() - 1);
            needNullOption = false;
        }
    }

    protected void setClientWidth(String clientWidth) {
        list.setWidth(clientWidth);
    }

    public int getClientWidth() {
        return list.getOffsetWidth();
    }

    private void populateData() {
        subset.setMeta(
                ((QListBoxModel) getModel()).getMeta().getAvailableChoises());
        subset.setData((SubsetData) ((QListBoxModel) getModel()).getData()
                .getItemsSelected().clone());
    }

    private void showDialog() {
        populateData();
        subset.setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
        dialog.setWidget(subset);
        dialog.setUISettings(getModel().getBaseMeta().getUISettings());
        dialog.show(list);
    }

    public void onClick(Widget sender) {
        firePopupDemanded();//todo change only on drop-down expand event
        // a work around to make "Or..." work when it is already selected:
        String currentValue = list.getValue(list.getSelectedIndex());
        if(currentValue.equals(OR_ITEM_VALUE)) {
            list.setSelectedIndex(0);
        }
    }

    protected DialogUISettings getUISettings() {
        return (dialog != null) ? dialog.getUISettings():null;
    }

    public int getFilledWidth() {
        return super.getFilledWidth() + emptyButton.getOffsetWidth() - 1;
    }
}
