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

import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HTMLTable;
import com.google.gwt.user.client.ui.KeyboardListener;
import com.google.gwt.user.client.ui.SourcesTableEvents;
import com.google.gwt.user.client.ui.TableListener;
import com.google.gwt.user.client.ui.Widget;
import com.queplix.core.client.common.StringUtil;

import java.util.ArrayList;
import java.util.List;


/**
 * Internal Grid of DataGrid. Displays actual data.
 * This class doesn't generate
 * {@link com.queplix.core.client.common.ui.grid.BaseInternalDataGrid.Events}
 * at all
 *
 * @author Vasily Mikhailitchenko
 * @author Sergey Kozmin
 * @since 19 Dec 2006
 */
class InternalDataGrid extends BaseInternalDataGrid implements TableListener {
    private static final int EMPTY_MODEL_ROWS_COUNT = 1;
    private static final int EMPTY_MODEL_COLUMNS_COUNT = 1;

    private static final CellDataProvider EMPTY_TEXT_CELL
            = new EmptyTextCellDataProvider();

    private boolean isRowsFake = false;
    private boolean isRowsAndCoulumnsFake = false;

    private HeaderGrid header;
    private GridElementsStrategy strategy;

    protected GridView view;

    private ArrayList gridListeners;
    private int modifier = 0;

    protected InternalDataGrid(GridView view, GridElementsStrategy strategy,
                               ArrayList gridListeners) {
        this.strategy = strategy;
        this.view = view;
        this.gridListeners = gridListeners;

        sinkEvents(Event.MOUSEEVENTS);
        sinkEvents(Event.KEYEVENTS);
        addTableListener(this);
    }

    public void setupWidgets(GridModel model) {
        int columnCount = model.getModelColumnCount();
        int rowCount = model.getModelRowCount();
        boolean toBeUpdated = false;
        if(rowCount <= 0 && columnCount > 0) {
            if(!isRowsFake) {
                isRowsFake = true;
            }
            resize(EMPTY_MODEL_ROWS_COUNT, columnCount);
            fillWithEmptyValues(EMPTY_MODEL_ROWS_COUNT, columnCount);
            toBeUpdated = true;
        } else if(rowCount <= 0 || columnCount <= 0) {
            if(!isRowsAndCoulumnsFake) {
                isRowsAndCoulumnsFake = true;
                resize(EMPTY_MODEL_ROWS_COUNT, EMPTY_MODEL_COLUMNS_COUNT);
                fillWithEmptyValues(EMPTY_MODEL_ROWS_COUNT,
                        EMPTY_MODEL_COLUMNS_COUNT);
                toBeUpdated = true;
            }
        } else {
            isRowsFake = false;
            isRowsAndCoulumnsFake = false;
            resize(rowCount, columnCount);
            setupRows(model);
            resizeColumns(model);
            toBeUpdated = true;
        }
        if(toBeUpdated) {
            int actualWidth = 0;
            for(int i = 0; i < columnCount; i++) {
                actualWidth += header.getHeaderCellWidth(i);
            }
            if(actualWidth > 0) {
                String width = StringUtil.pixelToSize(actualWidth);
                header.setWidth(width);
                setWidth(width);
            }
            tableInitialized(this);
        }
        resizeColumnsLikeHeader(model);
        header.setWidth(StringUtil.pixelToSize(getOffsetWidth()));
    }

    private void resizeColumns(GridModel model) {
        for(int i = 0, n = model.getModelColumnCount(); i < n; i++) {
            if(model.getDataGridMeta().getColumn(i).getWidth() == DataGridColumn
                    .SIZE_UNSPECIFIED) {
                header.autoresizeParticularColumn(i, false);
            }
        }
    }

    public void resizeColumnsLikeHeader(GridModel model) {
        for(int column = 0; column < model.getModelColumnCount(); column++) {
            resizeColumn(column, header.getHeaderCellWidth(column), 0);
        }
    }

    private void fillWithEmptyValues(int emptyModelRowsCount, int columnCount) {
        String rowStyleName = view.getRowStyleName();

        for(int row = 0; row < emptyModelRowsCount; row++) {
            for(int column = 0; column < columnCount; column++) {
                setCellValue(this, row, column, EMPTY_TEXT_CELL);
            }
            // Apply css style to the current row
            getRowFormatter().setStyleName(row, rowStyleName);
        }
    }

    public void resizeColumn(int column, int width, int delta) {
        if(column < numColumns && column >= 0) {
            setAllRowsColumnWidth(this, column, width, delta);
        }
    }

    /**
     * External method, can be used to know, that data is updated by third party.
     *
     * @param model      to get data from
     * @param changedRow changed row index
     */
    public void setupRow(GridModel model, int changedRow) {
        setRow(model, changedRow);
    }

    /**
     * Method that is used by internal components
     *
     * @param model      model to get data from
     * @param changedRow changed row index
     */
    private void setRow(GridModel model, int changedRow) {
        String rowStyleName = view.getRowStyleName();

        RowDataProvider row = model.getGridData().getRow(changedRow);
        for(int modelColumn = 0; modelColumn < row.getColumnsCount();
            modelColumn++) {
            setCellValue(this, changedRow, modelColumn, row.getCell(
                    modelColumn));
        }
        getRowFormatter().setStyleName(changedRow, rowStyleName);
    }

    public void setupRows(GridModel model) {
        GridDataProvider dataProvider = model.getGridData();
        for(int modelRow = 0; modelRow < dataProvider.getRecordsCount();
            modelRow++) {
            setRow(model, modelRow);
        }
    }

    public void onCellClicked(SourcesTableEvents sender, int row, int column) {
        if(!isRowsFake && !isRowsAndCoulumnsFake) {
            onCellClicked(row, column, modifier);
        }
    }

    protected void onCellClicked(int row, int column, int modifier) {
        for(int i = 0; i < gridListeners.size(); i++) {
            ((GridSelectionListener) gridListeners.get(i)).gridCellSelected(row,
                    column,
                    modifier);
        }
    }

    public void onKeyDown(char keyCode, int keyboardModifiers) {
        for(int i = 0; i < gridListeners.size(); i++) {
            ((GridSelectionListener) gridListeners.get(i)).onKeyDown(keyCode,
                    keyboardModifiers);
        }
    }

    protected void onMousePositionChanged(int row, boolean in) {
        for(int i = 0; i < gridListeners.size(); i++) {
            ((GridSelectionListener) gridListeners.get(i)).mousePositionChanged(
                    row, in);
        }
    }

    public void onBrowserEvent(Event event) {
        switch(DOM.eventGetType(event)) {
            case Event.ONMOUSEMOVE: {
                header.performColumnResize(DOM.eventGetClientX(event) - DOM
                        .getAbsoluteLeft(getElement()));
                break;
            }
            case Event.ONMOUSEUP: {
                header.handleMouseUpEvent(DOM.eventGetClientX(event) - DOM
                        .getAbsoluteLeft(getElement()));
                break;
            }
            case Event.ONMOUSEOVER: {
                Element td = getEventTargetCell(event);
                if(td != null) {
                    Element tr = DOM.getParent(td);
                    int row = DOM.getChildIndex(getBodyElement(), tr);
                    onMousePositionChanged(row, true);
                }
                break;
            }
            case Event.ONMOUSEOUT: {
                Element td = getEventTargetCell(event);
                if(td != null) {
                    Element tr = DOM.getParent(td);
                    int row = DOM.getChildIndex(getBodyElement(), tr);
                    onMousePositionChanged(row, false);
                }
                break;
            }
            case Event.ONCLICK: {
                if(DOM.eventGetCtrlKey(event)) {
                    modifier = KeyboardListener.MODIFIER_CTRL;
                } else if(DOM.eventGetShiftKey(event)) {
                    modifier = KeyboardListener.MODIFIER_SHIFT;
                } else {
                    modifier = 0;
                }
                super.onBrowserEvent(event);
                break;
            }
            // Keyboard events are processed by keyboardEventPreview
        }
    }

    public void setHeaderGrid(HeaderGrid header) {
        this.header = header;
    }

    public void setCellValue(Grid grid, int modelRow, int modelColumn,
                             CellDataProvider data) {
        switch(data.getCellType()) {
            case CellDataProvider.TEXT_TYPE: {
                strategy.setCellValue(grid, modelRow, modelColumn,
                        (String) data.getCellInfo());
                break;
            }
            case CellDataProvider.WIDGET_TYPE: {
                strategy.setCellValue(grid, modelRow, modelColumn,
                        (Widget) data.getCellInfo());
                break;
            }
        }
    }

    public void setAllRowsColumnWidth(Grid grid, int column, int sizeInPixels,
                                      int delta) {
        strategy.setAllRowsColumnWidth(grid, column, sizeInPixels, delta);
    }

    public void tableInitialized(Grid grid) {
        strategy.tableInitialized(grid);
    }

    public void setRowType(int row, int selectionType) {
        setRowStyle(row, getRowStyle(selectionType));
    }

    public void setRowsType(int startIndex, int endIndex, int selectionType) {
        String style = getRowStyle(selectionType);
        for(int i = startIndex; i < endIndex; i++) {
            setRowStyle(i, style);
        }
    }

    public void setRowsType(List rows, int selectionType) {
        String style = getRowStyle(selectionType);
        for(int i = 0; i < rows.size(); i++) {
            setRowStyle(((Integer) rows.get(i)).intValue(), style);
        }
    }

    public void clearSelection() {
        HTMLTable.RowFormatter formater = getRowFormatter();
        String style = getRowStyle(NOT_SELECTED_ROW_TYPE);
        for(int i = 0; i < getRowCount(); i++) {
            if(!style.equalsIgnoreCase(formater.getStyleName(i))) {
                formater.setStyleName(i, style);
            }
        }
    }

    public List getSelectedRows() {
        ArrayList selected = new ArrayList();
        HTMLTable.RowFormatter formater = getRowFormatter();
        String multStyle = getRowStyle(MULTIPLE_SELECTED_ROWS_TYPE);
        String singleStyle = getRowStyle(SINGLE_SELECTED_ROW_TYPE);

        for(int i = 0; i < getRowCount(); i++) {
            String curStyle = formater.getStyleName(i);
            if(multStyle.equalsIgnoreCase(curStyle) ||
                    singleStyle.equalsIgnoreCase(curStyle)) {
                selected.add(new Integer(i));
            }
        }
        return selected;
    }

    public List getSelectedRows(int selectionType) {
        ArrayList selected = new ArrayList();
        HTMLTable.RowFormatter formater = getRowFormatter();
        String type = getRowStyle(selectionType);

        for(int i = 0; i < getRowCount(); i++) {
            String curStyle = formater.getStyleName(i);
            if(type.equalsIgnoreCase(curStyle)) {
                selected.add(new Integer(i));
            }
        }
        return selected;
    }

    protected void setRowStyle(int row, String style) {
        getRowFormatter().setStyleName(row, style);
    }

    protected String getRowStyle(int selectionType) {
        String style;
        switch(selectionType) {
            case SINGLE_SELECTED_ROW_TYPE: {
                style = view.getActiveGridRowStyleName();
                break;
            }
            case MULTIPLE_SELECTED_ROWS_TYPE: {
                style = view.getMSGridRowStyleName();
                break;
            }
            /*case CURSOR_SELECTED_ROW_TYPE: {
                style = view.getHighlightedGridRowStyleName();
                break;
            }*/
            case MOUSE_OVER_ROW_TYPE: {
                style = view.getMouseOverRowStyleName();
                break;
            }
            case NOT_SELECTED_ROW_TYPE://do not break here 
            default: {
                style = view.getRowStyleName();
            }
        }
        return style;
    }

    private static class EmptyTextCellDataProvider implements CellDataProvider {
        public int getCellType() {
            return TEXT_TYPE;
        }

        public Object getCellInfo() {
            return "";
        }
    }
}