package bndtools.editor.common;

import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.TraverseEvent;
import org.eclipse.swt.events.TraverseListener;
import org.eclipse.swt.widgets.Table;

/**
 * Well ... the JFace <CODE>TableViewer<CODE> class does not provide
 * text celleditor keyboard support like if TAB is pressed in a column,
 * the focus does not go into the next column etc.,
 * 
 * After observing JDT method signature refactoring widget which does the
 * desired keyboard support, a generic utility listener has been developed
 * on the same lines.
 * 
 * Usage goes as ...
 * 
 * <P><PRE><CODE>
 * 
 * 		TableViewer tblViewer = new TableViewer(SWT.SINGLE | SWT.FULL_SELECTION | SWT.| SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER)
 * 		...
 * 		CellEditor[] cellEditors = ...
 * 		...
 * 		tblViewer.setCellEditors(cellEditors);
 * 		
 * 		TableViewerKeyBoardSupporter supporter = new TableViewerKeyBoardSupporter(tblViewer);
 * 		supporter.startSupport();
 *  
 * </CODE>
 * </PRE>
 * </P>
 */
public class TableViewerKeyBoardSupporter {
    protected TableViewer fTableViewer = null;

    public TableViewerKeyBoardSupporter(TableViewer tableViewer) {
        fTableViewer = tableViewer;
    }

    /**
     * After the cell editors have been set on tableviewer, this method should be called to start giving keyboard
     * support.
     */
    public void startSupport() {
        /* add table key listener */
        getTable().addKeyListener(new TableKeyListener(this));

        /* add table traverse listener */
        getTable().addTraverseListener(new TableTraverseListener(this));

        /* add table-textcelleditors key and traverse listeners */
        CellEditor[] cellEditors = fTableViewer.getCellEditors();
        if (cellEditors != null) {
            for (int colIndex = 0; colIndex < cellEditors.length; colIndex++) {
                CellEditor cellEditor = cellEditors[colIndex];
                if (cellEditor != null) {
                    // cellEditor.getControl().addKeyListener(new
                    // CellEditorKeyListener(this, cellEditor, colIndex));
                    cellEditor.getControl()
                        .addTraverseListener(new CellEditorTraverseListener(this, cellEditor, colIndex));
                }
            }
        }
    }

    protected TableViewer getTableViewer() {
        return fTableViewer;
    }

    protected Table getTable() {
        return fTableViewer.getTable();
    }

    protected int nextColumn(int column) {
        return (column >= getTable().getColumnCount() - 1) ? 0 : column + 1;
    }

    protected int prevColumn(int column) {
        return (column <= 0) ? getTable().getColumnCount() - 1 : column - 1;
    }

    protected void editColumnOrNextPossible(final int column) {
        Object selectedElem = getSelectedElement();
        if (selectedElem == null)
            return;

        int nextColumn = column;
        do {
            fTableViewer.editElement(selectedElem, nextColumn);
            if (fTableViewer.isCellEditorActive())
                return;
            nextColumn = nextColumn(nextColumn);
        } while (nextColumn != column);
    }

    protected void editColumnOrPrevPossible(int column) {
        Object selectedElem = getSelectedElement();
        if (selectedElem == null)
            return;

        int prevColumn = column;
        do {
            fTableViewer.editElement(selectedElem, prevColumn);
            if (fTableViewer.isCellEditorActive())
                return;
            prevColumn = prevColumn(prevColumn);
        } while (prevColumn != column);
    }

    private Object getSelectedElement() {
        IStructuredSelection selection = (IStructuredSelection) fTableViewer.getSelection();
        if (selection == null || selection.isEmpty())
            return null;

        return selection.getFirstElement();
    }
}

class CellEditorKeyListener extends KeyAdapter {
    private TableViewerKeyBoardSupporter fKeyBoardSupporter = null;
    private CellEditor fEditor = null;
    private int fEditorColumn = -1;

    public CellEditorKeyListener(TableViewerKeyBoardSupporter keyBoardSupporter, CellEditor editor, int editorColumn) {
        fKeyBoardSupporter = keyBoardSupporter;
        fEditor = editor;
        fEditorColumn = editorColumn;
    }

    @Override
    public void keyPressed(KeyEvent e) {
        if (e.stateMask == SWT.MOD1 || e.stateMask == SWT.MOD2) {
            if (e.keyCode == SWT.ARROW_UP || e.keyCode == SWT.ARROW_DOWN) {
                // allow starting multi-selection even if in edit mode
                fEditor.deactivate();
                e.doit = false;
                return;
            }
        }

        if (e.stateMask != SWT.NONE)
            return;

        switch (e.keyCode) {
            case SWT.ARROW_DOWN :
                e.doit = false;
                int nextRow = fKeyBoardSupporter.getTable()
                    .getSelectionIndex() + 1;
                if (nextRow >= fKeyBoardSupporter.getTable()
                    .getItemCount())
                    break;
                fKeyBoardSupporter.getTable()
                    .setSelection(nextRow);
                fKeyBoardSupporter.editColumnOrPrevPossible(fEditorColumn);
                break;

            case SWT.ARROW_UP :
                e.doit = false;
                int prevRow = fKeyBoardSupporter.getTable()
                    .getSelectionIndex() - 1;
                if (prevRow < 0)
                    break;
                fKeyBoardSupporter.getTable()
                    .setSelection(prevRow);
                fKeyBoardSupporter.editColumnOrPrevPossible(fEditorColumn);
                break;

            case SWT.F2 :
                e.doit = false;
                fEditor.deactivate();
                break;

            default :
                break;
        }
    }
}

class CellEditorTraverseListener implements TraverseListener {
    private TableViewerKeyBoardSupporter fKeyBoardSupporter = null;
    private CellEditor fEditor = null;
    private int fEditorColumn = -1;

    public CellEditorTraverseListener(TableViewerKeyBoardSupporter keyBoardSupporter, CellEditor editor, int editorColumn) {
        fKeyBoardSupporter = keyBoardSupporter;
        fEditor = editor;
        fEditorColumn = editorColumn;
    }

    /*
     * (non-Javadoc)
     * @see org.eclipse.swt.events.TraverseListener#keyTraversed(org.eclipse.swt. events.TraverseEvent)
     */
    @Override
    public void keyTraversed(TraverseEvent e) {

        switch (e.detail) {
            case SWT.TRAVERSE_TAB_NEXT :
                fKeyBoardSupporter.editColumnOrNextPossible(fKeyBoardSupporter.nextColumn(fEditorColumn));
                e.detail = SWT.TRAVERSE_NONE;
                break;

            case SWT.TRAVERSE_TAB_PREVIOUS :
                fKeyBoardSupporter.editColumnOrPrevPossible(fKeyBoardSupporter.prevColumn(fEditorColumn));
                e.detail = SWT.TRAVERSE_NONE;
                break;

            case SWT.TRAVERSE_ESCAPE :
                fKeyBoardSupporter.getTableViewer()
                    .cancelEditing();
                e.detail = SWT.TRAVERSE_NONE;
                break;

            case SWT.TRAVERSE_RETURN :
                fEditor.deactivate();
                e.detail = SWT.TRAVERSE_NONE;
                break;

            default :
                break;
        }
    }
}

class TableTraverseListener implements TraverseListener {
    private TableViewerKeyBoardSupporter fKeyBoardSupporter = null;

    public TableTraverseListener(TableViewerKeyBoardSupporter keyBoardSupporter) {
        fKeyBoardSupporter = keyBoardSupporter;
    }

    /*
     * (non-Javadoc)
     * @see org.eclipse.swt.events.TraverseListener#keyTraversed(org.eclipse.swt. events.TraverseEvent)
     */
    @Override
    public void keyTraversed(TraverseEvent e) {
        if (e.detail == SWT.TRAVERSE_RETURN && e.stateMask == SWT.NONE) {
            fKeyBoardSupporter.editColumnOrNextPossible(0);
            e.detail = SWT.TRAVERSE_NONE;
        }
    }
}

class TableKeyListener extends KeyAdapter {
    private TableViewerKeyBoardSupporter fKeyBoardSupporter = null;

    public TableKeyListener(TableViewerKeyBoardSupporter keyBoardSupporter) {
        fKeyBoardSupporter = keyBoardSupporter;
    }

    /*
     * (non-Javadoc)
     * @see org.eclipse.swt.events.KeyListener#keyPressed(org.eclipse.swt.events. KeyEvent)
     */
    @Override
    public void keyPressed(KeyEvent e) {
        if (e.keyCode == SWT.F2 && e.stateMask == SWT.NONE) {
            fKeyBoardSupporter.editColumnOrNextPossible(0);
            e.doit = false;
        }
    }
}