content: package pers.neige.neigeitems.command.coordinates;
  
  import com.mojang.brigadier.StringReader;
  import com.mojang.brigadier.exceptions.CommandSyntaxException;
  import lombok.val;
  import org.bukkit.Location;
  import org.bukkit.World;
  import org.bukkit.command.CommandSender;
  import org.bukkit.entity.Player;
  import org.bukkit.util.Vector;
  import org.jetbrains.annotations.Nullable;
  import pers.neige.neigeitems.command.CommandUtils;
  
  public class Coordinates {
      private final @Nullable Coordinate x;
      private final @Nullable Coordinate y;
      private final @Nullable Coordinate z;
      private final boolean noResult;
      private final boolean local;
  
      public Coordinates(@Nullable Coordinate x, @Nullable Coordinate y, @Nullable Coordinate z) {
          this.x = x;
          this.y = y;
          this.z = z;
          if (this.x == null || this.y == null || this.z == null) {
              noResult = true;
              local = false;
          } else {
              noResult = false;
              local = (this.x.getType() == LocationType.LOCAL && this.y.getType() == LocationType.LOCAL && this.z.getType() == LocationType.LOCAL);
          }
      }
  
      public static Coordinates parse(StringReader reader) throws CommandSyntaxException {
          int i = reader.getCursor();
          val x = readCoordinate(reader);
          if (reader.canRead() && reader.peek() == ' ') reader.skip();
          val y = readCoordinate(reader);
          if (reader.canRead() && reader.peek() == ' ') reader.skip();
          val z = readCoordinate(reader);
          return new Coordinates(x, y, z);
      }
  
      private static Coordinate readCoordinate(StringReader reader) throws CommandSyntaxException {
          if (!reader.canRead()) {
              return new Coordinate(LocationType.ABSOLUTE, 0.0);
          }
          LocationType type;
          if (reader.peek() == '~') {
              reader.skip();
              type = LocationType.RELATIVE;
          } else if (reader.peek() == '^') {
              reader.skip();
              type = LocationType.LOCAL;
          } else {
              type = LocationType.ABSOLUTE;
          }
          val value = (!reader.canRead() || reader.peek() == ' ') ? Double.valueOf(0.0) : CommandUtils.readDouble(reader);
          return new Coordinate(type, value);
      }
  
      public @Nullable Location getLocation(World world, CommandSender sender, Player target) {
          if (noResult) return null;
          if (sender instanceof Player || target != null) {
              Player player;
              if (target != null) {
                  player = target;
              } else {
                  player = (Player) sender;
              }
              if (local) {
                  val location = player.getEyeLocation();
                  val leftwards = x.getValue();
                  val upwards = y.getValue();
                  val forwards = z.getValue();
  
                  val rotation = location.getDirection();
                  val anchor = location.toVector();
  
                  val forward = rotation.clone().normalize();
                  val left = forward.clone().crossProduct(new Vector(0, -1, 0)).normalize();
                  val up = left.clone().crossProduct(forward).normalize();
  
                  val position = forward.multiply(forwards)
                          .add(up.multiply(-upwards))
                          .add(left.multiply(leftwards));
  
                  position.add(anchor);
  
                  return position.toLocation(world);
              } else {
                  val location = player.getLocation();
                  return new Location(world, x.get(location.getX()), y.get(location.getY()), z.get(location.getZ()));
              }
          } else {
              return new Location(world, x.get(0), y.get(0), z.get(0));
          }
      }
  }
  