package bluej.stride.framedjava.frames;

import bluej.Config;
import bluej.debugger.gentype.Reflective;
import bluej.parser.AssistContent.CompletionKind;
import bluej.parser.AssistContent.ParamInfo;
import bluej.stride.framedjava.ast.ASTUtility;
import bluej.stride.framedjava.ast.AccessPermission;
import bluej.stride.framedjava.ast.AccessPermissionFragment;
import bluej.stride.framedjava.ast.JavaFragment.PosInSourceDoc;
import bluej.stride.framedjava.ast.JavadocUnit;
import bluej.stride.framedjava.ast.NameDefSlotFragment;
import bluej.stride.framedjava.ast.ParamFragment;
import bluej.stride.framedjava.elements.ClassElement;
import bluej.stride.framedjava.elements.CodeElement;
import bluej.stride.framedjava.elements.ConstructorElement;
import bluej.stride.framedjava.elements.MethodProtoElement;
import bluej.stride.framedjava.elements.NormalMethodElement;
import bluej.stride.framedjava.slots.ExpressionCompletionCalculator;
import bluej.stride.framedjava.slots.TypeSlot;
import bluej.stride.generic.AssistContentThreadSafe;
import bluej.stride.generic.ExtensionDescription;
import bluej.stride.generic.ExtensionDescription.ExtensionSource;
import bluej.stride.generic.Frame;
import bluej.stride.generic.FrameCanvas;
import bluej.stride.generic.FrameContentRow;
import bluej.stride.generic.FrameCursor;
import bluej.stride.generic.FrameFactory;
import bluej.stride.generic.InteractionManager;
import bluej.stride.operations.CustomFrameOperation;
import bluej.stride.operations.FrameOperation;
import bluej.stride.operations.ToggleBooleanProperty;
import bluej.stride.slots.*;
import bluej.stride.slots.EditableSlot.MenuItemOrder;
import bluej.stride.slots.SuggestionList.SuggestionDetailsWithHTMLDoc;
import bluej.stride.slots.SuggestionList.SuggestionListListener;
import bluej.utility.Utility;
import bluej.utility.javafx.FXPlatformConsumer;
import bluej.utility.javafx.HangingFlowPane;
import bluej.utility.javafx.JavaFXUtil;
import bluej.utility.javafx.SharedTransition;
import javafx.beans.binding.StringExpression;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.TextField;
import threadchecker.OnThread;
import threadchecker.Tag;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

public class NormalMethodFrame extends MethodFrameWithBody<NormalMethodElement> 
   
   public static final String STATIC_NAME = "static";
   
   public static final String TOGGLE_STATIC_METHOD = "toggleStaticMethod";
   
   public static final String FINAL_NAME = "final";
   
   public static final String TOGGLE_FINAL_METHOD = "toggleFinalMethod";

   
   private final SlotLabel staticLabel;
   
   private BooleanProperty staticModifier = new SimpleBooleanProperty(false);
   
   private final SlotLabel finalLabel;
   
   private BooleanProperty finalModifier = new SimpleBooleanProperty(false);
   
   private final WrappableSlotLabel overrideLabel = new WrappableSlotLabel("") {        
      @Override
       
      public void setView(View oldView, View newView, SharedTransition animate)
       {
         if (oldView == View.NORMAL && newView == View.JAVA_PREVIEW)
           {
            fadeOut(animate, true);             
            }
           

         else
           {
            fadeIn(animate);             
            }         
         }     
      };
   @OnThread(Tag.FXPlatform)
   
   private String curOverrideSource = null;
   
   private final TypeSlot returnType;
   
   private final TextSlot<NameDefSlotFragment> methodName;
   
   private NormalMethodElement element;
    
   
   private NormalMethodFrame(InteractionManager editor)
   {
      super(editor);
      setDocumentationPromptText(Config.getString("frame.class.method.doc.prompt"));

       
      methodName = new MethodNameDefTextSlot(editor, this, getHeaderRow(), new MethodOverrideCompletionCalculator(), "method-name-");
       
      methodName.addValueListener(new SlotTraversalChars(
      () -> getHeaderRow().focusRight(methodName),
      SlotTraversalChars.METHOD_NAME.getChars()) {            
         @Override
           
         public void deletePressedAtEnd(HeaderItem slot)
{            
paramsPane.deleteFirstParam();             
            }         
         });
      returnType = new TypeSlot(editor, this, this, getHeaderRow(), TypeSlot.Role.RETURN, "method-return-type-");
      returnType.addClosingChar(' ');
      returnType.markReturnType();
        
       
      paramsPane = new FormalParameters(editor, this, this, getHeaderRow(), "method-param-");
        
      returnType.setSimplePromptText("type");
      methodName.setPromptText("name");

       
      staticLabel = new SlotLabel("static ");
       
      finalLabel = new SlotLabel("final ");

      modifiers.put(STATIC_NAME, staticModifier);
      modifiers.put(FINAL_NAME, finalModifier);

      overrideLabel.addStyleClass("method-override-label");
      overrideLabel.setAlignment(HangingFlowPane.FlowAlignment.RIGHT);
        
      getHeaderRow().bindContentsConcat(FXCollections.<ObservableList<? extends HeaderItem>>observableArrayList(
      FXCollections.observableArrayList(access),
      JavaFXUtil.listBool(staticModifier, staticLabel),
      JavaFXUtil.listBool(finalModifier, finalLabel),
      FXCollections.observableArrayList(returnType),
      FXCollections.observableArrayList(methodName),
      paramsPane.getSlots(),
      throwsPane.getHeaderItems(),
      FXCollections.observableArrayList(overrideLabel)
       
      ));

      JavaFXUtil.addChangeListener(staticModifier, b -> editor.modifiedFrame(this, false));
      JavaFXUtil.addChangeListener(finalModifier, b -> editor.modifiedFrame(this, false));     
      }
    
   
   public NormalMethodFrame(InteractionManager editor, AccessPermissionFragment access, boolean staticModifier,
           
   boolean finalModifier, String returnType, String name, String documentation, boolean enabled)
   {
      this(editor);
      this.access.setValue(access.getValue());
      access.registerSlot(this.access);
      this.staticModifier.set(staticModifier);
      this.finalModifier.set(finalModifier);
      setDocumentation(documentation);
      this.returnType.setText(returnType);
      methodName.setText(name);
      frameEnabledProperty.set(enabled);     
      }

   
   @Override
   
   public boolean focusWhenJustAdded()
   {
      returnType.requestFocus();
       
      return true;     
      }
    
   
   public static FrameFactory getFactory()
   {        
      return new FrameFactory<NormalMethodFrame>() {            
         @Override
           
         public NormalMethodFrame createBlock(InteractionManager editor)
           {                
            return new NormalMethodFrame(editor);             
            }
                        
           
         @Override 
           
         public Class getBlockClass()
           {                 
            return NormalMethodFrame.class;             
            }         
         };
      }

   
   @Override
   
   public void regenerateCode()
   {
      List<ParamFragment> params = generateParams();
        
       
      element = new NormalMethodElement(this, new AccessPermissionFragment(this, access), staticModifier.get(),
      finalModifier.get(), returnType.getSlotElement(), methodName.getSlotElement(), 
               
      params, throwsPane.getTypes(), getContents(), new JavadocUnit(getDocumentation()), frameEnabledProperty.get());     
      }

   
   @Override
   
   public NormalMethodElement getCode()
   {        
      return element;     
      }

   
   public String getName()
   {
      return methodName.getText();     
      }
    
   
   private class MethodOverrideCompletionCalculator
implements CompletionCalculator
   {        
      private List<AssistContentThreadSafe> inheritedMethods;
       
      private SuggestionList suggestionDisplay;

       
      @Override
      @OnThread(Tag.FXPlatform)
       
      public void withCalculatedSuggestionList(PosInSourceDoc pos, CodeElement codeEl,
                                                
      SuggestionListListener listener, FXPlatformConsumer<SuggestionList> handler) {             
         ClassFrame classFrame = (ClassFrame)ASTUtility.getTopLevelElement(codeEl).getFrame();
            
         classFrame.withInheritedItems(Collections.singleton(CompletionKind.METHOD), inheritedMethodsByDeclarer ->
           {                 
            inheritedMethods = inheritedMethodsByDeclarer.values().stream().flatMap(List::stream).collect(Collectors.toList());
                
               
            
            
                
               
            suggestionDisplay = new SuggestionList(getEditor(),
                       
            Utility.mapList(inheritedMethods, ac -> new SuggestionDetailsWithHTMLDoc(
            ac.getName(), 
            ExpressionCompletionCalculator.getParamsCompletionDisplay(ac),
            ac.getType(),
                               
            SuggestionList.SuggestionShown.COMMON,
            ac.getDocHTML())),
                       
            null, SuggestionList.SuggestionShown.RARE, null, listener);
            
            handler.accept(suggestionDisplay);             
            });
         }

       
      @Override
       
      public boolean execute(TextField field, int selected, int startOfCurWord)
       {
         if (selected == -1) {                
            return false;             
            }
            
         AssistContentThreadSafe a = inheritedMethods.get(selected);
         methodName.setText(a.getName());
         returnType.setText(a.getType());
            
           
         
         
         access.setValue(AccessPermission.fromAccess(a.getAccessPermission()));
            
         paramsPane.setParams(a.getParams(), ParamInfo::getUnqualifiedType, ParamInfo::getFormalName);
            
           
         return true;         
         }     
      }
    
   
   @Override
   @OnThread(Tag.FXPlatform)
   
   public List getContextOperations()
   {        
      List<FrameOperation> operations = new ArrayList<>(super.getContextOperations());
        
      InteractionManager editor = getEditor();
        
       
      operations.add(new CustomFrameOperation(editor, "method->constructor",
      Arrays.asList(Config.getString("frame.operation.change"), Config.getString("frame.operation.change.to.constructor")),
               
      MenuItemOrder.TRANSFORM, this,
      () -> {
      Frame parent = getParentCanvas().getParent().getFrame();
      if (parent instanceof ClassFrame) {
         FrameCanvas p = ((ClassFrame)parent).getConstructorsCanvas();
         FrameCursor c = p.getLastCursor();
                       
         ConstructorElement el = new ConstructorElement(null, new AccessPermissionFragment(this, access), generateParams(),
                               
         throwsPane.getTypes(), null, null, getContents(), new JavadocUnit(getDocumentation()), frameEnabledProperty.get());
         c.insertBlockAfter(el.createFrame(getEditor()));
         getParentCanvas().removeBlock(this);                     
         }                 
      }
       
   ));
        
       
   operations.add(new CustomFrameOperation(editor, "concrete->abstract",
   Arrays.asList(Config.getString("frame.operation.change"), Config.getString("frame.operation.change.to.abstract")),
               
   MenuItemOrder.TRANSFORM, this,
   () -> {
   FrameCursor c = getCursorBefore();
                   
   MethodProtoElement el = new MethodProtoElement(null, returnType.getSlotElement(), methodName.getSlotElement(),
                       
   generateParams(), throwsPane.getTypes(), new JavadocUnit(getDocumentation()), frameEnabledProperty.get());
   c.insertBlockAfter(el.createFrame(getEditor()));
   c.getParentCanvas().removeBlock(this);                 
   }
       
));

operations.addAll(getStaticFinalOperations());

       
return operations;     
}

   
@Override
   
public List getAvailableExtensions(FrameCanvas innerCanvas, FrameCursor cursorInCanvas)
   {        
   final List<ExtensionDescription> extensions = new ArrayList<>(super.getAvailableExtensions(innerCanvas, cursorInCanvas));
       
   getStaticFinalOperations().stream().forEach(op -> extensions.add(new ExtensionDescription(op, this, true,
               
   ExtensionSource.BEFORE, ExtensionSource.AFTER, ExtensionSource.MODIFIER, ExtensionSource.SELECTION)));
       
   return extensions;     
   }

   
private List getStaticFinalOperations()
   {        
   List<ToggleBooleanProperty> operations = new ArrayList<>();
       
   operations.add(new ToggleBooleanProperty(getEditor(), TOGGLE_FINAL_METHOD, FINAL_NAME, 'n'));
       
   operations.add(new ToggleBooleanProperty(getEditor(), TOGGLE_STATIC_METHOD, STATIC_NAME, 's'));
       
   return operations;     
   }

   

public StringExpression returnTypeProperty()
   {
   return returnType.javaProperty();     
   }

@OnThread(Tag.FXPlatform)
   
public void updateOverrideDisplay(ClassElement topLevel)
   {
   if (element == null)
           
   return;

       
   final NormalMethodElement cachedElement = element;
        
       
   

   List<String> qualParamTypes = cachedElement.getQualifiedParamTypes(topLevel);
   Reflective overriddenFrom = topLevel.findSuperMethod(cachedElement.getName(), qualParamTypes);
   if (overriddenFrom != null)
       {
      String name = overriddenFrom.getSimpleName();
      if (name.indexOf('.') != -1)
      name = name.substring(name.lastIndexOf('.') + 1);
           
      final String nameFinal = name;
      if (curOverrideSource == null || !curOverrideSource.equals(nameFinal))
           {                
         curOverrideSource = nameFinal;
         overrideLabel.setText(Config.getString("frame.class.overrides.from").replace("$", nameFinal));             
         }         
      }
       

   else
       {
      if (curOverrideSource != null)
           {                
         curOverrideSource = null;
         overrideLabel.setText("");             
         }         
      }     
   }

   
@Override
   
public EditableSlot getErrorShowRedirect()
   {        
   return methodName;     
   }

   
@Override
   
public void focusName()
   {
   methodName.requestFocus(Focus.LEFT);     
   }

   
@Override
   
protected FrameContentRow makeHeader(String stylePrefix)
   {        
   return new MethodHeaderRow(this, stylePrefix)
       {            
      @Override
           
      protected EditableSlot getSlotAfterParams()
           {
         return throwsPane.getTypeSlots().findFirst().orElse(null);             
         }

           
      @Override
           
      protected EditableSlot getSlotBeforeParams()
           {                
         return methodName;             
         }         
      };
   }

   
@Override
   
public boolean tryRestoreTo(CodeElement codeElement)
   {        
   
   if (codeElement instanceof NormalMethodElement)
       {
      NormalMethodElement nme = (NormalMethodElement)codeElement;
      staticModifier.set(nme.isStatic());
      finalModifier.set(nme.isFinal());
      returnType.setText(nme.getType());
      methodName.setText(nme.getName());
      restoreDetails(nme);
           
      return true;         
      }
       
   return false;     
   } 
}

.   setView
.   NormalMethodFrame
.   deletePressedAtEnd
.   HeaderItem>>observableArrayList
.   NormalMethodFrame
.   focusWhenJustAdded
.   getFactory
.   createBlock
.   getBlockClass
.   regenerateCode
.   getCode
.   getName

top, use, map, class MethodOverrideCompletionCalculator

.   withCalculatedSuggestionList
.   execute
.   getContextOperations
.   getAvailableExtensions
.   getStaticFinalOperations
.   returnTypeProperty
.   updateOverrideDisplay
.   getErrorShowRedirect
.   focusName
.   makeHeader
.   getSlotAfterParams
.   getSlotBeforeParams
.   tryRestoreTo




495 neLoCode + 0 LoComm