package p.studio.compiler.pbs.parser;

import p.studio.compiler.pbs.ast.PbsAst;
import p.studio.compiler.pbs.lexer.PbsTokenKind;

final class PbsExprLiteralSupport {
    private final PbsExprParserContext context;
    private final PbsTokenCursor cursor;

    PbsExprLiteralSupport(final PbsExprParserContext context) {
        this.context = context;
        this.cursor = context.cursor();
    }

    PbsAst.Expression parseLiteralExpression() {
        if (cursor.match(PbsTokenKind.TRUE)) {
            final var token = cursor.previous();
            return new PbsAst.BoolLiteralExpr(true, context.span(token.start(), token.end()));
        }
        if (cursor.match(PbsTokenKind.FALSE)) {
            final var token = cursor.previous();
            return new PbsAst.BoolLiteralExpr(false, context.span(token.start(), token.end()));
        }
        if (cursor.match(PbsTokenKind.INT_LITERAL)) {
            final var token = cursor.previous();
            return new PbsAst.IntLiteralExpr(parseLongOrDefault(token.lexeme()), context.span(token.start(), token.end()));
        }
        if (cursor.match(PbsTokenKind.FLOAT_LITERAL)) {
            final var token = cursor.previous();
            return new PbsAst.FloatLiteralExpr(parseDoubleOrDefault(token.lexeme()), context.span(token.start(), token.end()));
        }
        if (cursor.match(PbsTokenKind.BOUNDED_LITERAL)) {
            final var token = cursor.previous();
            final var raw = token.lexeme().substring(0, Math.max(token.lexeme().length() - 1, 0));
            return new PbsAst.BoundedLiteralExpr(parseIntOrDefault(raw), context.span(token.start(), token.end()));
        }
        if (cursor.match(PbsTokenKind.STRING_LITERAL)) {
            final var token = cursor.previous();
            return new PbsAst.StringLiteralExpr(unescapeString(token.lexeme()), context.span(token.start(), token.end()));
        }
        return null;
    }

    private long parseLongOrDefault(final String text) {
        try {
            return Long.parseLong(text);
        } catch (NumberFormatException ignored) {
            return 0L;
        }
    }

    private int parseIntOrDefault(final String text) {
        try {
            return Integer.parseInt(text);
        } catch (NumberFormatException ignored) {
            return 0;
        }
    }

    private double parseDoubleOrDefault(final String text) {
        try {
            return Double.parseDouble(text);
        } catch (NumberFormatException ignored) {
            return 0.0;
        }
    }

    private String unescapeString(final String lexeme) {
        if (lexeme.length() < 2) {
            return "";
        }
        final var raw = lexeme.substring(1, lexeme.length() - 1);
        final var sb = new StringBuilder(raw.length());
        for (int i = 0; i < raw.length(); i++) {
            final char c = raw.charAt(i);
            if (c != '\\' || i + 1 >= raw.length()) {
                sb.append(c);
                continue;
            }
            final char next = raw.charAt(++i);
            switch (next) {
                case 'n' -> sb.append('\n');
                case 'r' -> sb.append('\r');
                case 't' -> sb.append('\t');
                case '"' -> sb.append('"');
                case '\\' -> sb.append('\\');
                default -> sb.append('\\').append(next);
            }
        }
        return sb.toString();
    }
}
