import nolds

import numpy as np
from sklearn.manifold import Isomap

from .utils import apply_sliding_window


def recurrence_quantification(signal=None, window_size=None, overlap=None, windows=None):
    """
    Estimate recurrence quantification measure (here using correlation dimension)
    for each window in the signal.

    :param signal: The input signal as a 1D array or list (if windows not provided).
    :type signal: array-like
    :param window_size: The number of samples in each window (if windows not provided).
    :type window_size: int
    :param overlap: The number of samples to overlap between consecutive windows (if windows not provided).
    :type overlap: int
    :param windows: Optional precomputed list of windows.
    :type windows: list
    :return: 1D array of recurrence quantification values (correlation dimension) for each window.
    :rtype: numpy.ndarray

    Reference: https://en.wikipedia.org/wiki/Recurrence_quantification_analysis
    """
    return apply_sliding_window(
        lambda x: nolds.corr_dim(x, emb_dim=2),
        windows=windows, signal=signal, window_size=window_size, overlap=overlap
    )


def attractor_reconstruction(signal=None, window_size=None, overlap=None, windows=None):
    """
    Reconstruct the attractor in phase space for each window using Isomap
    as a dimensionality reduction technique.

    :param signal: The input signal as a 1D array or list (if windows not provided).
    :type signal: array-like
    :param window_size: The number of samples in each window (if windows not provided).
    :type window_size: int
    :param overlap: The number of samples to overlap between consecutive windows (if windows not provided).
    :type overlap: int
    :param windows: Optional precomputed list of windows.
    :type windows: list
    :return: A NumPy array (or list) of 2D embeddings for each window.
    :rtype: numpy.ndarray

    Reference: https://en.wikipedia.org/wiki/Phase_space_reconstruction
    """
    return apply_sliding_window(
        lambda x: Isomap(n_components=2).fit_transform(x.reshape(-1, 1)),
        windows=windows, signal=signal, window_size=window_size, overlap=overlap
    )


def mean_crossing_rate(signal=None, window_size=None, overlap=None, windows=None):
    """
    Calculate the number of mean crossings for each window in the signal.
    This is similar to the zero-crossing rate but uses the window's mean instead of zero.

    :param signal: The input signal as a 1D array or list (if windows not provided).
    :type signal: array-like
    :param window_size: The number of samples in each window (if windows not provided).
    :type window_size: int
    :param overlap: The number of samples to overlap between consecutive windows (if windows not provided).
    :type overlap: int
    :param windows: Optional precomputed list of windows.
    :type windows: list
    :return: 1D array of mean crossing counts for each window.
    :rtype: numpy.ndarray

    Reference: https://en.wikipedia.org/wiki/Mean_crossing_rate
    """
    return apply_sliding_window(
        lambda x: np.sum(np.diff(x > np.mean(x)) != 0),
        windows=windows, signal=signal, window_size=window_size, overlap=overlap
    )


def max_slope(signal=None, window_size=None, overlap=None, windows=None):
    """
    Calculate the maximum slope (i.e., maximum of the first derivative) in each window
    of the signal.

    :param signal: The input signal as a 1D array or list (if windows not provided).
    :type signal: array-like
    :param window_size: The number of samples in each window (if windows not provided).
    :type window_size: int
    :param overlap: The number of samples to overlap between consecutive windows (if windows not provided).
    :type overlap: int
    :param windows: Optional precomputed list of windows.
    :type windows: list
    :return: 1D array of maximum slope values for each window.
    :rtype: numpy.ndarray

    Reference: https://en.wikipedia.org/wiki/Slope
    """
    return apply_sliding_window(
        lambda x: np.max(np.diff(x)),
        windows=windows, signal=signal, window_size=window_size, overlap=overlap
    )
