package org.eclipse.ajdt.internal.ui.refactoring;

import java.util.Iterator;
import java.util.List;

import org.aspectj.asm.IProgramElement;
import org.eclipse.ajdt.core.javaelements.DeclareElement;
import org.eclipse.ajdt.core.javaelements.IAspectJElement;
import org.eclipse.ajdt.core.javaelements.IntertypeElement;
import org.eclipse.ajdt.core.model.AJProjectModelFacade;
import org.eclipse.ajdt.core.model.AJProjectModelFactory;
import org.eclipse.ajdt.core.model.AJRelationshipManager;
import org.eclipse.ajdt.internal.ui.resources.AJDTIcon;
import org.eclipse.ajdt.internal.ui.resources.AspectJImages;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.ui.util.SWTUtil;
import org.eclipse.jdt.internal.ui.util.TableLayoutComposite;
import org.eclipse.jface.viewers.CellLabelProvider;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.ltk.ui.refactoring.UserInputWizardPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;

public class PushInRefactoringInputPage extends UserInputWizardPage {
    class SortListener implements Listener {
        
        private final TableViewer tableViewer;
        private final Table table;

        public SortListener(TableViewer tableViewer) {
            this.tableViewer = tableViewer;
            this.table = tableViewer.getTable();
        }
        
        public void handleEvent(Event e) {
            // determine new sort column and direction

            TableColumn sortColumn = table.getSortColumn();
            TableColumn currentColumn = (TableColumn) e.widget;
            int dir = table.getSortDirection();

            if (sortColumn == currentColumn) {
                dir = dir == SWT.UP ? SWT.DOWN : SWT.UP;
            } else {
                table.setSortColumn(currentColumn);
                dir = SWT.UP;
            }

            // sort the data based on column and direction
            String sortIdentifier = null;
            if (currentColumn == iconColumn.getColumn()) {
                sortIdentifier = Sorter.ICON_SORT;
            }
            if (currentColumn == aspectColumn.getColumn()) {
                sortIdentifier = Sorter.ASPECT_SORT;
            }
            if (currentColumn == targetColumn.getColumn()) {
                sortIdentifier = Sorter.ITD_SORT;
            }
            if (currentColumn == itdColumn.getColumn()) {
                sortIdentifier = Sorter.TARGET_SORT;
            }

            tableViewer.setSorter(new Sorter(sortIdentifier,dir));
            table.setSortDirection(dir);
        }
    }
    
    class Sorter extends ViewerSorter {
        final static String ICON_SORT = "icon.sort";
        final static String ASPECT_SORT = "aspect.sort";
        final static String ITD_SORT = "itd.sort";
        final static String TARGET_SORT = "target.sort";
        
        private final int dir;
        private final String column;
        
        Sorter(String column, int dir) {
            super();
            this.column = column;
            this.dir = dir;
        }

        public int compare(Viewer viewer, Object e1, Object e2) {
            if (column == ASPECT_SORT) {
                if (e1 instanceof IJavaElement) {
                    if (! (e1 instanceof IType)) {
                        e1 = ((IJavaElement) e1).getParent();
                    }
                    e1 = ((IJavaElement) e1).getElementName();
                }
                if (e2 instanceof IJavaElement) {
                    if (e2 instanceof IType) {
                        e2 = ((IJavaElement) e2).getParent();
                    }
                    e2 = ((IJavaElement) e2).getElementName();
                }
            } else if (column == ITD_SORT || column == TARGET_SORT) {
                // This isn't quite right because what shows up in 
                // the column isn't what gets sorted on.
                if (e1 instanceof IJavaElement) {
                    e1 = ((IJavaElement) e1).getElementName();
                }
                if (e2 instanceof IJavaElement) {
                    e2 = ((IJavaElement) e2).getElementName();
                }
            }
            
            
            
            if (dir == SWT.DOWN) {
                return e1.toString().compareTo(e2.toString());
            } else {
                return e2.toString().compareTo(e1.toString());
            }
        }
    }
    
    private Listener sortListener;

    TableViewerColumn iconColumn;
    TableViewerColumn aspectColumn;
    TableViewerColumn targetColumn;
    TableViewerColumn itdColumn;
    
    private PushInRefactoringDescriptor descriptor;
    
	public PushInRefactoringInputPage(String name, PushInRefactoringDescriptor descriptor) {
		super(name);
		this.descriptor = descriptor;
	}

	public void createControl(Composite parent) {
		Composite result= new Composite(parent, SWT.NONE);

		setControl(result);

		GridLayout layout= new GridLayout(2, false);
		result.setLayout(layout);

        Label label = new Label(result, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
        label.setText("The following intertype declarations will be pushed into their target types:");
        GridData gridData= new GridData(GridData.FILL_HORIZONTAL);
        gridData.horizontalSpan= 2;
        gridData.verticalIndent= 5;
        label.setLayoutData(gridData);

		createTable(result);
		
		label = new Label(result, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
        label.setText("To change the set of intertype declarations to be pushed in, click cancel and reselect only the desired AspectJ elements.");
        label.setLayoutData(gridData);
       
        handleInputChanged();
	}



	private PushInRefactoring getPushInRefactoring() {
		return (PushInRefactoring) getRefactoring();
	}

	private void handleInputChanged() {
		RefactoringStatus status= new RefactoringStatus();
		PushInRefactoring refactoring= getPushInRefactoring();
		try {
            status.merge(refactoring.checkInitialConditions(new NullProgressMonitor()));
        } catch (OperationCanceledException e) {
        } catch (CoreException e) {
            status.merge(RefactoringStatus.createFatalErrorStatus(e.getMessage()));
        }
		setPageComplete(!status.hasError());
		int severity= status.getSeverity();
		String message= status.getMessageMatchingSeverity(severity);
		if (severity >= RefactoringStatus.INFO) {
			setMessage(message, severity);
		} else {
			setMessage("", NONE); //$NON-NLS-1$
		}
	}

	/*
	 * from @link ExtractClassUserInputWizardPage
	 */
    private void createTable(Composite parent) {
        GridData gridData;

        TableLayoutComposite layoutComposite= new TableLayoutComposite(parent, SWT.NONE);
        layoutComposite.addColumnData(new ColumnWeightData(5, convertWidthInCharsToPixels(3), true));
        layoutComposite.addColumnData(new ColumnWeightData(40, convertWidthInCharsToPixels(20), true));
        layoutComposite.addColumnData(new ColumnWeightData(40, convertWidthInCharsToPixels(20), true));
        layoutComposite.addColumnData(new ColumnWeightData(40, convertWidthInCharsToPixels(20), true));
        
        // use this instead if we want to implement check boxes
//        final CheckboxTableViewer tv = CheckboxTableViewer.newCheckList(layoutComposite, SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER | SWT.FULL_SELECTION | SWT.MULTI);
        final TableViewer tv = new TableViewer(layoutComposite, SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER | SWT.FULL_SELECTION | SWT.MULTI);
        tv.setContentProvider(new FieldContentProvider());
        createColumns(tv);

        Table table= tv.getTable();
        table.setLinesVisible(true);
        table.setHeaderVisible(true);
        gridData= new GridData(GridData.FILL_BOTH);
        table.setLayoutData(gridData);
        tv.setInput(descriptor);
        final Object[] fields= descriptor.getITDs();
        // only if using checkbox viewer
//        for (int i= 0; i < fields.length; i++) {
//            IAspectJElement field= (IAspectJElement) fields[i];
//            tv.setChecked(field, true);
//        }
        tv.refresh(true);
        gridData= new GridData(GridData.FILL_BOTH);
        gridData.heightHint= SWTUtil.getTableHeightHint(table, Math.max(fields.length,5));
        layoutComposite.setLayoutData(gridData);
        
        sortListener = new SortListener(tv);
        iconColumn.getColumn().addListener(SWT.Selection, sortListener);
        aspectColumn.getColumn().addListener(SWT.Selection, sortListener);
        targetColumn.getColumn().addListener(SWT.Selection, sortListener);
        itdColumn.getColumn().addListener(SWT.Selection, sortListener);
    }
    
    private void createColumns(final TableViewer tv) {

        // four columns: icon, Aspect name, ITD name, target type name(s)
        
        
        iconColumn = new TableViewerColumn(tv, SWT.LEAD);
        iconColumn.setLabelProvider(new CellLabelProvider() {
            public void update(ViewerCell cell) {
                Object elt = cell.getElement();
                if (elt instanceof IntertypeElement || elt instanceof DeclareElement) {
                    IAspectJElement ajElem = (IAspectJElement) elt;
                    AJDTIcon icon;
                    try {
                        icon = (AJDTIcon) AspectJImages.instance()
                                .getStructureIcon(ajElem.getAJKind(), ajElem.getAJAccessibility());
                        cell.setImage(icon.getImageDescriptor().createImage());
                    } catch (JavaModelException e) {
                    }
                }
            }
        });

        
        aspectColumn = new TableViewerColumn(tv, SWT.LEAD);
        aspectColumn.setLabelProvider(new CellLabelProvider() {
            public void update(ViewerCell cell) {
                Object elt = cell.getElement();
                if (elt instanceof IntertypeElement || elt instanceof DeclareElement) {
                    IAspectJElement itd = (IAspectJElement) elt;
                    cell.setText(itd.getParent().getElementName());
                }
            }
        });
        TableColumn column= aspectColumn.getColumn();
        column.setText("Declaring aspect");
        
        itdColumn = new TableViewerColumn(tv, SWT.LEAD);
        itdColumn.setLabelProvider(new CellLabelProvider() {
            public void update(ViewerCell cell) {
                Object elt = cell.getElement();
                if (elt instanceof IntertypeElement) {
                    IAspectJElement itd = (IAspectJElement) elt;
                    cell.setText(itd.getElementName());
                } else if (elt instanceof DeclareElement) {
                    DeclareElement de = (DeclareElement) elt;
                    IProgramElement ipe = AJProjectModelFactory.getInstance().getModelForJavaElement(de).javaElementToProgramElement(de);
                    String details = ipe.getDetails();
                    details = ipe.getDetails();
                    if (ipe.getKind().isDeclareAnnotation()) {
                        // don't want fully qualified names in window
                        String annotationName = details;
                        String[] split = details.split(":");
                        if (split.length == 2) {
                            int secondPart = Math.max(split[1].lastIndexOf('.')+1, 1);
                            split[1] = "@" + split[1].substring(secondPart).trim();
                            annotationName = split[1];
                        }
                        cell.setText(de.getElementName() + " " + annotationName);
                    } else {
                        // declare parents
                        List parents = ipe.getParentTypes();
                        StringBuffer sb = new StringBuffer();
                        for (Iterator parentIter = parents.iterator(); parentIter
                                .hasNext();) {
                            String parent = (String) parentIter.next();
                            String[] splits = parent.split("\\.");
                            parent = splits[splits.length-1];
                            sb.append(parent);
                            if (parentIter.hasNext()) {
                                sb.append(", ");
                            }
                        }
                        cell.setText(sb.toString());
                    }
                }
            }
        });
        column = itdColumn.getColumn();
        column.setText("Intertype Name");
        
        targetColumn = new TableViewerColumn(tv, SWT.LEAD);
        targetColumn.setLabelProvider(new CellLabelProvider() {
            public void update(ViewerCell cell) {
                Object elt = cell.getElement();
                if (elt instanceof IntertypeElement) {
                    IntertypeElement itd = (IntertypeElement) elt;
                    try {
                        cell.setText(new String(itd.getTargetType()));
                    } catch (JavaModelException e) {
                    }
                } else if (elt instanceof DeclareElement) {
                    DeclareElement de = (DeclareElement) elt;
                    AJProjectModelFacade model = AJProjectModelFactory.getInstance().getModelForJavaElement(de); 
                    IProgramElement ipe = model.javaElementToProgramElement(de);
                    String targetName;
                    if (ipe.getKind().isDeclareAnnotation()) {
                        String details = ipe.getDetails();
                        targetName = details;
                        String[] split = details.split(":");
                        if (split.length == 2) {
                            int firstPart = Math.max(split[0].lastIndexOf('.')+1, 0);
                            split[0] = split[0].substring(firstPart).trim();
                            targetName = split[0];
                        }
                    } else {
                        List/*IJavaElement*/ elts = model.getRelationshipsForElement(de, AJRelationshipManager.DECLARED_ON);
                        StringBuffer sb = new StringBuffer();
                        for (Iterator eltIter = elts.iterator(); eltIter
                                .hasNext();) {
                            IJavaElement target = (IJavaElement) eltIter.next();
                            sb.append(target.getElementName());
                            if (eltIter.hasNext()) {
                                sb.append(", ");
                            }
                        }
                        targetName = sb.toString();
                    }
                    cell.setText(targetName);
                }
            }
        });
        column = targetColumn.getColumn();
        column.setText("Target type");
    }

    /*
     * from @link ExtractClassUserInputWizardPage
     */
    public class FieldContentProvider implements IStructuredContentProvider {

        public void dispose() {
        }

        public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
        }

        public Object[] getElements(Object inputElement) {
            if (inputElement instanceof PushInRefactoringDescriptor) {
                PushInRefactoringDescriptor descriptor= (PushInRefactoringDescriptor) inputElement;
                return descriptor.getITDs();
            }
            return null;
        }

    }

    public void dispose() {
        super.dispose();
        iconColumn.getColumn().removeListener(SWT.Selection, sortListener);
        aspectColumn.getColumn().removeListener(SWT.Selection, sortListener);
        targetColumn.getColumn().removeListener(SWT.Selection, sortListener);
        itdColumn.getColumn().removeListener(SWT.Selection, sortListener);
    }
}