# -*- coding: utf-8 -*-
"""Authors: David Bondesson, OverLordGoldDragon

Ridge extraction on signals with varying time-frequency characteristics.
"""
if __name__ != '__main__':
    raise Exception("ran example file as non-main")

import numpy as np
import scipy.signal as sig
from ssqueezepy import ssq_cwt, ssq_stft, extract_ridges, TestSignals
from ssqueezepy.visuals import plot, imshow

#%%## Visual methods #########################################################
def viz(x, Tf, ridge_idxs, yticks=None, ssq=False, transform='cwt', show_x=True):
    if show_x:
        plot(x, title="x(t)", show=1,
             xlabel="Time [samples]", ylabel="Signal Amplitude [A.U.]")

    ylabel = ("Frequency scales [1/Hz]" if (transform == 'cwt' and not ssq) else
              "Frequencies [Hz]")
    title = "abs({}{}) w/ ridge_idxs".format("SSQ_" if ssq else "",
                                             transform.upper())

    ikw = dict(abs=1, cmap='turbo', yticks=yticks, title=title)
    pkw = dict(linestyle='--', color='k', xlabel="Time [samples]", ylabel=ylabel,
               xlims=(0, Tf.shape[1]))

    imshow(Tf, **ikw, show=0)
    plot(ridge_idxs, **pkw, show=1)


def tf_transforms(x, t, wavelet='morlet', window=None, padtype='wrap',
                  penalty=.5, n_ridges=2, cwt_bw=15, stft_bw=15,
                  ssq_cwt_bw=4, ssq_stft_bw=4):
    kw_cwt  = dict(t=t, padtype=padtype)
    kw_stft = dict(fs=1/(t[1] - t[0]), padtype=padtype, flipud=1)
    Twx, Wx, ssq_freqs_c, scales, *_ = ssq_cwt(x,  wavelet, **kw_cwt)
    Tsx, Sx, ssq_freqs_s, Sfs, *_    = ssq_stft(x, window,  **kw_stft)
    Sx, Sfs = Sx[::-1], Sfs[::-1]

    ckw = dict(penalty=penalty, n_ridges=n_ridges, transform='cwt')
    skw = dict(penalty=penalty, n_ridges=n_ridges, transform='stft')
    cwt_ridges      = extract_ridges(Wx,  scales,      bw=cwt_bw,      **ckw)
    ssq_cwt_ridges  = extract_ridges(Twx, ssq_freqs_c, bw=ssq_cwt_bw,  **ckw)
    stft_ridges     = extract_ridges(Sx,  Sfs,         bw=stft_bw,     **skw)
    ssq_stft_ridges = extract_ridges(Tsx, ssq_freqs_s, bw=ssq_stft_bw, **skw)

    viz(x, Wx,  cwt_ridges,      scales,      ssq=0, transform='cwt',  show_x=1)
    viz(x, Twx, ssq_cwt_ridges,  ssq_freqs_c, ssq=1, transform='cwt',  show_x=0)
    viz(x, Sx,  stft_ridges,     Sfs,         ssq=0, transform='stft', show_x=0)
    viz(x, Tsx, ssq_stft_ridges, ssq_freqs_s, ssq=1, transform='stft', show_x=0)

#%%# Basic example ###########################################################
# Example ridge from similar example as can be found at MATLAB:
# https://www.mathworks.com/help/wavelet/ref/wsstridge.html#bu6we25-penalty
test_matrix = np.array([[1, 4, 4], [2, 2, 2], [5, 5, 4]])
fs_test = np.exp([1, 2, 3])

ridge_idxs, *_ = extract_ridges(test_matrix, fs_test, penalty=2.0,
                                get_params=True)
print("Ridge follows indexes:", ridge_idxs)
assert np.allclose(ridge_idxs, np.array([[2, 2, 2]]))

#%%# sin + cos ###############################################################
N, f1, f2 = 513, 5, 20
padtype = 'wrap'
penalty = 20

t  = np.linspace(0, 1, N, endpoint=True)
x1 = np.sin(2*np.pi * f1 * t)
x2 = np.cos(2*np.pi * f2 * t)
x = x1 + x2

tf_transforms(x, t, padtype=padtype, penalty=penalty)

#%%# Linear + quadratic chirp ################################################
N = 513
penalty = 20
padtype = 'reflect'

t  = np.linspace(0, 20, N, endpoint=True)
x1 = sig.chirp(t, f0=2,  f1=8, t1=20, method='linear')
x2 = sig.chirp(t, f0=.4, f1=4, t1=20, method='quadratic')
x = x1 + x2

tf_transforms(x, t, padtype=padtype, stft_bw=4, penalty=penalty)

#%%# Cubic polynomial frequency variation + pure tone ########################
N, f = 257, 0.5
padtype = 'wrap'
penalty = 20

t  = np.linspace(0, 10, N, endpoint=True)
p1 = np.poly1d([0.025, -0.36, 1.25, 2.0])
p3 = np.poly1d([0.01, -0.25, 1.5, 4.0])
x1 = sig.sweep_poly(t, p1)
x3 = sig.sweep_poly(t, p3)
x2 = np.sin(2*np.pi * f * t)
x = x1 + x2 + x3
# x += np.sqrt(1) * np.random.randn(len(x))

tf_transforms(x, t, n_ridges=3, padtype=padtype, stft_bw=4, ssq_stft_bw=4,
              penalty=penalty)

#%%# Reflect-added linear chirps #############################################
N = 512
penalty = 2

tsigs = TestSignals(N)
x, t = tsigs.lchirp(N)
x += x[::-1]

tf_transforms(x, t, penalty=penalty, cwt_bw=10)

#%%# Parallel F.M. linear chirps  ############################################
N = 512
penalty = 2
padtype = 'reflect'

tsigs = TestSignals(N)
x, t = tsigs.par_lchirp(N)

tf_transforms(x, t, penalty=penalty, padtype=padtype)
