/**
 *
 */

package org.richfaces.component;

import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.FacesEvent;
import javax.faces.event.PhaseId;

import org.ajax4jsf.event.AjaxEvent;
import org.ajax4jsf.model.ExtendedDataModel;
import org.richfaces.event.ChangeColumnVisibilityEvent;
import org.richfaces.event.ColumnResizeEvent;
import org.richfaces.event.DragDropEvent;
import org.richfaces.model.ExtendedDataTableModifiableModel;
import org.richfaces.model.ExtendedTableDataModel;
import org.richfaces.model.FilterField;
import org.richfaces.model.ModifiableModel;
import org.richfaces.model.SortField2;

/**
 * JSF component class
 * 
 */
public abstract class UIExtendedDataTable extends UIDataTable implements
        Selectable {
    
    /**
     * COMPONENT_TYPE
     */
    public static final String COMPONENT_TYPE = "org.richfaces.ExtendedDataTable";
    
    /**
     * COMPONENT_FAMILY
     */
    public static final String COMPONENT_FAMILY = "org.richfaces.ExtendedDataTable";

    protected UIExtendedDataTableState state;

    public abstract Object getActiveRowKey();

    public abstract void setActiveRowKey(Object activeRowKey);

    public void broadcast(FacesEvent event) throws AbortProcessingException {
        super.broadcast(event);
        if (event instanceof DragDropEvent) {
            processDradDrop((DragDropEvent) event);
        } else if (event instanceof ChangeColumnVisibilityEvent) {
            processChangeColumnVisibility((ChangeColumnVisibilityEvent) event);
        } else if (event instanceof ColumnResizeEvent) {
            processColumnResize((ColumnResizeEvent) event);
        }

    }

    public void queueEvent(FacesEvent event) {
        if (event instanceof DragDropEvent) {
            new AjaxEvent(this).queue();
            event.setPhaseId(PhaseId.APPLY_REQUEST_VALUES);
            // event.setPhaseId(PhaseId.APPLY_REQUEST_VALUES);
            // new AjaxEvent(this).queue();
        } else if (event instanceof ChangeColumnVisibilityEvent) {
            new AjaxEvent(this).queue();
            event.setPhaseId(PhaseId.APPLY_REQUEST_VALUES);
        } else if (event instanceof ColumnResizeEvent) {
            new AjaxEvent(this).queue();
            event.setPhaseId(PhaseId.APPLY_REQUEST_VALUES);
        }
        super.queueEvent(event);
    }

    public Iterator<UIColumn> getSortedColumns() {
        return new SortedColumnsIterator(this);
    }

    public Iterator<UIColumn> getChildColumns() {
        return new ExtendedTableColumnsIterator(this);
    }

    public void processDradDrop(DragDropEvent event) {
        String dragValue = event.getDragValue().toString();// dnd_drag_script
        String dropValue = event.getDropValue().toString();// dnd_drop_script

        ensureTableStateInitialized();
        state.changeColumnsOrder(dragValue, dropValue, event.isDropBefore());
        state.publishChanges(getFacesContext(), this);

        getFacesContext().renderResponse();
    }

    public List<UIComponent> getSortedChildren() {
        ensureTableStateInitialized();
        return state.sortColumns(getFacesContext(), super.getChildren());
    }

    public void ensureTableStateInitialized() {
        if (state == null) {
            state = UIExtendedDataTableState.getUIExtendedDataTableState(this);
        }
    }

    public void processChangeColumnVisibility(ChangeColumnVisibilityEvent event) {
        ensureTableStateInitialized();
        state.toggleColumnVisibility(this, event.getColumnId());
        state.publishChanges(getFacesContext(), this);

        getFacesContext().renderResponse();
    }// processChangeColumnVisibility

    public boolean isColumnVisible(UIComponent column) {
        ensureTableStateInitialized();
        return state.isColumnVisible(column.getId());
    }// isColumnVisible

    public void processColumnResize(ColumnResizeEvent event) {
        ensureTableStateInitialized();
        state.changeColumnSize(this, event.getColumnWidths());
        state.publishChanges(getFacesContext(), this);

        getFacesContext().renderResponse();
    }// processChangeColumnVisibility

    public String getColumnSize(UIComponent column) {
        ensureTableStateInitialized();
        return state.getColumnSize(column);
    }

    public boolean isGroupingOn() {
        ensureTableStateInitialized();
        return state.isGroupingOn();
    }

    public String getGroupingColumnId() {
        ensureTableStateInitialized();
        return state.getGroupingColumnId();
    }

    public UIColumn getGroupingColumn() {
        String groupingColumnId = getGroupingColumnId();
        if (groupingColumnId == null)
            return null;
        for (Iterator<UIColumn> columns = getChildColumns(); columns.hasNext();) {
            UIColumn child = columns.next();
            if (groupingColumnId.equalsIgnoreCase(child.getId())) {
                return child;
            }
        }
        return null;
    }// getGroupingColumn

    public void setGroupingColumn(org.richfaces.component.UIColumn column) {
        ensureTableStateInitialized();
        if (column == null) {
            state.disableGrouping();
        } else {
            state.groupBy(column.getId(), column.getSortOrder());
        }
        state.publishChanges(getFacesContext(), this);
    }

    public void resetGroupVisibilityState() {
        ensureTableStateInitialized();
        state.resetGroupVisibilityState();
    }

    public void disableGrouping() {
        ensureTableStateInitialized();
        state.disableGrouping();
        state.publishChanges(getFacesContext(), this);
    }

    public boolean groupIsExpanded(int index) {
        ensureTableStateInitialized();
        return state.groupIsExpanded(index);
    }

    public void toggleGroup(int index) {
        ensureTableStateInitialized();
        state.toggleGroup(index);
        state.publishChanges(getFacesContext(), this);
    }

    public Collection<Object> getSortPriority() {
        Collection<Object> priority = super.getSortPriority();
        if (isGroupingOn()) {// grouping is on
            String groupColId = getGroupingColumnId();
            // try to add group column id as first
            if (priority.contains(groupColId)) {
                priority.remove(groupColId);
            }
            if (priority instanceof List) {
                ((List<Object>) priority).add(0, groupColId);
            } else {
                priority.add(groupColId);
            }
        }
        return priority;
    }

    public Object saveState(FacesContext context) {
        Object values[] = new Object[2];
        values[0] = super.saveState(context);
        values[1] = state;
        return values;
    }

    public void restoreState(FacesContext context, Object state) {
        Object values[] = (Object[]) state;
        super.restoreState(context, values[0]);
        this.state = (UIExtendedDataTableState) values[1];
    }

    public int getVisibleColumnsCount() {
        int count = 0;
        for (Iterator<UIColumn> iter = getChildColumns(); iter.hasNext();) {
            UIColumn column = iter.next();
            if (column.isRendered())
                count++;
        }// for
        return count;
    }// getVisibleColumnnCount

    @SuppressWarnings("unchecked")
    protected ExtendedDataModel createDataModel() {
        List<FilterField> filterFields = new LinkedList<FilterField>();
        Map<String, SortField2> sortFieldsMap = new LinkedHashMap<String, SortField2>();
        List<UIComponent> list = getChildren();
        for (Iterator<UIComponent> iterator = list.iterator(); iterator
                .hasNext();) {
            UIComponent component = iterator.next();
            if (component instanceof org.richfaces.component.UIColumn) {
                org.richfaces.component.UIColumn column = (org.richfaces.component.UIColumn) component;
                FilterField filterField = column.getFilterField();
                if (filterField != null) {
                    filterFields.add(filterField);
                }
                SortField2 sortField = column.getSortField();
                if (sortField != null) {
                    sortFieldsMap.put(component.getId(), sortField);
                }
            }

        }
        List<SortField2> sortFields = new LinkedList<SortField2>();
        Collection<Object> sortPriority = getSortPriority();
        if (sortPriority != null) {
            for (Object object : sortPriority) {
                if (object instanceof String) {
                    String id = (String) object;
                    SortField2 sortField = sortFieldsMap.get(id);
                    if (sortField != null) {
                        sortFields.add(sortField);
                        sortFieldsMap.remove(id);
                    }
                }
            }
        }
        sortFields.addAll(sortFieldsMap.values());
        setFilterFields(filterFields);
        setSortFields(sortFields);
        ExtendedDataModel dataModel = (ExtendedDataModel) super.getDataModel();
        if (dataModel instanceof ExtendedTableDataModel<?>) {
        	ExtendedTableDataModel<?> tableDataModel = (ExtendedTableDataModel<?>) dataModel;
            return new ExtendedDataTableModifiableModel(tableDataModel, getVar(),
                    getFilterFields(), getSortFields());
        } else {
            ModifiableModel modifiableModel = new ModifiableModel(dataModel, getVar());
            modifiableModel.modify(getFilterFields(), getSortFields());
            
            return modifiableModel;
        }
    }
}
