/*
 * 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.grid;

import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.ChangeListener;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ScrollPanel;
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 com.queplix.core.client.app.vo.SubsetData;
import com.queplix.core.client.common.event.Event;
import com.queplix.core.client.common.event.EventListener;
import com.queplix.core.client.common.ui.ButtonData;
import com.queplix.core.client.common.ui.DialogHelper;
import com.queplix.core.client.common.ui.IconButton;
import com.queplix.core.client.common.ui.MenuPopup;
import com.queplix.core.client.common.ui.OkayCancelPopup;
import com.queplix.core.client.common.ui.grid.GridSelectionModListener;
import com.queplix.core.client.common.ui.grid.GridSelectionModListenerCollection;
import com.queplix.core.client.common.ui.grid.PagerHyperlink;
import com.queplix.core.client.common.ui.grid.SourcesGridSelectionModEvents;
import com.queplix.core.client.i18n.I18N;

import java.util.ArrayList;

/**
 * This class contais response data for Load Report Request.
 *
 * @author Sergey Kozmin, Mike, Aliaksandr Melnik
 * @since 19 September 2006
 */
class QGridPager extends ScrollPanel implements QPagerEvents,
        ClickListener,
        ChangeListener,
        EventListener,
        SourcesGridSelectionModEvents {

    private static int VISIBLE_PAGES_COUNT = 10;

    private static final String PAGER_PADDING_RIGHT_ELEMENT_STYLE
            = "gridPager-paddingRightElement";

    private static final String COUNTER_TEXT = I18N.getMessages().gridCounter();
    private static final String TOTAL_RECORDS_TEXT =
            I18N.getMessages().gridTotalRecords() + ":";
    private static final String RECORDS_PAGE_TEXT =
            I18N.getMessages().gridRecordsPage() + ":";
    private static final String LOADED_RECORDS_TEXT =
            I18N.getMessages().gridLoaded() + ":";
    private static final String POSITIVE_INT_ERROR_MESSAGE = I18N.getMessages()
            .gridPageSizePositiveMessage();

    private static final ButtonData MENU_BUTTON = new ButtonData(null, null,
            "grid/menu.gif", null, "grid/menu_bright.gif");
    private static final ButtonData REFRESH_BUTTON = new ButtonData(null,
            I18N.getMessages().refreshGrid(), "grid/refresh.gif");
    private static final ButtonData WORD_BUTTON = new ButtonData(null,
            I18N.getMessages().exportToWord(), "grid/word.gif");
    private static final ButtonData PRINT_BUTTON = new ButtonData(null,
            I18N.getMessages().printGrid(), "grid/print.gif");
    private static final ButtonData EXCEL_BUTTON = new ButtonData(null,
            I18N.getMessages().exportToExcel(), "grid/excel.gif");

    private HorizontalPanel mainPanel;

    private HorizontalPanel panel;
    private CheckBox counterCtrl;
    private Label counterLabel;
    private TextBox pageSizeCtrl;
    private Label loadedRecordsCtrl;
    private Label totalRecordsLabel;
    private Label totalRecordsCtrl;
    private HorizontalPanel pagesCtrl;
    private QGridCustomizer customizer;
    private IconButton refreshButton;
    private IconButton printButton;
    private IconButton wordButton;
    private IconButton excelButton;
    private MenuPopup menu;

    public static interface Events {
        Event SELECT_ALL = new Event();
        Event DESELECT_ALL = new Event();
        Event INVERT_SELECTION = new Event();
    }

    private ArrayList pagerListeners = new ArrayList();
    private GridSelectionModListenerCollection selectionModListeners
            = new GridSelectionModListenerCollection();

    private QGridModelImpl model;

    private boolean inPopup;

    public QGridPager(QGridModelImpl model, boolean inPopup,
                      boolean customizable, boolean refreshable,
                      boolean hasExtendedSelectionActions) {
        this.model = model;
        this.inPopup = inPopup;

        setupWidgets(customizable, refreshable, hasExtendedSelectionActions);
        subscribeEvents(customizable, refreshable);
    }

    private void setupWidgets(boolean customizable, boolean refreshable,
                              boolean hasExtendedSelectionActions) {
        mainPanel = new HorizontalPanel();
        setWidget(mainPanel);
        DOM.setStyleAttribute(this.getElement(), "overflow", "hidden");

        mainPanel.setStyleName("gridPager-Bg");
        panel = new HorizontalPanel();
        panel.setStyleName("gridPager-Container");
        panel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);

        HorizontalPanel counterPanel = new HorizontalPanel();
        counterCtrl = new CheckBox("");
        counterCtrl.setChecked(model.isCounterToggledOn());
        counterLabel = new Label(COUNTER_TEXT);

        counterPanel.setStyleName("gridPager-Counter");
        counterPanel.add(counterCtrl);
        counterPanel.add(counterLabel);
        counterLabel.addStyleName(PAGER_PADDING_RIGHT_ELEMENT_STYLE);
        counterPanel.setCellVerticalAlignment(counterCtrl,
                VerticalPanel.ALIGN_MIDDLE);
        counterPanel.setCellVerticalAlignment(counterLabel,
                VerticalPanel.ALIGN_MIDDLE);

        mainPanel.add(counterPanel);
        mainPanel.setCellVerticalAlignment(counterPanel,
                VerticalPanel.ALIGN_MIDDLE);
        mainPanel.add(panel);
        mainPanel.setCellWidth(panel, "100%");
        mainPanel.setCellHorizontalAlignment(panel, VerticalPanel.ALIGN_LEFT);
        panel.add(new Label(RECORDS_PAGE_TEXT));
        panel.setSpacing(2);

        pageSizeCtrl = new TextBox();
        pageSizeCtrl.setText(model.getPageSize() + "");
        pageSizeCtrl.setStyleName("gridPager-PageSize");
        pageSizeCtrl.addStyleName("styled_black_input");
        HorizontalPanel holderPanel = new HorizontalPanel();
        holderPanel.add(pageSizeCtrl);
        holderPanel.setCellVerticalAlignment(pageSizeCtrl,
                VerticalPanel.ALIGN_MIDDLE);
        holderPanel.setCellWidth(pageSizeCtrl, "100%");
        holderPanel.addStyleName(PAGER_PADDING_RIGHT_ELEMENT_STYLE);
        panel.add(holderPanel);

        totalRecordsLabel = new Label(TOTAL_RECORDS_TEXT);
        panel.add(totalRecordsLabel);
        totalRecordsCtrl = new Label();
        totalRecordsCtrl.addStyleName(PAGER_PADDING_RIGHT_ELEMENT_STYLE);
        panel.add(totalRecordsCtrl);

        panel.add(new Label(LOADED_RECORDS_TEXT));
        loadedRecordsCtrl = new Label();
        panel.add(loadedRecordsCtrl);
        loadedRecordsCtrl.addStyleName(PAGER_PADDING_RIGHT_ELEMENT_STYLE);
        setupLoadedRecords();

        pagesCtrl = new HorizontalPanel();
        panel.add(pagesCtrl);
        setupPages();

        if(customizable) {
            customizer = new QGridCustomizer(model.getIncomingMetaData());
            panel.add(customizer);
        }
        if(refreshable) {
            refreshButton = new IconButton(REFRESH_BUTTON);
            if(!inPopup) {
                // For now, in-popup Grids don't support refreshing
                // In fact, each of them has to implement refreshing separately
                // TODO: fix that
                panel.add(refreshButton);
            }
        }
        if(!inPopup) {
            printButton = new IconButton(PRINT_BUTTON);
            wordButton = new IconButton(WORD_BUTTON);
            excelButton = new IconButton(EXCEL_BUTTON);
            panel.add(printButton);
            panel.add(wordButton);
            panel.add(excelButton);
        }

        if(hasExtendedSelectionActions) {
            createMenu();
        }
    }

    private void createMenu() {
        IconButton menuButton = new IconButton(MENU_BUTTON);
        menuButton.addStyleName("gridPager-menuButton");

        menu = new MenuPopup(menuButton);

        IconButton ib = new IconButton(new ButtonData(
                I18N.getMessages().selectAll()));
        menu.addButton(Events.SELECT_ALL, ib.getButtonState());
        ib = new IconButton(new ButtonData(I18N.getMessages().deselectAll()));
        menu.addButton(Events.DESELECT_ALL, ib.getButtonState());
        ButtonData invSelButtonData = new ButtonData(
                I18N.getMessages().invertSelection());
        invSelButtonData.setCaptionStyle("common_nobr");
        ib = new IconButton(invSelButtonData);
        menu.addButton(Events.INVERT_SELECTION, ib.getButtonState());
        menu.getEventSource().addEventListener(this);

        mainPanel.add(menuButton);
        mainPanel.setCellHorizontalAlignment(menuButton,
                HorizontalPanel.ALIGN_RIGHT);
    }

    public MenuPopup getMenu() {
        return menu;
    }

    private void subscribeEvents(boolean customizable, boolean refreshable) {
        counterCtrl.addClickListener(this);
        counterLabel.addClickListener(this);
        pageSizeCtrl.addChangeListener(this);

        if(customizable) {
            customizer.getEventSource().addEventListener(this);
        }
        if(refreshable) {
            refreshButton.addClickListener(this);
        }
        if(!inPopup) {
            printButton.addClickListener(this);
            wordButton.addClickListener(this);
            excelButton.addClickListener(this);
        }
    }

    private void setupLoadedRecords() {
        loadedRecordsCtrl.setText(String.valueOf(model.getModelRowCount()));
    }

    private void setupPages() {
        pagesCtrl.clear();

        final long currentPage = model.getCurrentPage();

        final PagerHyperlink firstPageLink = new PagerHyperlink();
        firstPageLink.setHTML("&lt;&lt;");
        firstPageLink.addClickListener(new ClickListener() {
            public void onClick(Widget sender) {
                firePageChanged(0);
            }
        });
        firstPageLink.addStyleName(PAGER_PADDING_RIGHT_ELEMENT_STYLE);
        firstPageLink.setDisabled(currentPage == 0);
        pagesCtrl.add(firstPageLink);

        PagerHyperlink prevPageLink = new PagerHyperlink();
        prevPageLink.setHTML("&lt;");
        prevPageLink.addClickListener(new ClickListener() {
            public void onClick(Widget sender) {
                firePageChanged(currentPage - 1);
            }
        });
        prevPageLink.addStyleName(PAGER_PADDING_RIGHT_ELEMENT_STYLE);
        prevPageLink.setDisabled(currentPage == 0);
        pagesCtrl.add(prevPageLink);

        if(model.isCounterToggledOn()) {
            totalRecordsLabel.setVisible(true);
            totalRecordsCtrl.setVisible(true);
            totalRecordsCtrl.setText(Long.toString(
                    model.getRecordsTotal() == -1 ? 0:model.getRecordsTotal()));
        } else {
            totalRecordsLabel.setVisible(false);
            totalRecordsCtrl.setVisible(false);
        }

        long firstPage = (long) Math.floor(currentPage / VISIBLE_PAGES_COUNT)
                * VISIBLE_PAGES_COUNT;//todo change it
        long pageCount = model.getPagesCount();
        long totalLastPage = pageCount - 1;
        long proposingListLastPage = firstPage + VISIBLE_PAGES_COUNT;
        long currentListLastPage = (proposingListLastPage > pageCount
                ? pageCount:proposingListLastPage) - 1;

        for(long page = firstPage; page <= currentListLastPage; page++) {
            String label = (page + 1) + "";
            if(page == currentPage) {
                label = "[" + label + "]";
            }
            PagerHyperlink pageLink = new PagerHyperlink();
            pageLink.setText(label);
            pageLink.addStyleName(PagerHyperlink.STYLE_ENABLED);
            pageLink.setTitle("Go to " + (page + 1) + " page");
            final long newPage = page;
            pageLink.addClickListener(new ClickListener() {
                public void onClick(Widget sender) {
                    firePageChanged(newPage);
                }
            });
            pageLink.addStyleName(PAGER_PADDING_RIGHT_ELEMENT_STYLE);
            pagesCtrl.add(pageLink);
        }

        /*if (lastPage != (model.getPagesCount() - 1)) {//todo restore "..." if needed, actually enabling/disabling of < or > shows its status.
            final long nextPage = lastPage + 1;
            PagerHyperlink nextPageSetLink = new PagerHyperlink();
            nextPageSetLink.setText("...");
            nextPageSetLink.addStyleName(PagerHyperlink.STYLE_ENABLED);
            nextPageSetLink.setTitle("Go to " + (nextPage + 1) + " page");
            nextPageSetLink.addClickListener(new ClickListener() {
                public void onClick(Widget sender) {
                    firePageChanged(nextPage);
                }
            });
            nextPageSetLink.addStyleName(PAGER_PADDING_RIGHT_ELEMENT_STYLE);
            pagesCtrl.add(nextPageSetLink);
        }*/

        PagerHyperlink nextPageLink = new PagerHyperlink();
        nextPageLink.setHTML("&gt;");
        nextPageLink.addStyleName(PagerHyperlink.STYLE_ENABLED);
        nextPageLink.setDisabled(currentPage >= totalLastPage);
        nextPageLink.addClickListener(new ClickListener() {
            public void onClick(Widget sender) {
                firePageChanged(currentPage + 1);
            }
        });
        nextPageLink.addStyleName(PAGER_PADDING_RIGHT_ELEMENT_STYLE);
        pagesCtrl.add(nextPageLink);

        PagerHyperlink lastPageLink = new PagerHyperlink();
        lastPageLink.setHTML("&gt;&gt;");
        lastPageLink.setDisabled(currentPage >= totalLastPage);
        lastPageLink.addClickListener(new ClickListener() {
            public void onClick(Widget sender) {
                firePageChanged(model.getPagesCount() - 1);
            }
        });
        lastPageLink.addStyleName(PAGER_PADDING_RIGHT_ELEMENT_STYLE);
        pagesCtrl.add(lastPageLink);
    }

    public void reinitPager() {
        setupLoadedRecords();
        setupPages();
    }

    public void addPagerListener(QPagerListener listener) {
        pagerListeners.add(listener);
    }

    public void removePagerListener(QPagerListener listener) {
        pagerListeners.remove(listener);
    }

    protected void firePageChanged(long newPage) {
        for(int i = 0; i < pagerListeners.size(); i++) {
            QPagerListener listener = (QPagerListener) pagerListeners.get(i);
            listener.onPageChange(newPage);
        }
    }

    protected void firePageSizeChanged(int newPageSize) {
        for(int i = 0; i < pagerListeners.size(); i++) {
            QPagerListener listener = (QPagerListener) pagerListeners.get(i);
            listener.onPageSizeChange(newPageSize);
        }
    }

    protected void fireCounterToggled(boolean newCounterValue) {
        for(int i = 0; i < pagerListeners.size(); i++) {
            QPagerListener listener = (QPagerListener) pagerListeners.get(i);
            listener.onToggleCounter(newCounterValue);
        }
    }

    private void fireCustomized(SubsetData data) {
        for(int i = 0; i < pagerListeners.size(); i++) {
            QPagerListener listener = (QPagerListener) pagerListeners.get(i);
            listener.onCustomize(data);
        }
    }

    private void fireRefreshed() {
        for(int i = 0; i < pagerListeners.size(); i++) {
            QPagerListener listener = (QPagerListener) pagerListeners.get(i);
            listener.onRefresh();
        }
    }

    private void firePrint() {
        for(int i = 0; i < pagerListeners.size(); i++) {
            QPagerListener listener = (QPagerListener) pagerListeners.get(i);
            listener.onPrint();
        }
    }

    private void fireWord() {
        for(int i = 0; i < pagerListeners.size(); i++) {
            QPagerListener listener = (QPagerListener) pagerListeners.get(i);
            listener.onExportToWord();
        }
    }

    private void fireExcel() {
        for(int i = 0; i < pagerListeners.size(); i++) {
            QPagerListener listener = (QPagerListener) pagerListeners.get(i);
            listener.onExportToExcel();
        }
    }

    public void setVerticalAlignment(
            HasVerticalAlignment.VerticalAlignmentConstant align) {
    }

    public void onEvent(Event event, Widget sender) {
        if(sender == customizer) {
            if(event == OkayCancelPopup.Events.OK) {
                SubsetData data = customizer.getSubsetData();
                fireCustomized(data);
            }
        } else if(sender == menu) {
            if(event == Events.SELECT_ALL) {
                selectionModListeners.fireSelectAll();
            } else if(event == Events.DESELECT_ALL) {
                selectionModListeners.fireDeselectAll();
            } else if(event == Events.INVERT_SELECTION) {
                selectionModListeners.fireInvertSelection();
            }
        }
    }

    public void addGridSelectionModListener(GridSelectionModListener listener) {
        selectionModListeners.add(listener);
    }

    public void removeGridSelectionModListener(
            GridSelectionModListener listener) {
        selectionModListeners.remove(listener);
    }

    public void onClick(Widget sender) {
        if(sender == counterCtrl) {
            if(model.isCounterToggledOn() != counterCtrl.isChecked()) {
                fireCounterToggled(counterCtrl.isChecked());
            }
        } else if(sender == counterLabel) {
            counterCtrl.setChecked(!model.isCounterToggledOn());
            fireCounterToggled(counterCtrl.isChecked());
        } else if(sender == refreshButton) {
            fireRefreshed();
        } else if(sender == printButton) {
            firePrint();
        } else if(sender == wordButton) {
            fireWord();
        } else if(sender == excelButton) {
            fireExcel();
        }
    }

    public void onChange(Widget sender) {
        if(sender == pageSizeCtrl) {
            int pageSize = model.getPageSize();
            try {
                int newPageSize = Integer.parseInt(pageSizeCtrl.getText());
                if(newPageSize < 1) {
                    throw new NumberFormatException();
                } else {
                    pageSize = newPageSize;
                    firePageSizeChanged(pageSize);
                }
            } catch (NumberFormatException e) {
                pageSizeCtrl.setText(String.valueOf(pageSize));
                DialogHelper.showModalMessageDialog(POSITIVE_INT_ERROR_MESSAGE);
            }
        }
    }
}
