text: ﻿using System.Linq;
  using WowPacketParser.DBC;
  using WowPacketParser.Enums;
  using WowPacketParser.Misc;
  using WowPacketParser.Parsing;
  using CoreParsers = WowPacketParser.Parsing.Parsers;
  using SplineFlag = WowPacketParserModule.V7_0_3_22248.Enums.SplineFlag;
  
  namespace WowPacketParserModule.V8_0_1_27101.Parsers
  {
      public static class MovementHandler
      {
          public static void ReadMonsterSplineFilter(Packet packet, params object[] indexes)
          {
              var count = packet.ReadUInt32("MonsterSplineFilterKey", indexes);
              packet.ReadSingle("BaseSpeed", indexes);
              packet.ReadInt16("StartOffset", indexes);
              packet.ReadSingle("DistToPrevFilterKey", indexes);
              packet.ReadInt16("AddedToStart", indexes);
  
              for (int i = 0; i < count; i++)
              {
                  packet.ReadInt16("IDx", indexes, i);
                  packet.ReadUInt16("Speed", indexes, i);
              }
  
              packet.ResetBitReader();
              packet.ReadBits("FilterFlags", 2, indexes);
          }
  
          public static void ReadMonsterSplineSpellEffectExtraData(Packet packet, params object[] indexes)
          {
              packet.ReadPackedGuid128("TargetGUID", indexes);
              packet.ReadUInt32("SpellVisualID", indexes);
              packet.ReadUInt32("ProgressCurveID", indexes);
              packet.ReadUInt32("ParabolicCurveID", indexes);
              packet.ReadSingle("JumpGravity", indexes);
          }
  
          public static void ReadMonsterSplineJumpExtraData(Packet packet, params object[] indexes)
          {
              packet.ReadSingle("JumpGravity", indexes);
              packet.ReadUInt32("StartTime", indexes);
              packet.ReadUInt32("Duration", indexes);
          }
  
          public static void ReadMovementSpline(Packet packet, Vector3 pos, params object[] indexes)
          {
              packet.ReadUInt32E<SplineFlag>("Flags", indexes);
              packet.ReadByte("AnimTier", indexes);
              packet.ReadUInt32("TierTransStartTime", indexes);
              packet.ReadInt32("Elapsed", indexes);
              packet.ReadUInt32("MoveTime", indexes);
              packet.ReadUInt32("FadeObjectTime", indexes);
  
              packet.ReadByte("Mode", indexes);
              packet.ReadByte("VehicleExitVoluntary", indexes);
  
              packet.ReadPackedGuid128("TransportGUID", indexes);
              packet.ReadSByte("VehicleSeat", indexes);
  
              packet.ResetBitReader();
  
              var type = packet.ReadBits("Face", 2, indexes);
              var pointsCount = packet.ReadBits("PointsCount", 16, indexes);
              var packedDeltasCount = packet.ReadBits("PackedDeltasCount", 16, indexes);
              var hasSplineFilter = packet.ReadBit("HasSplineFilter", indexes);
              var hasSpellEffectExtraData = packet.ReadBit("HasSpellEffectExtraData", indexes);
              var hasJumpExtraData = packet.ReadBit("HasJumpExtraData", indexes);
  
              if (hasSplineFilter)
                  ReadMonsterSplineFilter(packet, indexes, "MonsterSplineFilter");
  
              switch (type)
              {
                  case 1:
                      packet.ReadVector3("FaceSpot", indexes);
                      break;
                  case 2:
                      packet.ReadSingle("FaceDirection", indexes);
                      packet.ReadPackedGuid128("FacingGUID", indexes);
                      break;
                  case 3:
                      packet.ReadSingle("FaceDirection", indexes);
                      break;
                  default:
                      break;
              }
  
              Vector3 endpos = new Vector3();
              for (int i = 0; i < pointsCount; i++)
              {
                  var spot = packet.ReadVector3();
  
                  // client always taking first point
                  if (i == 0)
                      endpos = spot;
  
                  packet.AddValue("Points", spot, indexes, i);
              }
  
              var waypoints = new Vector3[packedDeltasCount];
              for (int i = 0; i < packedDeltasCount; i++)
              {
                  var packedDeltas = packet.ReadPackedVector3();
                  waypoints[i].X = packedDeltas.X;
                  waypoints[i].Y = packedDeltas.Y;
                  waypoints[i].Z = packedDeltas.Z;
              }
  
              if (hasSpellEffectExtraData)
                  ReadMonsterSplineSpellEffectExtraData(packet, "MonsterSplineSpellEffectExtra");
  
              if (hasJumpExtraData)
                  ReadMonsterSplineJumpExtraData(packet, "MonsterSplineJumpExtraData");
  
              // Calculate mid pos
              var mid = new Vector3
              {
                  X = (pos.X + endpos.X) * 0.5f,
                  Y = (pos.Y + endpos.Y) * 0.5f,
                  Z = (pos.Z + endpos.Z) * 0.5f
              };
  
              for (var i = 0; i < packedDeltasCount; ++i)
              {
                  var vec = new Vector3
                  {
                      X = mid.X - waypoints[i].X,
                      Y = mid.Y - waypoints[i].Y,
                      Z = mid.Z - waypoints[i].Z
                  };
                  packet.AddValue("WayPoints", vec, indexes, i);
              }
          }
  
          public static void ReadMovementMonsterSpline(Packet packet, Vector3 pos, params object[] indexes)
          {
              packet.ReadUInt32("Id", indexes);
              packet.ReadVector3("Destination", indexes);
  
              packet.ResetBitReader();
  
              packet.ReadBit("CrzTeleport", indexes);
              packet.ReadBits("StopDistanceTolerance", 3, indexes);
  
              ReadMovementSpline(packet, pos, indexes, "MovementSpline");
          }
  
          [Parser(Opcode.SMSG_ON_MONSTER_MOVE)]
          public static void HandleOnMonsterMove(Packet packet)
          {
              packet.ReadPackedGuid128("MoverGUID");
              var pos = packet.ReadVector3("Position");
  
              ReadMovementMonsterSpline(packet, pos, "MovementMonsterSpline");
          }
  
          [Parser(Opcode.SMSG_PHASE_SHIFT_CHANGE)]
          public static void HandlePhaseShift(Packet packet)
          {
              CoreParsers.MovementHandler.ActivePhases.Clear();
              packet.ReadPackedGuid128("Client");
              // PhaseShiftData
              packet.ReadInt32("PhaseShiftFlags");
              var count = packet.ReadInt32("PhaseShiftCount");
              packet.ReadPackedGuid128("PersonalGUID");
              for (var i = 0; i < count; ++i)
              {
                  var flags = packet.ReadUInt16("PhaseFlags", i);
                  var id = packet.ReadUInt16("Id", i);
                  CoreParsers.MovementHandler.ActivePhases.Add(id, true);
              }
              if (DBC.Phases.Any())
              {
                  foreach (var phaseGroup in DBC.GetPhaseGroups(CoreParsers.MovementHandler.ActivePhases.Keys))
                      packet.WriteLine($"PhaseGroup: { phaseGroup } Phases: { string.Join(" - ", DBC.Phases[phaseGroup]) }");
              }
              var visibleMapIDsCount = packet.ReadInt32("VisibleMapIDsCount") / 2;
              for (var i = 0; i < visibleMapIDsCount; ++i)
                  packet.ReadInt16<MapId>("VisibleMapID", i);
              var preloadMapIDCount = packet.ReadInt32("PreloadMapIDsCount") / 2;
              for (var i = 0; i < preloadMapIDCount; ++i)
                  packet.ReadInt16<MapId>("PreloadMapID", i);
              var uiMapPhaseIdCount = packet.ReadInt32("UiMapPhaseIDsCount") / 2;
              for (var i = 0; i < uiMapPhaseIdCount; ++i)
                  packet.ReadInt16("UiMapPhaseId", i);
          }
  
          [Parser(Opcode.SMSG_MOVE_UPDATE_MOD_MOVEMENT_FORCE_MAGNITUDE)]
          public static void HandleMoveUpdateModMovementForceMagnitude(Packet packet)
          {
              V7_0_3_22248.Parsers.MovementHandler.ReadMovementStats(packet, "MovementStats");
              packet.ReadSingle("Speed");
          }
      }
  }
  