import numpy as np
from scipy.optimize import curve_fit


def calc_and_draw_fit(func, plot_function, x_values, y_values, y_std, weg):
    popt, perr = calc_fit(func, x_values, y_values, y_std, weg)
    plot_fit(func, plot_function, x_values, popt)
    return popt, perr


def calc_fit(func, x_values, y_values, y_std, weg):
    """Man gibt die gewünschte Funktion und Datenpunkte ein, Fit wird berechnet
    gibt optimierte Parameter popt mit der Unsicherheit perr zurück"""
    # anfangsparameter
    # bei alter methode: V, Km, Ki ... schwer zu raten
    # bei mod methode: vmax, Smax, Ki
    initial_guess = np.array([np.max(y_values), 1, 2])
    # calculate the fit
    # Define parameter bounds: (lower_bounds, upper_bounds)
    lower_bounds = [0, 0, 0]
    # Min values for vmax, Smax, Ki
    # Ki can not be zero or negative, and Ki>
    upper_bounds = [np.inf, np.inf, np.inf]  # Max values for vmax, Smax, Ki

    if weg > 0:
        if y_std is None:
            popt, pcov = curve_fit(
                func,
                x_values[:-weg],
                y_values[:-weg],
                p0=initial_guess,
                bounds=(lower_bounds, upper_bounds),
            )
        else:
            popt, pcov = curve_fit(
                func,
                x_values[:-weg],
                y_values[:-weg],
                p0=initial_guess,
                sigma=y_std[:-weg],
                bounds=(lower_bounds, upper_bounds),
            )
    else:
        popt, pcov = curve_fit(
            func,
            x_values,
            y_values,
            p0=initial_guess,
            sigma=y_std,
            bounds=(lower_bounds, upper_bounds),
        )
    perr = np.sqrt(np.diag(pcov))
    return popt, perr


def plot_fit(func, plot_function, x_values, popt):
    x_fit = np.arange(x_values[-1], step=x_values[-1] / 1000)
    plot_function(x_fit, func(x_fit, *popt), legend_label_y="fit")
