"""Mathematical expression evaluator with precedence (Punkt vor Strich).
Supports +, -, *, /, (, ), and decimal numbers. Safe – no eval()."""

import re
from decimal import Decimal, InvalidOperation

TOKEN_NUM = 'NUM'
TOKEN_PLUS = '+'
TOKEN_MINUS = '-'
TOKEN_MUL = '*'
TOKEN_DIV = '/'
TOKEN_LPAR = '('
TOKEN_RPAR = ')'

def tokenize(expr):
    tokens = []
    i = 0
    while i < len(expr):
        c = expr[i]
        if c in ' \t':
            i += 1
            continue
        if c in '+-*/()':
            tokens.append(c)
            i += 1
        elif c.isdigit() or c == '.':
            start = i
            had_dot = False
            while i < len(expr) and (expr[i].isdigit() or (expr[i] == '.' and not had_dot)):
                if expr[i] == '.':
                    had_dot = True
                i += 1
            tokens.append(('NUM', expr[start:i]))
        else:
            raise ValueError(f"Ungültiges Zeichen: '{c}'")
    return tokens

def parse_primary(tokens, pos):
    if pos >= len(tokens):
        raise ValueError("Erwarte Zahl oder (")
    tok = tokens[pos]
    if isinstance(tok, tuple) and tok[0] == 'NUM':
        return Decimal(tok[1]), pos + 1
    if tok == '(':
        val, pos = parse_expr(tokens, pos + 1)
        if pos >= len(tokens) or tokens[pos] != ')':
            raise ValueError("Fehlende schließende Klammer")
        return val, pos + 1
    raise ValueError(f"Unerwartetes Token: {tok}")

def parse_mul(tokens, pos):
    val, pos = parse_primary(tokens, pos)
    while pos < len(tokens):
        tok = tokens[pos]
        if tok in ('*', '/'):
            right, pos = parse_primary(tokens, pos + 1)
            if tok == '*':
                val *= right
            else:
                if right == 0:
                    raise ValueError("Division durch Null")
                val /= right
        else:
            break
    return val, pos

def parse_expr(tokens, pos):
    val, pos = parse_mul(tokens, pos)
    while pos < len(tokens):
        tok = tokens[pos]
        if tok in ('+', '-'):
            right, pos = parse_mul(tokens, pos + 1)
            if tok == '+':
                val += right
            else:
                val -= right
        else:
            break
    return val, pos

def berechne_formel(expr_str):
    if not expr_str or not expr_str.strip():
        return 0
    # Deutsche Dezimaltrenner ersetzen
    expr_str = expr_str.replace(',', '.')
    tokens = tokenize(expr_str.strip())
    if not tokens:
        return 0
    val, pos = parse_expr(tokens, 0)
    if pos != len(tokens):
        raise ValueError("Überflüssige Zeichen am Ende")
    return float(val)
