import { world, system } from "@minecraft/server";
import vehiclesValues from "./VehicleValues.js";
import {
  vehicleCheck,
  takeoffSpeed,
  takeOffFlight,
  vehicleSpeed,
  isAirInFrontAndBelow,
  sendDevMessage,
  applyAcceleration,
  setHoverState,
} from "./VehicleMovementUtil.js";
const planeSpeeds = new Map(); // Map for storing speed - tied to player - this is so it resets when the player exits a moving vehicle
const planeStepDown = new Map(); // Map for storing data for making sure the vehicle doesn't accelerate when going down blocks
const lastGroundTime = new Map(); // Track when players were last on ground

// Main loop to check and display speed
system.runInterval(() => {
  for (const player of world.getPlayers()) {
    if (vehicleCheck(player)) {
      // Get vehicle information
      const vehicle = player.getComponent("minecraft:riding").entityRidingOn; // Vehicle player is riding
      const driver = vehicle?.getComponent("minecraft:rideable").getRiders()[0];

      // Only apply movement logic to the driver
      if (driver !== player) continue; // Skip this player if they are not the driver

      const vehicleTypeId = vehicle.typeId; // Type id of vehicle
      const vehicleData = Object.values(vehiclesValues).find(
        // Vehicle values from import
        (v) => v.typeId === vehicleTypeId
      );

      // Check for fuel
      const hasFuel = vehicle?.getProperty("pb_dino:has_fuel");
      // Store time to help track if the plane is flying, or falling down a block
      const currentTime = Date.now();

      if (vehicleData.type == "heli") {
        if (driver.isJumping) {
          if (!hasFuel) {
            driver.onScreenDisplay.setActionBar(
              "Insert fuel into the helicopters equipment slot to fly"
            );
            return;
          }
          driver.setDynamicProperty("end", Date.now());
          // Check "HoverState" - if jump is pressed again, add tag, return TRUE, if not, remove tag, return FALSE
          if (!setHoverState(driver, vehicle))
            driver.setDynamicProperty("start", Date.now());
          driver.onScreenDisplay.setActionBar(
            "Hover Mode: §4§lOff§r - release and then tap jump to activate"
          );
        }
        // If not holding jump, but helicopter has tag "hovering" - IN HOVER MODE
        else if (vehicle?.hasTag("pb_dino_hovering")) {
          vehicle?.applyImpulse({ x: 0, y: 0.0784, z: 0 });
          driver.onScreenDisplay.setActionBar(
            "Hover Mode: §2§lOn§r - press jump to de-activate"
          );
        }
        // Not jumping and not on ground
        else if (!vehicle?.isOnGround) {
          vehicle?.removeTag("pb_dino_hovering");
          vehicle?.removeEffect("levitation");
          vehicle?.addEffect("slow_falling", 20, {
            amplifier: 0,
            showParticles: false,
          });
          driver.onScreenDisplay.setActionBar("Hold jump to ascend");
        }
        return;
      }
      // Blocks checks to change impulse amounts
      let blockBelow = null;
      let isOnSlowBlock = false;
      let isOnIceBlock = false;
      const isOnGround = vehicle.isOnGround;

      if (vehicle.location.y < 300 && vehicle.location.y > 5) {
        blockBelow = vehicle.dimension.getBlock({
          x: Math.floor(vehicle.location.x),
          y: Math.floor(vehicle.location.y - 0.5),
          z: Math.floor(vehicle.location.z),
        });
        isOnSlowBlock = [
          "minecraft:soul_sand",
          "minecraft:slime",
          "minecraft:honey_block",
        ].includes(blockBelow.typeId);
        isOnIceBlock = [
          "minecraft:ice",
          "minecraft:packed_ice",
          "minecraft:blue_ice",
        ].includes(blockBelow.typeId);
      }
      // Variables for using in code
      let impulse = planeSpeeds.get(driver.name) || 0; // Get player's speed from the map
      let maxSpeed = isOnGround
        ? vehicleData.maxGroundSpeed
        : vehicleData.maxAirSpeed;
      let acceleration = isOnGround
        ? vehicleData.groundAcceleration
        : vehicleData.airAcceleration;
      const currentSpeed = vehicleSpeed(vehicle);

      const timeSinceGround = lastGroundTime.has(player.name)
        ? (currentTime - lastGroundTime.get(player.name)) / 1000
        : Infinity;

      const isFlying = !isOnGround && timeSinceGround > 2; // Consider flying after 1.5 seconds in air

      // Run movement
      if (driver.isJumping && hasFuel) {
        if (impulse > currentSpeed * 0.8) {
          sendDevMessage(driver, "Vehicle is stuck!");
          impulse = 0;
        }

        if (isOnGround) {
          lastGroundTime.set(player.name, currentTime);
          if (planeStepDown.has(vehicle)) {
            impulse = planeStepDown.get(vehicle);
            planeStepDown.delete(vehicle);
          }
          // Adjusting impulse based on different surfaces
          if (isOnSlowBlock) {
            if (currentSpeed < maxSpeed * 0.4) {
              impulse += acceleration * 0.6;
              sendDevMessage(driver, "Slow - Acc.");
            } else {
              impulse *= 0.8;
              sendDevMessage(driver, "Slow - Dec.");
            }
          } else if (isOnIceBlock) {
            if (currentSpeed < maxSpeed * 0.7) {
              impulse += acceleration * 0.2;
              sendDevMessage(driver, "Ice - Acc.");
            } else {
              impulse *= 0.8;
              sendDevMessage(driver, "Ice - Dec.");
            }
          } else if (currentSpeed < maxSpeed) {
            impulse += acceleration;
            sendDevMessage(driver, "Normal - Acc.");
          } else {
            impulse -= acceleration;
            sendDevMessage(driver, "Normal - Dec.");
          }
        } else if (
          currentSpeed < vehicleData.take_off &&
          isAirInFrontAndBelow(vehicle, driver) &&
          !isFlying
        ) {
          if (!planeStepDown.has(vehicle)) {
            planeStepDown.set(vehicle, impulse);
            impulse /= 2;
          }
          impulse -= acceleration * 2;
          sendDevMessage(driver, "Air - Reduced Acc.");
        } else if (currentSpeed < vehicleData.take_off && isFlying) {
          // CLAMP SPEED: Ensure the plane's speed does not exceed maxSpeed
          impulse = maxSpeed / 10;
        } else if (!isOnGround && currentSpeed >= vehicleData.take_off) {
          // CLAMP SPEED: Ensure the plane's speed does not exceed maxSpeed
          impulse = maxSpeed / 10;
        }

        // Apply the impulse values to the vehicle
        planeSpeeds.set(driver.name, impulse); // Update player's speed in the map
      } else if (driver.isJumping && !hasFuel) {
        driver.onScreenDisplay.setActionBar(
          "Insert fuel into the planes equipment slot to drive"
        );
      } else if (!driver.isJumping && hasFuel) {
        driver.onScreenDisplay.setActionBar("Hold jump to accelerate");
        // Apply deceleration based on whether the vehicle is on the ground or in the air
        if (isOnGround) {
          impulse *= 0.95; // Normal ground deceleration
          sendDevMessage(driver, "Decelerating on ground.");
        } else {
          // In the air, apply a reduced deceleration (gliding effect)
          impulse *= 0.995; // Reduced deceleration in air
          sendDevMessage(driver, "Gliding in the air.");
        }

        // Ensure the impulse doesn't go below 0.05
        if (impulse < 0.05) impulse = 0;
        planeSpeeds.set(driver.name, impulse); // Update player's speed in the map
      } else if (!hasFuel) {
        driver.onScreenDisplay.setActionBar("Hold jump to accelerate");
        // Apply deceleration based on whether the vehicle is on the ground or in the air
        if (isOnGround) {
          impulse *= 0.95; // Normal ground deceleration
          sendDevMessage(driver, "Decelerating on ground.");
        } else {
          // In the air, apply a reduced deceleration (gliding effect)
          impulse *= 0.995; // Reduced deceleration in air
          sendDevMessage(driver, "Gliding in the air.");
        }

        // Ensure the impulse doesn't go below 0.05
        if (impulse < 0.05) impulse = 0;
        planeSpeeds.set(driver.name, impulse); // Update player's speed in the map
      }
      // If in the air and exceeding max speed, cap impulse and update stored value
      if (!isOnGround && currentSpeed > maxSpeed) {
        impulse = maxSpeed / 10;
        planeSpeeds.set(driver.name, impulse); // Update with capped speed
      }

      // Apply the impulse values to the vehicle only if conditions allow
      if (isOnGround || !driver.getDynamicProperty("canTakeOff")) {
        planeSpeeds.set(driver.name, impulse); // Update player's speed in the map
      }

      if (isFlying) {
        impulse = Math.max(0.08, impulse);
        planeSpeeds.set(driver.name, impulse); // Update player's speed in the map
      }

      planeSpeeds.set(driver.name, impulse); // Update player's speed in the map
      // After working out the amount of impulse, apply it to the vehicle
      applyAcceleration(vehicle, driver, impulse);

      if (takeoffSpeed(driver, vehicle, hasFuel, currentSpeed, vehicleData)) {
        // Plane flight
        takeOffFlight(driver, vehicle, hasFuel);
      } else {
        vehicle?.addEffect("slow_falling", 20, {
          amplifier: 1,
          showParticles: false,
        });
      }

      // Show speed/max/impulse if has dev tag
      if (driver.hasTag("pb_dev_info")) {
        driver.onScreenDisplay.setActionBar(
          `Speed: ${currentSpeed.toFixed(2)} / Max: ${maxSpeed.toFixed(
            2
          )} / Impulse: ${impulse.toFixed(
            2
          )} / Alt: ${vehicle.location.y.toFixed(0)} / V.Ang: ${driver
            ?.getViewDirection()
            .y.toFixed(2)} \ Fly: ${isFlying}`
        );
      }
    } else {
      // If player is not in a vehicle, reset their speed to 0 in the map
      // This stops the vehicle from storing its speed when a player exits whilst moving
      planeSpeeds.set(player.name, 0);
    }
  }
});
