package wtf.opal.client.screen.click.dropdown.panel;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.input.KeyInput;
import wtf.opal.client.Constants;
import wtf.opal.client.OpalClient;
import wtf.opal.client.feature.module.ModuleCategory;
import wtf.opal.client.renderer.NVGRenderer;
import wtf.opal.client.renderer.repository.FontRepository;
import wtf.opal.client.screen.click.OpalPanelComponent;
import wtf.opal.client.screen.click.dropdown.ClickGuiAnimationConfig;
import wtf.opal.client.screen.click.dropdown.ClickGuiBlur;
import wtf.opal.client.screen.click.dropdown.DropdownClickGUI;
import wtf.opal.utility.misc.HoverUtility;
import wtf.opal.utility.render.ColorUtility;
import wtf.opal.utility.render.Scroller;
import wtf.opal.utility.render.animation.Animation;
import wtf.opal.utility.render.animation.Easing;

@Environment(EnvType.CLIENT)
public final class CategoryPanel extends OpalPanelComponent {
   private final Scroller scroller = new Scroller();
   private final ModuleCategory category;
   private Animation openAnimation;
   private final Animation collapseAnimation = new Animation(Easing.DECELERATE, 160L);
   private final int panelIndex;
   private boolean lastPanel;
   private boolean closing;
   private boolean collapsed;
   private boolean dragging;
   private float layoutX;
   private float layoutY;
   private float dragOffsetX;
   private float dragOffsetY;
   private float dragStartMouseX;
   private float dragStartMouseY;
   private float dragStartOffsetX;
   private float dragStartOffsetY;
   private String searchQuery = "";
   private final List<ModulePanel> modulePanelList = new ArrayList<>();

   public CategoryPanel(ModuleCategory category, int panelIndex) {
      this.category = category;
      this.panelIndex = panelIndex;
      this.lastPanel = panelIndex == ModuleCategory.VALUES.length - 1;
   }

   public void setLastPanel(boolean lastPanel) {
      this.lastPanel = lastPanel;
   }

   public void setLayoutDimensions(float x, float y, float width, float height) {
      this.layoutX = x;
      this.layoutY = y;
      this.setDimensions(x + this.dragOffsetX, y + this.dragOffsetY, width, height);
   }

   public void setSearchQuery(String searchQuery) {
      this.searchQuery = DropdownClickGUI.normalizeSearch(searchQuery);
   }

   public boolean isMouseOverPanel(double mouseX, double mouseY) {
      float var5 = this.collapsed ? this.height : Math.min(this.getTotalHeight(), (float)Constants.mc.getWindow().getScaledHeight() - this.y);
      return HoverUtility.isHovering(this.x, this.y, this.width, var5, mouseX, mouseY);
   }

   @Override
   public void render(DrawContext context, int mouseX, int mouseY, float delta) {
      if (this.dragging) {
         this.dragOffsetX = this.dragStartOffsetX + ((float)mouseX - this.dragStartMouseX);
         this.dragOffsetY = this.dragStartOffsetY + ((float)mouseY - this.dragStartMouseY);
         this.setDimensions(this.layoutX + this.dragOffsetX, this.layoutY + this.dragOffsetY, this.width, this.height);
      }

      ClickGuiAnimationConfig.applyClickGuiOpenClose(this.openAnimation, 100L + (long)this.panelIndex * 80L, Easing.EASE_OUT_SINE);
      ClickGuiAnimationConfig.applyFunctionBar(this.collapseAnimation, 160L, Easing.DECELERATE);
      this.openAnimation.run(this.closing ? 0.0F : 1.0F);
      this.collapseAnimation.run(this.collapsed ? 0.0F : 1.0F);
      if (this.lastPanel && this.openAnimation.isFinished() && this.closing) {
         Constants.mc.setScreen(null);
      } else {
         float[] var5 = new float[]{this.y + this.height};
         float var6 = this.getTotalHeight();
         float var7 = this.openAnimation.getValue();
         float var8 = Math.min((float)Constants.mc.getWindow().getScaledHeight() - this.y, var6 * var7);
         float var9 = this.scroller.getAnimation().getValue();
         float var10 = 5.0F * (1.0F - this.collapseAnimation.getValue());
         float var11 = Math.max(this.height, Math.min(var6, (float)Constants.mc.getWindow().getScaledHeight() - this.y));
         NVGRenderer.globalAlpha(var7);
         NVGRenderer.scissor(
            this.x,
            this.y,
            this.width,
            var8,
            () -> {
               this.renderGlassColumn(var11);
               NVGRenderer.roundedRectVarying(
                  this.x, this.y + var9, this.width, this.height, 8.0F, 8.0F, var10 + 3.0F, var10 + 3.0F, ColorUtility.applyOpacity(-1, 0.025F)
               );
               NVGRenderer.roundedRectVarying(
                  this.x,
                  this.y + var9,
                  this.width,
                  this.height,
                  8.0F,
                  8.0F,
                  var10 + 3.0F,
                  var10 + 3.0F,
                  ColorUtility.scaleOpacity(ColorUtility.getHeaderBackgroundColor(), 0.42F)
               );
               NVGRenderer.roundedRectOutline(this.x, this.y + var9, this.width, this.height, 8.0F, 1.1F, ColorUtility.applyOpacity(-1, 0.12F));
               FontRepository.getFont("productsans-bold")
                  .drawString(this.category.getDisplayName(), this.x + 5.0F, this.y + var9 + 13.0F, 9.0F, ColorUtility.getEnabledTextColor());
               FontRepository.getFont("materialicons-outlined")
                  .drawString(this.category.getIcon(), this.x + this.width - 15.5F, this.y + var9 + 15.0F, 10.0F, ColorUtility.getEnabledTextColor());
               if (!(this.collapseAnimation.getValue() <= 0.01F)) {
                  int var9x = 0;
                  int var10x = this.getVisibleModuleCount();

                  for (int var11x = 0; var11x < this.modulePanelList.size(); var11x++) {
                     ModulePanel var12 = this.modulePanelList.get(var11x);
                     if (this.isVisible(var12)) {
                        float var13 = this.height + var12.getExpandAnimation().getValue() * var12.getAddedHeight();
                        var12.setDimensions(this.x, var5[0] + var9, this.width, var13);
                        var12.setLastModule(var9x == var10x - 1);
                        var12.render(context, mouseX, mouseY, delta);
                        var5[0] += var13;
                        var9x++;
                     }
                  }
               }
            }
         );
         NVGRenderer.globalAlpha(1.0F);
         if (this.collapsed && this.collapseAnimation.getValue() <= 0.01F) {
            this.scroller.onScroll(0.0F);
         } else {
            this.scroller.onScroll(this.getMaxOffset(var6));
         }
      }
   }

   @Override
   public void mouseClicked(double mouseX, double mouseY, int button) {
      if (HoverUtility.isHovering(this.x, this.y, this.width, this.height, mouseX, mouseY)) {
         if (button == 0) {
            this.dragging = true;
            this.dragStartMouseX = (float)mouseX;
            this.dragStartMouseY = (float)mouseY;
            this.dragStartOffsetX = this.dragOffsetX;
            this.dragStartOffsetY = this.dragOffsetY;
            return;
         }

         if (button == 1) {
            this.collapsed = !this.collapsed;
            return;
         }
      }

      if (!this.collapsed) {
         float var6 = this.getTotalHeight();
         if (HoverUtility.isHovering(this.x, this.y, this.width, Math.min(var6, (float)Constants.mc.getWindow().getScaledHeight() - this.y), mouseX, mouseY)) {
            this.modulePanelList.stream().filter(this::isVisible).forEach(modulePanel -> modulePanel.mouseClicked(mouseX, mouseY, button));
         }
      }
   }

   @Override
   public void keyPressed(KeyInput keyInput) {
      if (!this.collapsed) {
         this.modulePanelList.stream().filter(this::isVisible).forEach(modulePanel -> modulePanel.keyPressed(keyInput));
      }
   }

   @Override
   public void charTyped(char chr, int modifiers) {
      if (!this.collapsed) {
         this.modulePanelList.stream().filter(this::isVisible).forEach(modulePanel -> modulePanel.charTyped(chr, modifiers));
      }
   }

   @Override
   public void mouseReleased(double mouseX, double mouseY, int button) {
      if (button == 0) {
         this.dragging = false;
      }

      if (!this.collapsed) {
         this.modulePanelList.stream().filter(this::isVisible).forEach(modulePanel -> modulePanel.mouseReleased(mouseX, mouseY, button));
      }
   }

   @Override
   public void mouseScrolled(double mouseX, double mouseY, double horizontalAmount, double verticalAmount) {
      if (!this.collapsed) {
         float var9 = this.getTotalHeight();
         if (HoverUtility.isHovering(this.x, this.y, this.width, var9, mouseX, mouseY)) {
            this.scroller.addScroll(verticalAmount, this.getMaxOffset(var9));
         }

         this.modulePanelList
            .stream()
            .filter(this::isVisible)
            .forEach(modulePanel -> modulePanel.mouseScrolled(mouseX, mouseY, horizontalAmount, verticalAmount));
      }
   }

   @Override
   public void init() {
      if (this.modulePanelList.isEmpty()) {
         OpalClient.getInstance()
            .getModuleRepository()
            .getModulesInCategory(this.category)
            .forEach(module -> this.modulePanelList.add(new ModulePanel(module)));
         this.modulePanelList.sort(Comparator.comparing(CategoryPanel::moduleSortKey).thenComparing(p -> p.getModule().getName()));
      }

      this.openAnimation = new Animation(Easing.EASE_OUT_SINE, 100L + (long)this.panelIndex * 80L);
      this.collapseAnimation.setValue(this.collapsed ? 0.0F : 1.0F);
      this.closing = false;
      this.modulePanelList.forEach(ModulePanel::init);
   }

   @Override
   public void close() {
      this.closing = true;
      this.modulePanelList.forEach(ModulePanel::close);
   }

   private float getTotalHeight() {
      float var1 = this.height;
      float var2 = 0.0F;

      for (ModulePanel var4 : this.modulePanelList) {
         if (this.isVisible(var4)) {
            var4.getExpandAnimation().run(var4.isExpanded() ? 1.0F : 0.0F);
            var2 += this.height + var4.getExpandAnimation().getValue() * var4.getAddedHeight();
         }
      }

      return var1 + var2 * this.collapseAnimation.getValue();
   }

   private int getVisibleModuleCount() {
      int var1 = 0;

      for (ModulePanel var3 : this.modulePanelList) {
         if (this.isVisible(var3)) {
            var1++;
         }
      }

      return var1;
   }

   private boolean isVisible(ModulePanel panel) {
      if (this.searchQuery.isEmpty()) {
         return true;
      } else {
         String var2 = DropdownClickGUI.normalizeSearch(panel.getModule().getName());
         String var3 = DropdownClickGUI.normalizeSearch(panel.getModule().getDisplayName());
         String var4 = DropdownClickGUI.normalizeSearch(panel.getModule().getId());
         String var5 = DropdownClickGUI.normalizeSearch(panel.getModule().getDescription());
         return var2.contains(this.searchQuery) || var3.contains(this.searchQuery) || var4.contains(this.searchQuery) || var5.contains(this.searchQuery);
      }
   }

   private float getMaxOffset(float totalHeight) {
      float var2 = (float)Constants.mc.getWindow().getScaledHeight() - this.y;
      float var3 = Math.min(var2, totalHeight * this.openAnimation.getValue());
      float var4 = var3 == var2 ? this.y : 0.0F;
      return Math.max(0.0F, totalHeight - var3 + var4);
   }

   private static String moduleSortKey(ModulePanel panel) {
      String var1 = panel.getModule().getName();
      return var1 == null ? "" : var1.toLowerCase(Locale.ROOT);
   }

   private void renderGlassColumn(float columnHeight) {
      if (!(columnHeight <= 1.0F)) {
         int var2 = ColorUtility.getPanelBackgroundColor();
         int var3 = ColorUtility.applyOpacity(ColorUtility.brighter(var2, 0.08F), 0.36F);
         int var4 = ColorUtility.applyOpacity(ColorUtility.darker(var2, 0.18F), 0.3F);
         int var5 = ColorUtility.getThemeColor();
         float var6 = 9.0F;
         if (ClickGuiBlur.isEnabled()) {
            NVGRenderer.roundedRect(this.x, this.y, this.width, columnHeight, 9.0F, NVGRenderer.BLUR_PAINT);
         }

         NVGRenderer.roundedRectGradient(this.x, this.y, this.width, columnHeight, 9.0F, var3, var4, 90.0F);
         NVGRenderer.roundedRect(
            this.x + 1.2F, this.y + 1.2F, this.width - 2.4F, Math.min(18.0F, columnHeight - 2.4F), 7.0F, ColorUtility.applyOpacity(-1, 0.035F)
         );
         NVGRenderer.roundedRectOutline(this.x, this.y, this.width, columnHeight, 9.0F, 1.15F, ColorUtility.applyOpacity(-1, 0.13F));
         NVGRenderer.roundedRectOutline(
            this.x + 0.75F, this.y + 0.75F, this.width - 1.5F, columnHeight - 1.5F, 8.0F, 0.85F, ColorUtility.applyOpacity(var5, 0.1F)
         );
      }
   }
}
