package dev.crucifixbeta.mod.modules.impl.misc;

import dev.crucifixbeta.api.events.eventbus.EventListener;
import dev.crucifixbeta.api.events.impl.MoveEvent;
import dev.crucifixbeta.api.events.impl.UpdateEvent;
import dev.crucifixbeta.api.utils.player.EntityUtil;
import dev.crucifixbeta.api.utils.player.InventoryUtil;
import dev.crucifixbeta.api.utils.render.Render3DUtil;
import dev.crucifixbeta.core.impl.CommandManager;
import dev.crucifixbeta.mod.modules.Module;
import dev.crucifixbeta.mod.modules.impl.movement.ElytraFly;
import dev.crucifixbeta.mod.modules.settings.impl.BooleanSetting;
import dev.crucifixbeta.mod.modules.settings.impl.ColorSetting;
import dev.crucifixbeta.mod.modules.settings.impl.EnumSetting;
import dev.crucifixbeta.mod.modules.settings.impl.SliderSetting;
import java.awt.Color;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.screen.slot.SlotActionType;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;

@Environment(EnvType.CLIENT)
public class BaseFinderXin extends Module {
   private ChunkPos originChunk;
   private ChunkPos targetChunk;
   private int currentCircle;
   private BaseFinderXin.PathEnum currentPath;
   private boolean isBack;
   private int turnDelayTimer;
   private float targetYaw;
   private final Set<ChunkPos> targetChunks = Collections.synchronizedSet(new HashSet<>());
   private final Set<ChunkPos> visitedChunks = Collections.synchronizedSet(new HashSet<>());
   private final Set<ChunkPos> currentPathChunks = Collections.synchronizedSet(new HashSet<>());
   public final SliderSetting chunkLoadRadius = this.add(new SliderSetting("\u533a\u5757\u52a0\u8f7d\u8303\u56f4", 5.0, 2.0, 10.0, 1.0));
   public final SliderSetting circleLimit = this.add(new SliderSetting("\u641c\u7d22\u5708\u6570\u9650\u5236", 50.0, 2.0, 100.0, 1.0));
   private final EnumSetting<BaseFinderXin.MoveMode> moveMode = this.add(new EnumSetting<>("\u79fb\u52a8\u6a21\u5f0f", BaseFinderXin.MoveMode.ELYTRA));
   private final SliderSetting moveSpeed = this.add(new SliderSetting("\u79fb\u52a8\u901f\u5ea6", 3.0, 0.01, 3.0, 0.01));
   private final SliderSetting turnDelay = this.add(new SliderSetting("\u8f6c\u5411\u5ef6\u8fdf", 40.0, 1.0, 100.0, 1.0));
   private final BooleanSetting waitChunkLoad = this.add(new BooleanSetting("\u7b49\u5f85\u533a\u5757\u52a0\u8f7d", true));
   public final SliderSetting renderDistance = this.add(new SliderSetting("\u6e32\u67d3\u8ddd\u79bb", 32.0, 6.0, 128.0, 1.0));
   public final SliderSetting renderHeight = this.add(new SliderSetting("\u6e32\u67d3\u9ad8\u5ea6", 0.0, -64.0, 320.0, 1.0));
   private final SliderSetting preloadCircles = this.add(new SliderSetting("\u9884\u52a0\u8f7d\u5708\u6570", 3.0, 1.0, 10.0, 1.0));
   private final BooleanSetting autoSwapElytra = this.add(new BooleanSetting("\u81ea\u52a8\u66ff\u6362\u9798\u7fc5", true));
   private final SliderSetting elytraDurabilityThreshold = this.add(
      new SliderSetting("\u9798\u7fc5\u8010\u4e45\u5ea6\u9608\u503c", 10.0, 1.0, 432.0, 1.0, () -> this.autoSwapElytra.getValue())
   );
   private final SliderSetting walkSpeed = this.add(new SliderSetting("\u8d70\u8def\u901f\u5ea6", 0.3, 0.1, 0.6, 0.01));
   private final BooleanSetting autoJump = this.add(new BooleanSetting("\u81ea\u52a8\u8df3\u8dc3", true));
   private final BooleanSetting autoSprint = this.add(new BooleanSetting("\u81ea\u52a8\u75be\u8dd1", true));
   private final ColorSetting targetChunksSideColor = this.add(new ColorSetting("\u76ee\u6807\u533a\u5757\u9762\u989c\u8272", new Color(255, 0, 0, 95)));
   private final ColorSetting targetChunksLineColor = this.add(new ColorSetting("\u76ee\u6807\u533a\u5757\u7ebf\u989c\u8272", new Color(255, 0, 0, 205)));
   private final ColorSetting visitedChunksSideColor = this.add(new ColorSetting("\u5df2\u8bbf\u95ee\u533a\u5757\u9762\u989c\u8272", new Color(0, 255, 0, 40)));
   private final ColorSetting visitedChunksLineColor = this.add(new ColorSetting("\u5df2\u8bbf\u95ee\u533a\u5757\u7ebf\u989c\u8272", new Color(0, 255, 0, 80)));
   private final ColorSetting currentPathSideColor = this.add(new ColorSetting("\u5f53\u524d\u8def\u5f84\u533a\u5757\u9762\u989c\u8272", new Color(255, 255, 0, 60)));
   private final ColorSetting currentPathLineColor = this.add(new ColorSetting("\u5f53\u524d\u8def\u5f84\u533a\u5757\u7ebf\u989c\u8272", new Color(255, 255, 0, 100)));

   public BaseFinderXin() {
      super("BaseFinderXin", Module.Category.Misc);
      this.setChinese("\u57fa\u5730\u67e5\u627e");
   }

   @Override
   public void onEnable() {
      if (mc.player != null) {
         this.currentCircle = 0;
         this.currentPath = BaseFinderXin.PathEnum.NEXT_CIRCLE;
         this.originChunk = mc.player.getChunkPos();
         this.targetChunks.clear();
         this.visitedChunks.clear();
         this.currentPathChunks.clear();
         this.preloadTargetChunks();
         this.isBack = true;
         mc.options.forwardKey.setPressed(true);
         if (this.autoSprint.getValue()) {
            mc.options.sprintKey.setPressed(true);
         }
      }
   }

   private void preloadTargetChunks() {
      this.targetChunks.clear();
      int var1 = Math.min(this.currentCircle + (int)this.preloadCircles.getValue(), (int)this.circleLimit.getValue());

      for (int var2 = this.currentCircle; var2 <= var1; var2++) {
         this.addCircleChunks(var2);
      }
   }

   private void addCircleChunks(int circle) {
      int var2 = (int)(this.chunkLoadRadius.getValue() * (double)circle * 2.0);
      ChunkPos var3 = new ChunkPos(this.originChunk.x - var2, this.originChunk.z);
      this.targetChunks.add(var3);
      ChunkPos var4 = new ChunkPos(this.originChunk.x - var2, this.originChunk.z - var2);
      this.addPathChunks(var3, var4);
      ChunkPos var5 = new ChunkPos(this.originChunk.x + var2, this.originChunk.z - var2);
      this.addPathChunks(var4, var5);
      ChunkPos var6 = new ChunkPos(this.originChunk.x + var2, this.originChunk.z + var2);
      this.addPathChunks(var5, var6);
      ChunkPos var7 = new ChunkPos(this.originChunk.x - var2, this.originChunk.z + var2);
      this.addPathChunks(var6, var7);
      this.addPathChunks(var7, var3);
   }

   private void addPathChunks(ChunkPos from, ChunkPos to) {
      int var3 = to.x - from.x;
      int var4 = to.z - from.z;
      int var5 = Math.max(Math.abs(var3), Math.abs(var4));
      if (var5 == 0) {
         this.targetChunks.add(from);
      } else {
         for (int var6 = 0; var6 <= var5; var6++) {
            int var7 = from.x + var3 * var6 / var5;
            int var8 = from.z + var4 * var6 / var5;
            this.targetChunks.add(new ChunkPos(var7, var8));
         }
      }
   }

   private void updateCurrentPathChunks() {
      this.currentPathChunks.clear();
      if (this.originChunk != null && this.currentPath != null) {
         int var1 = (int)(this.chunkLoadRadius.getValue() * (double)this.currentCircle * 2.0);
         switch (this.currentPath) {
            case CENTER_TO_LEFT:
               ChunkPos var8 = this.originChunk;
               ChunkPos var13 = new ChunkPos(this.originChunk.x - var1, this.originChunk.z);
               this.addCurrentPathChunks(var8, var13);
               break;
            case CENTER_LEFT_TO_UP_LEFT:
               ChunkPos var7 = new ChunkPos(this.originChunk.x - var1, this.originChunk.z);
               ChunkPos var12 = new ChunkPos(this.originChunk.x - var1, this.originChunk.z - var1);
               this.addCurrentPathChunks(var7, var12);
               break;
            case UP_LEFT_TO_UP_RIGHT:
               ChunkPos var6 = new ChunkPos(this.originChunk.x - var1, this.originChunk.z - var1);
               ChunkPos var11 = new ChunkPos(this.originChunk.x + var1, this.originChunk.z - var1);
               this.addCurrentPathChunks(var6, var11);
               break;
            case UP_RIGHT_TO_DOWN_RIGHT:
               ChunkPos var5 = new ChunkPos(this.originChunk.x + var1, this.originChunk.z - var1);
               ChunkPos var10 = new ChunkPos(this.originChunk.x + var1, this.originChunk.z + var1);
               this.addCurrentPathChunks(var5, var10);
               break;
            case DOWN_RIGHT_TO_DOWN_LEFT:
               ChunkPos var4 = new ChunkPos(this.originChunk.x + var1, this.originChunk.z + var1);
               ChunkPos var9 = new ChunkPos(this.originChunk.x - var1, this.originChunk.z + var1);
               this.addCurrentPathChunks(var4, var9);
               break;
            case DOWN_LEFT_TO_LEFT:
               ChunkPos var2 = new ChunkPos(this.originChunk.x - var1, this.originChunk.z + var1);
               ChunkPos var3 = new ChunkPos(this.originChunk.x - var1, this.originChunk.z);
               this.addCurrentPathChunks(var2, var3);
         }
      }
   }

   private void addCurrentPathChunks(ChunkPos from, ChunkPos to) {
      int var3 = to.x - from.x;
      int var4 = to.z - from.z;
      int var5 = Math.max(Math.abs(var3), Math.abs(var4));

      for (int var6 = 0; var6 <= var5; var6++) {
         int var7 = from.x + var3 * var6 / var5;
         int var8 = from.z + var4 * var6 / var5;
         this.currentPathChunks.add(new ChunkPos(var7, var8));
      }
   }

   @EventListener
   public void onUpdate(UpdateEvent event) {
      if (mc.player != null) {
         this.checkAndSwapElytra();
         this.updateCurrentPathChunks();
         ChunkPos var2 = mc.player.getChunkPos();
         this.visitedChunks.add(var2);
         if ((double)this.currentCircle > this.circleLimit.getValue()) {
            CommandManager.sendMessageId("\u641c\u7d22\u5b8c\u6210", -1);
            this.disable();
         } else {
            if (this.currentPath == BaseFinderXin.PathEnum.NEXT_CIRCLE) {
               this.updateNextPath();
               this.currentCircle++;
               CommandManager.sendMessageId("前往第" + this.currentCircle + "圈...", -1);
               int var3 = this.currentCircle + (int)this.preloadCircles.getValue();
               if ((double)var3 <= this.circleLimit.getValue()) {
                  for (int var4 = this.currentCircle; var4 <= var3; var4++) {
                     this.addCircleChunks(var4);
                  }
               }
            }

            switch (this.currentPath) {
               case CENTER_TO_LEFT:
               case DOWN_LEFT_TO_LEFT:
                  this.targetChunk = new ChunkPos(
                     this.originChunk.x - (int)(this.chunkLoadRadius.getValue() * (double)this.currentCircle * 2.0), this.originChunk.z
                  );
                  break;
               case CENTER_LEFT_TO_UP_LEFT:
                  this.targetChunk = new ChunkPos(
                     this.originChunk.x - (int)(this.chunkLoadRadius.getValue() * (double)this.currentCircle * 2.0),
                     this.originChunk.z - (int)(this.chunkLoadRadius.getValue() * (double)this.currentCircle * 2.0)
                  );
                  break;
               case UP_LEFT_TO_UP_RIGHT:
                  this.targetChunk = new ChunkPos(
                     this.originChunk.x + (int)(this.chunkLoadRadius.getValue() * (double)this.currentCircle * 2.0),
                     this.originChunk.z - (int)(this.chunkLoadRadius.getValue() * (double)this.currentCircle * 2.0)
                  );
                  break;
               case UP_RIGHT_TO_DOWN_RIGHT:
                  this.targetChunk = new ChunkPos(
                     this.originChunk.x + (int)(this.chunkLoadRadius.getValue() * (double)this.currentCircle * 2.0),
                     this.originChunk.z + (int)(this.chunkLoadRadius.getValue() * (double)this.currentCircle * 2.0)
                  );
                  break;
               case DOWN_RIGHT_TO_DOWN_LEFT:
                  this.targetChunk = new ChunkPos(
                     this.originChunk.x - (int)(this.chunkLoadRadius.getValue() * (double)this.currentCircle * 2.0),
                     this.originChunk.z + (int)(this.chunkLoadRadius.getValue() * (double)this.currentCircle * 2.0)
                  );
            }

            if (mc.player.getChunkPos().equals(this.targetChunk)) {
               if (this.turnDelayTimer == 0) {
                  this.turnDelayTimer = (int)this.turnDelay.getValue();
               } else {
                  if (this.turnDelayTimer == 1) {
                     this.updateNextPath();
                     CommandManager.sendMessageId("已完成第" + this.currentCircle + "圈的 " + this.currentPath, -1);
                  }

                  this.turnDelayTimer--;
               }
            } else {
               if (this.moveMode.getValue() == BaseFinderXin.MoveMode.ELYTRA) {
                  if (ElytraFly.INSTANCE != null && ElytraFly.INSTANCE.isOn() && this.targetChunk != null) {
                     double var5 = (double)(this.targetChunk.getStartX() + 8) - mc.player.getX();
                     double var7 = (double)(this.targetChunk.getStartZ() + 8) - mc.player.getZ();
                     float var9 = (float)Math.toDegrees(Math.atan2(-var5, var7));
                     mc.player.setYaw(var9);
                     mc.player.setPitch(0.0F);
                     mc.options.forwardKey.setPressed(true);
                  } else {
                     mc.options.forwardKey.setPressed(false);
                  }
               } else if (this.moveMode.getValue() == BaseFinderXin.MoveMode.AUTO_WALK && this.targetChunk != null) {
                  double var10 = (double)(this.targetChunk.getStartX() + 8) - mc.player.getX();
                  double var11 = (double)(this.targetChunk.getStartZ() + 8) - mc.player.getZ();
                  float var12 = (float)Math.toDegrees(Math.atan2(-var10, var11));
                  mc.player.setYaw(var12);
                  if (this.autoSprint.getValue()) {
                     mc.options.sprintKey.setPressed(true);
                  }

                  mc.options.forwardKey.setPressed(true);
               } else {
                  mc.options.forwardKey.setPressed(false);
                  mc.options.sprintKey.setPressed(false);
               }
            }
         }
      }
   }

   @EventListener
   public void onMove(MoveEvent event) {
      if (mc.player != null && mc.world != null) {
         if (this.targetChunk != null && this.turnDelayTimer <= 0) {
            Vec3d var2 = mc.player.getPos();
            Vec3d var3 = new Vec3d((double)(this.targetChunk.getStartX() + 8), mc.player.getY(), (double)(this.targetChunk.getStartZ() + 8));
            double var4 = var3.x - var2.x;
            double var6 = var3.z - var2.z;
            this.targetYaw = (float)Math.toDegrees(Math.atan2(-var4, var6));
            Direction var8 = Direction.fromRotation((double)this.targetYaw);
            if (!this.waitChunkLoad.getValue() || this.AdjacentChunksLoaded(var8)) {
               if (this.waitChunkLoad.getValue()) {
                  int var9 = (int)(mc.player.getX() / 16.0);
                  int var10 = (int)(mc.player.getZ() / 16.0);
                  if (!mc.world.getChunkManager().isChunkLoaded(var9, var10)) {
                     return;
                  }
               }

               double var11 = Math.sqrt(var4 * var4 + var6 * var6);
               if (!(var11 < 1.0)) {
                  Vec3d var13 = new Vec3d(var4, 0.0, var6).normalize();
                  if (this.moveMode.getValue() == BaseFinderXin.MoveMode.ELYTRA) {
                     mc.player.getAbilities().flying = true;
                     mc.player.getAbilities().allowFlying = true;
                     event.setX(var13.x * this.moveSpeed.getValue());
                     event.setY(0.0);
                     event.setZ(var13.z * this.moveSpeed.getValue());
                     event.setX(event.getX() * 0.98);
                     event.setZ(event.getZ() * 0.99);
                  } else if (this.moveMode.getValue() == BaseFinderXin.MoveMode.AUTO_WALK) {
                     event.setX(var13.x * this.walkSpeed.getValue());
                     event.setY(0.0);
                     event.setZ(var13.z * this.walkSpeed.getValue());
                     if (this.autoJump.getValue() && mc.player.horizontalCollision) {
                        event.setY(0.42);
                     }
                  }
               }
            }
         }
      }
   }

   @Override
   public void onDisable() {
      CommandManager.sendMessageId("\u4f60\u53ef\u4ee5\u4fdd\u5b58\u6b64\u4fe1\u606f\u4ee5\u4fbf\u4e8e\u4e0b\u6b21\u91cd\u65b0\u5f00\u59cb: ", -1);
      if (this.originChunk != null) {
         CommandManager.sendMessageId("originX（起始区块X）: " + this.originChunk.x, -1);
         CommandManager.sendMessageId("originZ（起始区块Z）: " + this.originChunk.z, -1);
      }

      CommandManager.sendMessageId("circle（圈数）: " + this.currentCircle, -1);
      CommandManager.sendMessageId("currentPath（圈进度）: " + this.currentPath, -1);
      this.targetChunks.clear();
      this.visitedChunks.clear();
      this.currentPathChunks.clear();
      this.originChunk = null;
      this.targetChunk = null;
      this.currentPath = null;
      if (this.moveMode.getValue() == BaseFinderXin.MoveMode.ELYTRA) {
         if (ElytraFly.INSTANCE != null && ElytraFly.INSTANCE.isOn()) {
            ElytraFly.INSTANCE.disable();
            CommandManager.sendMessageId("\u5df2\u81ea\u52a8\u7981\u7528\u9798\u7fc5\u98de\u884c", -1);
         }
      } else if (this.moveMode.getValue() == BaseFinderXin.MoveMode.AUTO_WALK) {
         mc.options.forwardKey.setPressed(false);
         mc.options.sprintKey.setPressed(false);
      }
   }

   private boolean AdjacentChunksLoaded(Direction direction) {
      ChunkPos var2 = mc.player.getChunkPos();

      for (int var3 = -((int)this.chunkLoadRadius.getValue()); var3 <= (int)this.chunkLoadRadius.getValue(); var3++) {
         ChunkPos var4 = null;
         switch (direction) {
            case NORTH:
            case SOUTH:
               var4 = new ChunkPos(var2.x + var3, var2.z);
               break;
            case EAST:
            case WEST:
               var4 = new ChunkPos(var2.x, var2.z + var3);
         }

         if (!mc.world.getChunkManager().isChunkLoaded(var4.x, var4.z)) {
            return false;
         }
      }

      return true;
   }

   private void checkAndSwapElytra() {
      if (this.autoSwapElytra.getValue()) {
         ItemStack var1 = mc.player.getEquippedStack(EquipmentSlot.CHEST);
         if (var1.isOf(Items.ELYTRA)) {
            int var2 = var1.getMaxDamage();
            int var3 = var1.getDamage();
            int var4 = var2 - var3;
            if (var4 <= (int)this.elytraDurabilityThreshold.getValue()) {
               int var5 = InventoryUtil.findItemInventorySlot(Items.ELYTRA);
               if (var5 != -1) {
                  ItemStack var6 = mc.player.getInventory().getStack(var5 >= 36 ? var5 - 36 : var5);
                  int var7 = var6.getMaxDamage();
                  int var8 = var6.getDamage();
                  int var9 = var7 - var8;
                  if (var9 > var4) {
                     if (!EntityUtil.inInventory()) {
                        return;
                     }

                     mc.interactionManager.clickSlot(mc.player.currentScreenHandler.syncId, 6, 0, SlotActionType.PICKUP, mc.player);
                     mc.interactionManager.clickSlot(mc.player.currentScreenHandler.syncId, var5, 0, SlotActionType.PICKUP, mc.player);
                     mc.interactionManager.clickSlot(mc.player.currentScreenHandler.syncId, 6, 0, SlotActionType.PICKUP, mc.player);
                     CommandManager.sendMessageId("已自动替换鞘翅 (剩余耐久度: " + var9 + ")", -1);
                  }
               }
            }
         }
      }
   }

   @Override
   public void onRender3D(MatrixStack matrixStack) {
      if (mc.player != null) {
         BlockPos var2 = new BlockPos(mc.player.getBlockX(), (int)this.renderHeight.getValue(), mc.player.getBlockZ());
         double var3 = this.renderDistance.getValue() * 16.0;
         synchronized (this.targetChunks) {
            for (ChunkPos var7 : this.targetChunks) {
               if (var7 != null && var2.isWithinDistance(new BlockPos(var7.getCenterX(), (int)this.renderHeight.getValue(), var7.getCenterZ()), var3)) {
                  Color var8;
                  Color var9;
                  if (this.currentPathChunks.contains(var7)) {
                     var8 = this.currentPathSideColor.getValue();
                     var9 = this.currentPathLineColor.getValue();
                  } else if (this.visitedChunks.contains(var7)) {
                     var8 = this.visitedChunksSideColor.getValue();
                     var9 = this.visitedChunksLineColor.getValue();
                  } else {
                     var8 = this.targetChunksSideColor.getValue();
                     var9 = this.targetChunksLineColor.getValue();
                  }

                  if (var8.getAlpha() > 5 || var9.getAlpha() > 5) {
                     this.renderChunk(matrixStack, var7, var8, var9);
                  }
               }
            }
         }
      }
   }

   private void renderChunk(MatrixStack matrixStack, ChunkPos chunk, Color sideColor, Color lineColor) {
      Box var5 = new Box(
         new Vec3d((double)chunk.getStartX(), this.renderHeight.getValue(), (double)chunk.getStartZ()),
         new Vec3d((double)(chunk.getEndX() + 1), this.renderHeight.getValue() + 1.0, (double)(chunk.getEndZ() + 1))
      );
      Render3DUtil.drawFill(matrixStack, var5, sideColor);
      Render3DUtil.drawBox(matrixStack, var5, lineColor);
   }

   private void updateNextPath() {
      switch (this.currentPath) {
         case NEXT_CIRCLE:
            this.currentPath = BaseFinderXin.PathEnum.CENTER_TO_LEFT;
            break;
         case CENTER_TO_LEFT:
            this.currentPath = BaseFinderXin.PathEnum.CENTER_LEFT_TO_UP_LEFT;
            break;
         case CENTER_LEFT_TO_UP_LEFT:
            this.currentPath = BaseFinderXin.PathEnum.UP_LEFT_TO_UP_RIGHT;
            break;
         case UP_LEFT_TO_UP_RIGHT:
            this.currentPath = BaseFinderXin.PathEnum.UP_RIGHT_TO_DOWN_RIGHT;
            break;
         case UP_RIGHT_TO_DOWN_RIGHT:
            this.currentPath = BaseFinderXin.PathEnum.DOWN_RIGHT_TO_DOWN_LEFT;
            break;
         case DOWN_RIGHT_TO_DOWN_LEFT:
            this.currentPath = BaseFinderXin.PathEnum.DOWN_LEFT_TO_LEFT;
            break;
         case DOWN_LEFT_TO_LEFT:
            this.currentPath = BaseFinderXin.PathEnum.NEXT_CIRCLE;
      }
   }
@Environment(EnvType.CLIENT)
   public static enum MoveMode {
      ELYTRA,
      AUTO_WALK;

}

   @Environment(EnvType.CLIENT)
   public static enum PathEnum {
      NEXT_CIRCLE,
      CENTER_TO_LEFT,
      CENTER_LEFT_TO_UP_LEFT,
      UP_LEFT_TO_UP_RIGHT,
      UP_RIGHT_TO_DOWN_RIGHT,
      DOWN_RIGHT_TO_DOWN_LEFT,
      DOWN_LEFT_TO_LEFT;

}
}
