import numpy as np
import pandas as pd
from scipy.fft import fft, fftfreq

def get_mean_freq(signal, frame_size, hop_length):
    mean = []
    for i in range(0, len(signal), hop_length):
        L = len(signal[i:i+frame_size])
        y = abs(np.fft.fft(signal[i:i+frame_size]/L))[:int(L/2)]
        current_mean = np.sum(y)/frame_size
        mean.append(current_mean)
    return np.array(mean)

def get_variance_freq(signal, frame_size, hop_length):
    var = []
    for i in range(0, len(signal), hop_length):
        L = len(signal[i:i+frame_size])
        y = abs(np.fft.fft(signal[i:i+frame_size]/L))[:int(L/2)]
        current_var = (np.sum((y - (np.sum(y)/frame_size))**2))/(frame_size-1)
        var.append(current_var)
    return np.array(var)

def get_third_freq(signal, frame_size, hop_length):
    third = []
    for i in range(0, len(signal), hop_length):
        L = len(signal[i:i+frame_size])
        y = abs(np.fft.fft(signal[i:i+frame_size]/L))[:int(L/2)]
        current_third = (np.sum((y - (np.sum(y)/frame_size))**3))/(frame_size * (np.sqrt((np.sum((y - (np.sum(y)/frame_size))**2))/(frame_size-1)))**3)
        third.append(current_third)
    return np.array(third)

def get_forth_freq(signal, frame_size, hop_length):
    forth = []
    for i in range(0, len(signal), hop_length):
        L = len(signal[i:i+frame_size])
        y = abs(np.fft.fft(signal[i:i+frame_size]/L))[:int(L/2)]
        current_forth = (np.sum((y - (np.sum(y)/frame_size))**4))/(frame_size * ((np.sum((y - (np.sum(y)/frame_size))**2))/(frame_size-1))**2)
        forth.append(current_forth)
    return np.array(forth)

def get_grand_freq(signal, frame_size, hop_length):
    grand = []
    for i in range(0, len(signal), hop_length):
        L = len(signal[i:i+frame_size])
        y = abs(np.fft.fft(signal[i:i+frame_size]/L))[:int(L/2)]
        f = np.fft.fftfreq (L,.1/25600)[:int(L/2)] 
        current_grand = np.sum(f * y)/np.sum(y)
        grand.append(current_grand)
    return np.array(grand)

def get_std_freq(signal, frame_size, hop_length):
    std = []
    for i in range(0, len(signal), hop_length):
        L = len(signal[i:i+frame_size])
        y = abs(np.fft.fft(signal[i:i+frame_size]/L))[:int(L/2)]
        f = np.fft.fftfreq (L,.1/25600)[:int(L/2)] 
        current_std = np.sqrt(np.sum((f-(np.sum(f * y)/np.sum(y)))**2 * y)/frame_size)
        std.append(current_std)
    return np.array(std)

def get_Cfactor_freq(signal, frame_size, hop_length):
    cfactor = []
    for i in range(0, len(signal), hop_length):
        L = len(signal[i:i+frame_size])
        y = abs(np.fft.fft(signal[i:i+frame_size]/L))[:int(L/2)]
        f = np.fft.fftfreq (L,.1/25600)[:int(L/2)] 
        current_cfactor = np.sqrt(np.sum(f**2 * y)/np.sum(y))
        cfactor.append(current_cfactor)
    return np.array(cfactor)

def get_Dfactor_freq(signal, frame_size, hop_length):
    dfactor = []
    for i in range(0, len(signal), hop_length):
        L = len(signal[i:i+frame_size])
        y = abs(np.fft.fft(signal[i:i+frame_size]/L))[:int(L/2)]
        f = np.fft.fftfreq (L,.1/25600)[:int(L/2)] 
        current_dfactor = np.sqrt(np.sum(f**4 * y)/np.sum(f**2 * y))
        dfactor.append(current_dfactor)
    return np.array(dfactor)

def get_Efactor_freq(signal, frame_size, hop_length):
    efactor = []
    for i in range(0, len(signal), hop_length):
        L = len(signal[i:i+frame_size])
        y = abs(np.fft.fft(signal[i:i+frame_size]/L))[:int(L/2)]
        f = np.fft.fftfreq (L,.1/25600)[:int(L/2)] 
        current_efactor = np.sqrt(np.sum(f**2 * y)/np.sqrt(np.sum(y) * np.sum(f**4 * y)))
        efactor.append(current_efactor)
    return np.array(efactor)

def get_Gfactor_freq(signal, frame_size, hop_length):
    gfactor = []
    for i in range(0, len(signal), hop_length):
        L = len(signal[i:i+frame_size])
        y = abs(np.fft.fft(signal[i:i+frame_size]/L))[:int(L/2)]
        f = np.fft.fftfreq (L,.1/25600)[:int(L/2)] 
        current_gfactor = (np.sqrt(np.sum((f-(np.sum(f * y)/np.sum(y)))**2 * y)/frame_size))/(np.sum(f * y)/np.sum(y))
        gfactor.append(current_gfactor)
    return np.array(gfactor)

def get_third1_freq(signal, frame_size, hop_length):
    third1 = []
    for i in range(0, len(signal), hop_length):
        L = len(signal[i:i+frame_size])
        y = abs(np.fft.fft(signal[i:i+frame_size]/L))[:int(L/2)]
        f = np.fft.fftfreq (L,.1/25600)[:int(L/2)] 
        current_third1 = np.sum((f - (np.sum(f * y)/np.sum(y)))**3 * y)/(frame_size * (np.sqrt(np.sum((f-(np.sum(f * y)/np.sum(y)))**2 * y)/frame_size))**3)
        third1.append(current_third1)
    return np.array(third1)

def get_forth1_freq(signal, frame_size, hop_length):
    forth1 = []
    for i in range(0, len(signal), hop_length):
        L = len(signal[i:i+frame_size])
        y = abs(np.fft.fft(signal[i:i+frame_size]/L))[:int(L/2)]
        f = np.fft.fftfreq (L,.1/25600)[:int(L/2)] 
        current_forth1 = np.sum((f - (np.sum(f * y)/np.sum(y)))**4 * y)/(frame_size * (np.sqrt(np.sum((f-(np.sum(f * y)/np.sum(y)))**2 * y)/frame_size))**4)
        forth1.append(current_forth1)
    return np.array(forth1)

def get_Hfactor_freq(signal, frame_size, hop_length):
    hfactor = []
    for i in range(0, len(signal), hop_length):
        L = len(signal[i:i+frame_size])
        y = abs(np.fft.fft(signal[i:i+frame_size]/L))[:int(L/2)]
        f = np.fft.fftfreq (L,.1/25600)[:int(L/2)] 
        current_hfactor = np.sum(np.sqrt(abs(f - (np.sum(f * y)/np.sum(y)))) * y)/(frame_size * np.sqrt(np.sqrt(np.sum((f-(np.sum(f * y)/np.sum(y)))**2 * y)/frame_size)))
        hfactor.append(current_hfactor)
    return np.array(hfactor)

def get_Jfactor_freq(signal, frame_size, hop_length):
    jfactor = []
    for i in range(0, len(signal), hop_length):
        L = len(signal[i:i+frame_size])
        y = abs(np.fft.fft(signal[i:i+frame_size]/L))[:int(L/2)]
        f = np.fft.fftfreq (L,.1/25600)[:int(L/2)] 
        current_jfactor = np.sum(np.sqrt(abs(f - (np.sum(f * y)/np.sum(y)))) * y)/(frame_size * np.sqrt(np.sqrt(np.sum((f-(np.sum(f * y)/np.sum(y)))**2 * y)/frame_size)))
        jfactor.append(current_jfactor)
    return np.array(jfactor)

class FrequencyFeatureExtractor:
    def __init__(self, data):
        # Assuming data is a numpy array
        self.x = data
        # Perform FFT and compute magnitude of frequency components
        self.frequency_spectrum = np.abs(fft(self.x))
        self.n = len(self.frequency_spectrum)
        self.mean_freq = np.mean(self.frequency_spectrum)
        self.variance_freq = np.var(self.frequency_spectrum)
        self.std_freq = np.std(self.frequency_spectrum)

        # Calculate the required frequency features
        self.features = self.calculate_features()

    def calculate_features(self):
        S_mu = self.mean_freq
        S_MAX = np.max(self.frequency_spectrum)
        S_SBP = np.sum(self.frequency_spectrum)
        S_Peak = np.max(self.frequency_spectrum)
        S_V = np.sum((self.frequency_spectrum - S_mu) ** 2) / (self.n - 1)
        S_Sigma = np.sqrt(S_V)
        S_Skewness = np.sum((self.frequency_spectrum - S_mu) ** 3) / (self.n * S_Sigma ** 3)
        S_Kurtosis = np.sum((self.frequency_spectrum - S_mu) ** 4) / (self.n * S_Sigma ** 4)
        S_RSPPB = S_Peak / S_mu

        return {
            'Mean of band Power Spectrum (S_mu)': S_mu,
            'Max of band power spectrum (S_MAX)': S_MAX,
            'Sum of total band power (S_SBP)': S_SBP,
            'Peak of band power (S_Peak)': S_Peak,
            'Variance of band power (S_V)': S_V,
            'Standard Deviation of band power (S_Sigma)': S_Sigma,
            'Skewness of band power (S_Skewness)': S_Skewness,
            'Kurtosis of band power (S_Kurtosis)': S_Kurtosis,
            'Relative Spectral Peak per Band Power (S_RSPPB)': S_RSPPB
        }

    def __repr__(self):
        result = "Frequency Domain Feature Extraction Results:\n"
        for feature, value in self.features.items():
            result += f"{feature}: {value:.4f}\n"
        return result

def ExtractFrequencyFeatures(object):
    data = pd.read_csv(object, skiprows=1)  # Skip the header row separator char info
    extractor = FrequencyFeatureExtractor(data.iloc[:, 1].values)  # Assuming the data is in the second column
    features = extractor.features
    return features

# Usage Example
# extractor = FrequencyFeatureExtractor('path_to_your_data.csv')
# print(extractor)
