from __future__ import annotations

import numpy as np
import scikit_fluorescence as skf


def scale_decay_model_to_experiment(
        model_decay: np.ndarray,
        experimental_decay: np.ndarray,
        weights: np.ndarray = None,
        start: int = 0,
        stop: int = None,
        experimental_background: float = 0.0,
        scale_inplace: bool = True
) -> float:
    """Scales a model decay to an experimental decay considering the noise and
    a constant background in experimental data for a specified range.

    A scaling factor for a model decay and an experimental decay is computed such,
    that the number of photons in the model decay match the number of photons
    in the experimental decay considering the uncertainty of the experimental
    decay that is provided as parameter.

    If the weights of the experimental decay are not provided as an input the
    weights are calculated assuming Poisson distributed counts in the
    experimental decay.

    The scaling factor is computed for a range specified by `start` and `stop`.

    Parameters
    ----------
    model_decay : numpy.array
        Model decay for that a scaling factor is computed.
    experimental_decay : numpy.array
        Experimental decay to which the model decay provided by `model_decay`
        is scaled by the returned floating number
    weights : numpy.array
        Weights of the experimental decay that are used scale the model to
        the experiment. In case no weights are provided, the weights are
        computed assuming a Poisson noise in the experimental data.
    experimental_background : float
        Constant offset in the experimental data that is subtracted from the
        experimental decay
    start : int
        Start index that defines the range in which the model decay is scaled
        to the experimental decay
    stop : int
        Stop index that defines the range in which the model decay is scaled
        to the experimental decay.
    scale_inplace : bool
        If True (default) the experimental decay provided by the parameter
        `experimental_decay` is scaled inplace (modified) to the experimental
        decay.

    Returns
    -------
    float
        The scaling factor that was used to scale the model function to the
        experimental decay.


    Examples
    --------


    """
    if stop is None:
        stop = len(model_decay)
    scale = chisurf.fluorescence.tcspc.rescale_w_bg(
        model_decay=model_decay,
        experimental_decay=experimental_decay,
        experimental_weights=weights,
        experimental_background=experimental_background,
        start=start,
        stop=stop
    )
    if scale_inplace:
        model_decay *= scale
    return scale


def convolve_function(
        lifetime_spectrum: np.ndarray,
        instrument_response_function: np.ndarray,
        dt: float,
        lamp_background: float,
        time_shift: float,
        convolution_start: int,
        convolution_stop: int,
        repetition_rate: float,
        experimental_time_axis: np.ndarray,
        experimental_counts: np.ndarray,
        number_of_photons: float,
        scale_to_experimental_decay: bool,
        do_convolution: bool,
        convolution_mode: str,
        verbose: bool = False,
        auto_scale: bool = True
) -> np.ndarray:
    """

    Parameters
    ----------
    verbose
    lifetime_spectrum
    instrument_response_function
    dt
    lamp_background
    time_shift
    convolution_start
    convolution_stop
    repetition_rate
    experimental_fluorescence_decay
    number_of_photons
    scale_to_experimental_decay
    do_convolution
    convolution_mode

    Returns
    -------

    """

    irf = instrument_response_function
    data = experimental_counts

    # Make sure used IRF is of same size as data-array
    irf_y = np.resize(irf, data.shape)
    n_points = irf_y.shape[0]

    # Make sure that the start / stop are reasonable range
    stop = min(convolution_stop, n_points)
    start = min(0, convolution_start)
    n_points = stop - start

    if convolution_mode == "per":
        period = 1000. / repetition_rate
        skf.tcspc.convolve.convolve_lifetime_spectrum_periodic(
            decay=lifetime_spectrum,
            lifetime_spectrum=lifetime_spectrum,
            irf=instrument_response_function,
            start=start,
            stop=stop,
            n_points=n_points,
            period=period,
            dt=dt,
            conv_stop=stop
        )
    elif convolution_mode == "exp":
        t = data
        chisurf.fluorescence.tcspc.convolve.convolve_lifetime_spectrum(
            lifetime_spectrum=lifetime_spectrum,
            decay=data,
            irf=irf_y,
            stop=stop,
            time_axis=t
        )
    elif convolution_mode == "full":
        decay = np.convolve(
            data,
            irf_y,
            mode="full"
        )[:n_points]

    if verbose:
        print("------------")
        print("Convolution:")
        print("Lifetimes: %s" % data)
        print("dt: %s" % dt)
        print("Irf: %s" % irf.name)
        print("Stop: %s" % stop)
        print("dt: %s" % dt)
        print("Convolution mode: %s" % convolution_mode)
