package net.minecraft.client.gui.font;

import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.Supplier;
import net.minecraft.ChatFormatting;
import net.minecraft.SharedConstants;
import net.minecraft.Util;
import net.minecraft.client.Minecraft;
import net.minecraft.client.StringSplitter;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.util.Mth;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;

@OnlyIn(Dist.CLIENT)
public class TextFieldHelper {
   private final Supplier<String> getMessageFn;
   private final Consumer<String> setMessageFn;
   private final Supplier<String> getClipboardFn;
   private final Consumer<String> setClipboardFn;
   private final Predicate<String> stringValidator;
   private int cursorPos;
   private int selectionPos;

   public TextFieldHelper(Supplier<String> p_95137_, Consumer<String> p_95138_, Supplier<String> p_95139_, Consumer<String> p_95140_, Predicate<String> p_95141_) {
      this.getMessageFn = p_95137_;
      this.setMessageFn = p_95138_;
      this.getClipboardFn = p_95139_;
      this.setClipboardFn = p_95140_;
      this.stringValidator = p_95141_;
      this.setCursorToEnd();
   }

   public static Supplier<String> createClipboardGetter(Minecraft p_95154_) {
      return () -> {
         return getClipboardContents(p_95154_);
      };
   }

   public static String getClipboardContents(Minecraft p_95170_) {
      return ChatFormatting.stripFormatting(p_95170_.keyboardHandler.getClipboard().replaceAll("\\r", ""));
   }

   public static Consumer<String> createClipboardSetter(Minecraft p_95183_) {
      return (p_95173_) -> {
         setClipboardContents(p_95183_, p_95173_);
      };
   }

   public static void setClipboardContents(Minecraft p_95156_, String p_95157_) {
      p_95156_.keyboardHandler.setClipboard(p_95157_);
   }

   public boolean charTyped(char p_95144_) {
      if (SharedConstants.isAllowedChatCharacter(p_95144_)) {
         this.insertText(this.getMessageFn.get(), Character.toString(p_95144_));
      }

      return true;
   }

   public boolean keyPressed(int p_95146_) {
      if (Screen.isSelectAll(p_95146_)) {
         this.selectAll();
         return true;
      } else if (Screen.isCopy(p_95146_)) {
         this.copy();
         return true;
      } else if (Screen.isPaste(p_95146_)) {
         this.paste();
         return true;
      } else if (Screen.isCut(p_95146_)) {
         this.cut();
         return true;
      } else {
         TextFieldHelper.CursorStep textfieldhelper$cursorstep = Screen.hasControlDown() ? TextFieldHelper.CursorStep.WORD : TextFieldHelper.CursorStep.CHARACTER;
         if (p_95146_ == 259) {
            this.removeFromCursor(-1, textfieldhelper$cursorstep);
            return true;
         } else {
            if (p_95146_ == 261) {
               this.removeFromCursor(1, textfieldhelper$cursorstep);
            } else {
               if (p_95146_ == 263) {
                  this.moveBy(-1, Screen.hasShiftDown(), textfieldhelper$cursorstep);
                  return true;
               }

               if (p_95146_ == 262) {
                  this.moveBy(1, Screen.hasShiftDown(), textfieldhelper$cursorstep);
                  return true;
               }

               if (p_95146_ == 268) {
                  this.setCursorToStart(Screen.hasShiftDown());
                  return true;
               }

               if (p_95146_ == 269) {
                  this.setCursorToEnd(Screen.hasShiftDown());
                  return true;
               }
            }

            return false;
         }
      }
   }

   private int clampToMsgLength(int p_95196_) {
      return Mth.clamp(p_95196_, 0, this.getMessageFn.get().length());
   }

   private void insertText(String p_95161_, String p_95162_) {
      if (this.selectionPos != this.cursorPos) {
         p_95161_ = this.deleteSelection(p_95161_);
      }

      this.cursorPos = Mth.clamp(this.cursorPos, 0, p_95161_.length());
      String s = (new StringBuilder(p_95161_)).insert(this.cursorPos, p_95162_).toString();
      if (this.stringValidator.test(s)) {
         this.setMessageFn.accept(s);
         this.selectionPos = this.cursorPos = Math.min(s.length(), this.cursorPos + p_95162_.length());
      }

   }

   public void insertText(String p_95159_) {
      this.insertText(this.getMessageFn.get(), p_95159_);
   }

   private void resetSelectionIfNeeded(boolean p_95164_) {
      if (!p_95164_) {
         this.selectionPos = this.cursorPos;
      }

   }

   public void moveBy(int p_232576_, boolean p_232577_, TextFieldHelper.CursorStep p_232578_) {
      switch (p_232578_) {
         case CHARACTER:
            this.moveByChars(p_232576_, p_232577_);
            break;
         case WORD:
            this.moveByWords(p_232576_, p_232577_);
      }

   }

   public void moveByChars(int p_169094_) {
      this.moveByChars(p_169094_, false);
   }

   public void moveByChars(int p_95151_, boolean p_95152_) {
      this.cursorPos = Util.offsetByCodepoints(this.getMessageFn.get(), this.cursorPos, p_95151_);
      this.resetSelectionIfNeeded(p_95152_);
   }

   public void moveByWords(int p_169096_) {
      this.moveByWords(p_169096_, false);
   }

   public void moveByWords(int p_95167_, boolean p_95168_) {
      this.cursorPos = StringSplitter.getWordPosition(this.getMessageFn.get(), p_95167_, this.cursorPos, true);
      this.resetSelectionIfNeeded(p_95168_);
   }

   public void removeFromCursor(int p_232573_, TextFieldHelper.CursorStep p_232574_) {
      switch (p_232574_) {
         case CHARACTER:
            this.removeCharsFromCursor(p_232573_);
            break;
         case WORD:
            this.removeWordsFromCursor(p_232573_);
      }

   }

   public void removeWordsFromCursor(int p_232580_) {
      int i = StringSplitter.getWordPosition(this.getMessageFn.get(), p_232580_, this.cursorPos, true);
      this.removeCharsFromCursor(i - this.cursorPos);
   }

   public void removeCharsFromCursor(int p_95190_) {
      String s = this.getMessageFn.get();
      if (!s.isEmpty()) {
         String s1;
         if (this.selectionPos != this.cursorPos) {
            s1 = this.deleteSelection(s);
         } else {
            int i = Util.offsetByCodepoints(s, this.cursorPos, p_95190_);
            int j = Math.min(i, this.cursorPos);
            int k = Math.max(i, this.cursorPos);
            s1 = (new StringBuilder(s)).delete(j, k).toString();
            if (p_95190_ < 0) {
               this.selectionPos = this.cursorPos = j;
            }
         }

         this.setMessageFn.accept(s1);
      }

   }

   public void cut() {
      String s = this.getMessageFn.get();
      this.setClipboardFn.accept(this.getSelected(s));
      this.setMessageFn.accept(this.deleteSelection(s));
   }

   public void paste() {
      this.insertText(this.getMessageFn.get(), this.getClipboardFn.get());
      this.selectionPos = this.cursorPos;
   }

   public void copy() {
      this.setClipboardFn.accept(this.getSelected(this.getMessageFn.get()));
   }

   public void selectAll() {
      this.selectionPos = 0;
      this.cursorPos = this.getMessageFn.get().length();
   }

   private String getSelected(String p_95175_) {
      int i = Math.min(this.cursorPos, this.selectionPos);
      int j = Math.max(this.cursorPos, this.selectionPos);
      return p_95175_.substring(i, j);
   }

   private String deleteSelection(String p_95185_) {
      if (this.selectionPos == this.cursorPos) {
         return p_95185_;
      } else {
         int i = Math.min(this.cursorPos, this.selectionPos);
         int j = Math.max(this.cursorPos, this.selectionPos);
         String s = p_95185_.substring(0, i) + p_95185_.substring(j);
         this.selectionPos = this.cursorPos = i;
         return s;
      }
   }

   public void setCursorToStart() {
      this.setCursorToStart(false);
   }

   public void setCursorToStart(boolean p_95177_) {
      this.cursorPos = 0;
      this.resetSelectionIfNeeded(p_95177_);
   }

   public void setCursorToEnd() {
      this.setCursorToEnd(false);
   }

   public void setCursorToEnd(boolean p_95187_) {
      this.cursorPos = this.getMessageFn.get().length();
      this.resetSelectionIfNeeded(p_95187_);
   }

   public int getCursorPos() {
      return this.cursorPos;
   }

   public void setCursorPos(int p_169099_) {
      this.setCursorPos(p_169099_, true);
   }

   public void setCursorPos(int p_95180_, boolean p_95181_) {
      this.cursorPos = this.clampToMsgLength(p_95180_);
      this.resetSelectionIfNeeded(p_95181_);
   }

   public int getSelectionPos() {
      return this.selectionPos;
   }

   public void setSelectionPos(int p_169101_) {
      this.selectionPos = this.clampToMsgLength(p_169101_);
   }

   public void setSelectionRange(int p_95148_, int p_95149_) {
      int i = this.getMessageFn.get().length();
      this.cursorPos = Mth.clamp(p_95148_, 0, i);
      this.selectionPos = Mth.clamp(p_95149_, 0, i);
   }

   public boolean isSelecting() {
      return this.cursorPos != this.selectionPos;
   }

   @OnlyIn(Dist.CLIENT)
   public static enum CursorStep {
      CHARACTER,
      WORD;
   }
}