package org.allaymc.server.command.selector;

import lombok.experimental.UtilityClass;
import org.allaymc.api.command.selector.SelectorSyntaxException;
import org.allaymc.server.AllayServer;

/**
 * ParseUtils provides some commonly used static functions about target selector parsing.
 */
@UtilityClass
public class ParseUtils {

    /**
     * Parse the offset int value
     *
     * @param value Text
     * @param base  Base value
     * @return Offset value
     */
    public static int parseOffsetInt(String value, int base) throws SelectorSyntaxException {
        try {
            if (value.startsWith("~")) {
                return value.length() != 1 ? base + Integer.parseInt(value.substring(1)) : base;
            } else {
                return Integer.parseInt(value);
            }
        } catch (NumberFormatException e) {
            throw new SelectorSyntaxException("Error parsing target selector", e);
        }
    }

    /**
     * Parse the offset double value
     *
     * @param value Text
     * @param base  Base value
     * @return Offset value
     */
    public static float parseOffsetFloat(String value, float base) throws SelectorSyntaxException {
        try {
            if (value.startsWith("~")) {
                return value.length() != 1 ? base + Float.parseFloat(value.substring(1)) : base;
            } else {
                return Float.parseFloat(value);
            }
        } catch (NumberFormatException e) {
            throw new SelectorSyntaxException("Error parsing target selector", e);
        }
    }

    /**
     * Parse the offset double value
     *
     * @param value Text
     * @param base  Base value
     * @return Offset value
     */
    public static double parseOffsetDouble(String value, double base) throws SelectorSyntaxException {
        try {
            if (value.startsWith("~")) {
                return value.length() != 1 ? base + Double.parseDouble(value.substring(1)) : base;
            } else {
                return Double.parseDouble(value);
            }
        } catch (NumberFormatException e) {
            throw new SelectorSyntaxException("Error parsing target selector", e);
        }
    }

    /**
     * Check if the parameter is reversed
     *
     * @param value Given string
     * @return Whether it is reversed
     */
    public static boolean checkReversed(String value) {
        return value.startsWith("!");
    }

    /**
     * Require the parameter not to be reversed. If reversed, throw {@link SelectorSyntaxException}
     *
     * @param value Given string
     */
    public static void cannotReversed(String value) throws SelectorSyntaxException {
        if (checkReversed(value)) {
            throw new SelectorSyntaxException("Argument cannot be reversed!");
        }
    }

    /**
     * Require the parameter not to be more than 1
     *
     * @param args    Parameter list
     * @param keyName Parameter key name
     */
    public static void singleArgument(String[] args, String keyName) throws SelectorSyntaxException {
        if (args.length > 1) {
            throw new SelectorSyntaxException("Multiple arguments is not allow in arg " + keyName);
        }
    }

    /**
     * Check if the given value is between the given two numbers
     *
     * @param bound1 Boundary 1
     * @param bound2 Boundary 2
     * @param value  The value
     * @return Whether the given value is between the given two numbers
     */
    public static boolean checkBetween(double bound1, double bound2, double value) {
        return bound1 < bound2 ?
                (value >= bound1 && value <= bound2) :
                (value >= bound2 && value <= bound1);
    }

    /**
     * Parse the game mode number from the given game mode string
     * <p>
     * This method matches the parameter with the predefined value of the original selector parameter m
     *
     * @param token String
     * @return Game mode number
     */
    public static int parseGameMode(String token) throws SelectorSyntaxException {
        return switch (token) {
            case "s", "survival", "0" -> 0;
            case "c", "creative", "1" -> 1;
            case "a", "adventure", "2" -> 2;
            case "spectator", "6" -> 6;
            case "d", "default" -> AllayServer.getSettings().genericSettings().defaultGameMode().ordinal();
            default -> throw new SelectorSyntaxException("Unknown GameMode token: " + token);
        };
    }
}
