/*
 * Copyright 2000-2010 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 *
 * 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.intellij.refactoring.changeSignature;

import com.intellij.codeInsight.completion.JavaCompletionUtil;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.VariableKind;
import com.intellij.refactoring.ui.JavaCodeFragmentTableCellEditor;
import com.intellij.refactoring.ui.StringTableCellEditor;
import com.intellij.ui.EditorTextField;
import com.intellij.util.ui.ColumnInfo;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import javax.swing.table.TableCellEditor;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.util.LinkedHashSet;
import java.util.Set;

public class JavaParameterTableModel extends ParameterTableModelBase<ParameterInfoImpl> {
  private final Project myProject;

  public JavaParameterTableModel(final PsiElement typeContext,
                                 PsiElement defaultValueContext,
                                 final ChangeSignatureDialogBase dialog) {
    this(typeContext, defaultValueContext,
         new JavaTypeColumn(typeContext.getProject()),
         new JavaNameColumn(typeContext.getProject()),
         new DefaultValueColumn<ParameterInfoImpl>(typeContext.getProject(), StdFileTypes.JAVA) {
           @Override
           public TableCellEditor doCreateEditor(ParameterTableModelItemBase<ParameterInfoImpl> item) {
             return new EditorWithExpectedType(typeContext);
           }
         }, new AnyVarColumn<ParameterInfoImpl>() {
        @Override
        public boolean isCellEditable(ParameterTableModelItemBase<ParameterInfoImpl> item) {
          return !dialog.isGenerateDelegate() && super.isCellEditable(item);
        }
      });
  }

  protected JavaParameterTableModel(PsiElement typeContext, PsiElement defaultValueContext, ColumnInfo... columns) {
    super(typeContext, defaultValueContext, columns);
    myProject = typeContext.getProject();
  }

  @Override
  protected ParameterTableModelItemBase<ParameterInfoImpl> createRowItem(@Nullable ParameterInfoImpl parameterInfo) {
    if (parameterInfo == null) {
      parameterInfo = new ParameterInfoImpl(-1);
    }
    PsiElementFactory f = JavaPsiFacade.getInstance(myProject).getElementFactory();
    final PsiTypeCodeFragment paramTypeCodeFragment =
      f.createTypeCodeFragment(parameterInfo.getTypeText(), myTypeContext, false, true, true);
    parameterInfo.getTypeWrapper().addImportsTo(paramTypeCodeFragment);
    PsiExpressionCodeFragment defaultValueCodeFragment =
      f.createExpressionCodeFragment(parameterInfo.getDefaultValue(), myDefaultValueContext, null, true);
    defaultValueCodeFragment.setVisibilityChecker(JavaCodeFragment.VisibilityChecker.EVERYTHING_VISIBLE);

    return new ParameterTableModelItemBase<ParameterInfoImpl>(parameterInfo, paramTypeCodeFragment, defaultValueCodeFragment) {
      @Override
      public boolean isEllipsisType() {
        try {
          return paramTypeCodeFragment.getType() instanceof PsiEllipsisType;
        }
        catch (PsiTypeCodeFragment.TypeSyntaxException e) {
          return false;
        }
        catch (PsiTypeCodeFragment.NoTypeException e) {
          return false;
        }
      }
    };
  }

  @Nullable
  private static PsiType getRowType(JTable table, int row) {
    try {
      return ((PsiTypeCodeFragment)((JavaParameterTableModel)table.getModel()).getItems().get(row).typeCodeFragment).getType();
    }
    catch (PsiTypeCodeFragment.TypeSyntaxException e) {
      return null;
    }
    catch (PsiTypeCodeFragment.NoTypeException e) {
      return null;
    }
  }

  private static class VariableCompletionTableCellEditor extends StringTableCellEditor {
    public VariableCompletionTableCellEditor(Project project) {
      super(project);
    }

    public Component getTableCellEditorComponent(final JTable table,
                                                 Object value,
                                                 boolean isSelected,
                                                 final int row,
                                                 int column) {
      final EditorTextField textField =
        (EditorTextField)super.getTableCellEditorComponent(table, value, isSelected, row, column);
      textField.registerKeyboardAction(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
          PsiType type = getRowType(table, row);
          if (type != null) {
            completeVariable(textField, type);
          }
        }
      }, KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, InputEvent.CTRL_MASK), JComponent.WHEN_IN_FOCUSED_WINDOW);
      return textField;
    }

    private static void completeVariable(EditorTextField editorTextField, PsiType type) {
      Editor editor = editorTextField.getEditor();
      String prefix = editorTextField.getText();
      if (prefix == null) prefix = "";
      Set<LookupElement> set = new LinkedHashSet<LookupElement>();
      JavaCompletionUtil.completeVariableNameForRefactoring(editorTextField.getProject(), set, prefix, type, VariableKind.PARAMETER);

      LookupElement[] lookupItems = set.toArray(new LookupElement[set.size()]);
      editor.getCaretModel().moveToOffset(prefix.length());
      editor.getSelectionModel().removeSelection();
      LookupManager.getInstance(editorTextField.getProject()).showLookup(editor, lookupItems, prefix);
    }
  }

  private static class EditorWithExpectedType extends JavaCodeFragmentTableCellEditor {
    public EditorWithExpectedType(PsiElement typeContext) {
      super(typeContext.getProject());
    }

    public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
      final Component editor = super.getTableCellEditorComponent(table, value, isSelected, row, column);
      final PsiType type = getRowType(table, row);
      if (type != null) {
        ((PsiExpressionCodeFragment)myCodeFragment).setExpectedType(type);
      }
      return editor;
    }
  }

  public static class JavaTypeColumn extends TypeColumn<ParameterInfoImpl> {
    private final Project myProject;

    public JavaTypeColumn(Project project) {
      super(project, StdFileTypes.JAVA);
      myProject = project;
    }

    @Override
    public TableCellEditor doCreateEditor(ParameterTableModelItemBase<ParameterInfoImpl> o) {
      return new JavaCodeFragmentTableCellEditor(myProject);
    }
  }

  public static class JavaNameColumn extends NameColumn<ParameterInfoImpl> {
    private final Project myProject;

    public JavaNameColumn(Project project) {
      super(project);
      myProject = project;
    }

    @Override
    public TableCellEditor doCreateEditor(ParameterTableModelItemBase<ParameterInfoImpl> o) {
      return new VariableCompletionTableCellEditor(myProject);
    }
  }
}

@@ -1149,32 +1149,85 @@
 ableCellEditor;%0A
+import com.intellij.refactoring.util.CanonicalTypes;%0A
 import com.intel
@@ -3332,38 +3332,122 @@
     
-parameterInfo.getTypeWrapper()
+final CanonicalTypes.Type paramType = parameterInfo.getTypeWrapper();%0A    if (paramType != null) %7B%0A      paramType
 .add
@@ -3476,24 +3476,30 @@
 eFragment);%0A
+    %7D%0A
     PsiExpre

fix NPE