package jetbrains.mps.lang.text.editor;

/*Generated by MPS */

import jetbrains.mps.openapi.editor.EditorContext;
import org.jetbrains.mps.openapi.model.SNode;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SNodeOperations;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SPropertyOperations;
import java.util.Objects;
import jetbrains.mps.editor.runtime.selection.SelectionUtil;
import jetbrains.mps.editor.runtime.cells.CellIdManager;
import jetbrains.mps.openapi.editor.selection.SelectionManager;
import jetbrains.mps.editor.runtime.deletionApprover.DeletionApproverUtil;
import jetbrains.mps.internal.collections.runtime.ListSequence;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SLinkOperations;
import org.jetbrains.mps.openapi.language.SConcept;
import jetbrains.mps.smodel.adapter.structure.MetaAdapterFactory;
import org.jetbrains.mps.openapi.language.SProperty;
import org.jetbrains.mps.openapi.language.SContainmentLink;

/*package*/ class TextDeleteStrategyFactory {
  private TextDeleteStrategyFactory() {
  }

  private static abstract class TextDeleteStrategy extends TextStrategy {
    protected final boolean myIsForward;

    private TextDeleteStrategy(EditorContext editorContext, boolean isForward) {
      super(editorContext);
      myIsForward = isForward;
    }
  }

  /*package*/ static TextStrategy createDeleteStrategy(SNode currentNode, EditorContext editorContext, boolean isForward) {
    SNode neighbour = SNodeOperations.as(((isForward ? SNodeOperations.getNextSibling(currentNode) : SNodeOperations.getPrevSibling(currentNode))), CONCEPTS.TextElement$WN);
    if (SNodeOperations.isInstanceOf(currentNode, CONCEPTS.Word$Dn) && (neighbour != null)) {
      if (SNodeOperations.isInstanceOf(neighbour, CONCEPTS.Word$Dn)) {
        return new GlueNeighbourWordStrategy(SNodeOperations.cast(currentNode, CONCEPTS.Word$Dn), SNodeOperations.cast(neighbour, CONCEPTS.Word$Dn), editorContext, isForward);
      } else if (isEmptyString(SPropertyOperations.getString(SNodeOperations.cast(currentNode, CONCEPTS.Word$Dn), PROPS.value$zQr_))) {
        return new DeleteEmptyWordStrategy(SNodeOperations.cast(currentNode, CONCEPTS.Word$Dn), neighbour, editorContext, isForward);
      }
    }
    if ((neighbour != null)) {
      if (SNodeOperations.isInstanceOf(neighbour, CONCEPTS.Word$Dn)) {
        return new SelectNeighbourWordStrategy(SNodeOperations.cast(neighbour, CONCEPTS.Word$Dn), editorContext, isForward);
      } else {
        return new RemoveNeighbourStrategy(neighbour, editorContext, isForward);
      }
    } else {
      SNode currentLine = SNodeOperations.as(SNodeOperations.getParent(currentNode), CONCEPTS.Line$yC);
      SNode lineContainer = TextStrategy.findLineContainer(currentLine);

      SNode neighbourContainer = (isForward ? SNodeOperations.getNextSibling(lineContainer) : SNodeOperations.getPrevSibling(lineContainer));
      SNode neighbourLine = TextStrategy.findLineInContainer(neighbourContainer);
      if ((Objects.equals(SNodeOperations.getConcept(neighbourContainer), SNodeOperations.getConcept(lineContainer)) || SNodeOperations.isInstanceOf(neighbourContainer, CONCEPTS.Line$yC)) && (neighbourLine != null)) {
        return new RemoveLineStrategy(currentNode, currentLine, neighbourLine, editorContext, isForward);
      } else {
        return new RemoveWholeTextStrategy(SNodeOperations.getParent(currentLine), editorContext, isForward);
      }
    }

  }

  private static class GlueNeighbourWordStrategy extends TextDeleteStrategy {
    private SNode myCurrentWord;
    private SNode myNeighbour;
    private String myCurrentWordValue;
    private String myNeighbourValue;

    /*package*/ GlueNeighbourWordStrategy(SNode currentWord, SNode neighbourWord, EditorContext editorContext, boolean isForward) {
      super(editorContext, isForward);
      myCurrentWord = currentWord;
      myNeighbour = neighbourWord;
      myCurrentWordValue = getValueOrEmpty(SPropertyOperations.getString(myCurrentWord, PROPS.value$zQr_));
      myNeighbourValue = getValueOrEmpty(SPropertyOperations.getString(myNeighbour, PROPS.value$zQr_));
    }

    @Override
    /*package*/ void execute() {
      int selectionIndex = (myIsForward ? myCurrentWordValue.length() : myNeighbourValue.length());
      SNode newWord;
      if ((myCurrentWordValue == null || myCurrentWordValue.length() == 0)) {
        newWord = SNodeOperations.copyNode(myNeighbour);
      } else {
        newWord = SNodeOperations.copyNode(myCurrentWord);
      }
      SPropertyOperations.assign(newWord, PROPS.value$zQr_, getNewValue());
      SNodeOperations.deleteNode(myNeighbour);
      SNodeOperations.replaceWithAnother(myCurrentWord, newWord);
      SelectionUtil.selectLabelCellAnSetCaret(myEditorContext, newWord, "*" + CellIdManager.createPropertyId("value"), selectionIndex);
    }
    private String getNewValue() {
      StringBuilder builder = new StringBuilder();
      if (myIsForward) {
        builder.append(myCurrentWordValue);
        builder.append(myNeighbourValue);
      } else {
        builder.append(myNeighbourValue);
        builder.append(myCurrentWordValue);
      }
      return builder.toString();
    }

    private String getValueOrEmpty(String value) {
      if (value == null) {
        return "";
      } else {
        return value;
      }
    }
  }
  private static class DeleteEmptyWordStrategy extends TextDeleteStrategy {
    private SNode myCurrentWord;
    private SNode myNeighbour;

    private DeleteEmptyWordStrategy(SNode currentWord, SNode neighbour, EditorContext editorContext, boolean isForward) {
      super(editorContext, isForward);
      myCurrentWord = currentWord;
      myNeighbour = neighbour;
    }

    @Override
    /*package*/ void execute() {
      SNodeOperations.deleteNode(myCurrentWord);
      if (myIsForward) {
        SelectionUtil.selectLabelCellAnSetCaret(myEditorContext, myNeighbour, SelectionManager.FIRST_CELL, 0);
      } else {
        SelectionUtil.selectLabelCellAnSetCaret(myEditorContext, myNeighbour, SelectionManager.LAST_CELL, -1);
      }
    }
  }
  private static class RemoveNeighbourStrategy extends TextDeleteStrategy {
    private SNode myNeighbour;

    private RemoveNeighbourStrategy(SNode neighbour, EditorContext editorContext, boolean isForward) {
      super(editorContext, isForward);
      myNeighbour = neighbour;
    }

    @Override
    /*package*/ void execute() {
      boolean wasApproved = DeletionApproverUtil.approve(myEditorContext, myNeighbour);
      if (!(wasApproved)) {
        SNodeOperations.deleteNode(myNeighbour);
      }
    }
  }
  private static class SelectNeighbourWordStrategy extends TextDeleteStrategy {
    private SNode myNeighbour;

    private SelectNeighbourWordStrategy(SNode neighbour, EditorContext editorContext, boolean isForward) {
      super(editorContext, isForward);
      myNeighbour = neighbour;
    }

    @Override
    /*package*/ void execute() {
      if (myIsForward) {
        SelectionUtil.selectLabelCellAnSetCaret(myEditorContext, myNeighbour, SelectionManager.FIRST_CELL, 0);
      } else {
        SelectionUtil.selectLabelCellAnSetCaret(myEditorContext, myNeighbour, SelectionManager.LAST_CELL, -1);
      }
    }
  }
  private static class RemoveWholeTextStrategy extends TextDeleteStrategy {
    private SNode myCommentText;

    /*package*/ RemoveWholeTextStrategy(SNode text, EditorContext editorContext, boolean isForward) {
      super(editorContext, isForward);
      myCommentText = text;
    }

    @Override
    /*package*/ void execute() {
      boolean wasApproved = DeletionApproverUtil.approve(myEditorContext, myCommentText);
      if (!(wasApproved)) {
        SNodeOperations.deleteNode(myCommentText);
      }
    }
  }
  private static class RemoveLineStrategy extends TextDeleteStrategy {
    private SNode myNeighbourLine;
    private SNode myCurrentLine;
    private SNode myCurrentNode;


    /*package*/ RemoveLineStrategy(SNode currentNode, SNode currentLine, SNode neighbourLine, EditorContext editorContext, boolean isForward) {
      super(editorContext, isForward);
      myCurrentNode = currentNode;
      myCurrentLine = currentLine;
      myNeighbourLine = neighbourLine;
    }

    @Override
    /*package*/ void execute() {
      SNode edgeElement;
      if (myIsForward) {
        edgeElement = ListSequence.fromList(SLinkOperations.getChildren(myNeighbourLine, LINKS.elements$_j45)).first();
        ListSequence.fromList(SLinkOperations.getChildren(myNeighbourLine, LINKS.elements$_j45)).visitAll((it) -> ListSequence.fromList(SLinkOperations.getChildren(myCurrentLine, LINKS.elements$_j45)).addElement(it));
        SNodeOperations.deleteNode(TextStrategy.findLineContainer(myNeighbourLine));
      } else {
        edgeElement = ListSequence.fromList(SLinkOperations.getChildren(myCurrentLine, LINKS.elements$_j45)).first();
        ListSequence.fromList(SLinkOperations.getChildren(myCurrentLine, LINKS.elements$_j45)).visitAll((it) -> ListSequence.fromList(SLinkOperations.getChildren(myNeighbourLine, LINKS.elements$_j45)).addElement(it));
        SNodeOperations.deleteNode(TextStrategy.findLineContainer(myCurrentLine));
      }
      if (SNodeOperations.isInstanceOf(edgeElement, CONCEPTS.Word$Dn) && SNodeOperations.isInstanceOf(myCurrentNode, CONCEPTS.Word$Dn)) {
        TextDeleteStrategyFactory.createDeleteStrategy(myCurrentNode, myEditorContext, myIsForward).execute();
      }
    }
  }
  private static boolean isEmptyString(String str) {
    return str == null || str.isEmpty();
  }

  private static final class CONCEPTS {
    /*package*/ static final SConcept TextElement$WN = MetaAdapterFactory.getConcept(0xc7fb639fbe784307L, 0x89b0b5959c3fa8c8L, 0x229012ddae35ee7L, "jetbrains.mps.lang.text.structure.TextElement");
    /*package*/ static final SConcept Word$Dn = MetaAdapterFactory.getConcept(0xc7fb639fbe784307L, 0x89b0b5959c3fa8c8L, 0x229012ddae35f04L, "jetbrains.mps.lang.text.structure.Word");
    /*package*/ static final SConcept Line$yC = MetaAdapterFactory.getConcept(0xc7fb639fbe784307L, 0x89b0b5959c3fa8c8L, 0x2331694e561af166L, "jetbrains.mps.lang.text.structure.Line");
  }

  private static final class PROPS {
    /*package*/ static final SProperty value$zQr_ = MetaAdapterFactory.getProperty(0xc7fb639fbe784307L, 0x89b0b5959c3fa8c8L, 0x229012ddae35f04L, 0x229012ddae35f05L, "value");
  }

  private static final class LINKS {
    /*package*/ static final SContainmentLink elements$_j45 = MetaAdapterFactory.getContainmentLink(0xc7fb639fbe784307L, 0x89b0b5959c3fa8c8L, 0x2331694e561af166L, 0x2331694e561af167L, "elements");
  }
}
