import pandas as pd
from dataclasses import dataclass, fields
import numpy as np


# datova trida pro vypocteni a uchovani statistickych parametru
@dataclass
class StatsData:
    mean: float = 0
    var: float = 0
    stdev: float = 0
    median: float = 0
    perc1: float = 0
    perc99: float = 0
    quart1: float = 0
    quart3: float = 0

    # vola funkce pro vypocteni statistickcyh parametru
    def get(self, feat):
        self.mean = mean(feat)
        self.var = var(feat)
        self.stdev = stdev(feat)
        self.median = median(feat)
        self.perc1 = perc1(feat)
        self.perc99 = perc99(feat)
        self.quart1 = quart1(feat)
        self.quart3 = quart3(feat)

    # zplostuje datovou tridu do vektoru pro potreby ukladani do souboru
    def flatten(self):
        return list([self.mean,
                     self.var,
                     self.stdev,
                     self.median,
                     self.perc1,
                     self.perc99,
                     self.quart1,
                     self.quart3])

    # opak predchozi funkce
    def to_class(self, array):
        self.mean = array[0]
        self.var = array[1]
        self.stdev = array[2]
        self.median = array[3]
        self.perc1 = array[4]
        self.perc99 = array[5]
        self.quart1 = array[6]
        self.quart3 = array[7]


# funkce pro vypocet statistickych udaju
def mean(feat):
    return np.nanmean(feat)


def var(feat):
    return np.nanvar(feat)


def stdev(feat):
    return np.nanstd(feat)


def median(feat):
    return np.nanmedian(feat)


def perc1(feat):
    return np.nanpercentile(feat, 1)


def perc99(feat):
    return np.nanpercentile(feat, 99)


def quart1(feat):
    return np.nanquantile(feat, 0.25)


def quart3(feat):
    return np.nanquantile(feat, 0.75)


# datova trida pro uchovani statistickcyh udaju pro extrahovane parametry
@dataclass
class FeatsStats:
    rms: StatsData = StatsData()
    tempo: StatsData = StatsData()
    spec_cent: StatsData = StatsData()
    spec_band: StatsData = StatsData()
    spec_roll: StatsData = StatsData()
    zero: StatsData = StatsData()
    plp: StatsData = StatsData()
    pyin: StatsData = StatsData()
    mfcc02: StatsData = StatsData()
    mfcc03: StatsData = StatsData()
    mfcc04: StatsData = StatsData()
    mfcc05: StatsData = StatsData()
    mfcc06: StatsData = StatsData()
    mfcc07: StatsData = StatsData()
    mfcc08: StatsData = StatsData()
    mfcc09: StatsData = StatsData()
    mfcc10: StatsData = StatsData()
    mfcc11: StatsData = StatsData()
    mfcc12: StatsData = StatsData()
    mfcc13: StatsData = StatsData()

    # vola funkce pro vypocteni statistickcyh parametru pro kazdy extrahovany parametr
    def get(self, feat):
        self.rms.get(feat.rms)
        self.tempo.get(feat.tempo)
        self.spec_cent.get(feat.spec_cent)
        self.spec_band.get(feat.spec_band)
        self.spec_roll.get(feat.spec_roll)
        self.zero.get(feat.zero)
        self.plp.get(feat.plp)
        self.pyin.get(feat.pyin)
        self.mfcc02.get(feat.mfcc[1])
        self.mfcc03.get(feat.mfcc[2])
        self.mfcc04.get(feat.mfcc[3])
        self.mfcc05.get(feat.mfcc[4])
        self.mfcc06.get(feat.mfcc[5])
        self.mfcc07.get(feat.mfcc[6])
        self.mfcc08.get(feat.mfcc[7])
        self.mfcc09.get(feat.mfcc[8])
        self.mfcc10.get(feat.mfcc[9])
        self.mfcc11.get(feat.mfcc[10])
        self.mfcc12.get(feat.mfcc[11])
        self.mfcc13.get(feat.mfcc[12])

    # uklada statisticke paramatry do CSV
    # path - cesta k souboru
    def save(self, path):
        pd.DataFrame({'PAR': ['MEAN', 'VAR', 'STDEV', 'MEDIAN', 'PERC1', 'PERC99', 'QUART1', 'QUART3'],
                      'RMS': self.rms.flatten(),
                      'TEMPO': self.tempo.flatten(),
                      'SP_CENT': self.spec_cent.flatten(),
                      'SP_BAND': self.spec_band.flatten(),
                      'SP_ROLL': self.spec_roll.flatten(),
                      'ZERO': self.zero.flatten(),
                      'PLP': self.plp.flatten(),
                      'PYIN': self.pyin.flatten(),
                      'MFCC_02': self.mfcc02.flatten(),
                      'MFCC_03': self.mfcc03.flatten(),
                      'MFCC_04': self.mfcc04.flatten(),
                      'MFCC_05': self.mfcc05.flatten(),
                      'MFCC_06': self.mfcc06.flatten(),
                      'MFCC_07': self.mfcc07.flatten(),
                      'MFCC_08': self.mfcc08.flatten(),
                      'MFCC_09': self.mfcc09.flatten(),
                      'MFCC_10': self.mfcc10.flatten(),
                      'MFCC_11': self.mfcc11.flatten(),
                      'MFCC_12': self.mfcc12.flatten(),
                      'MFCC_13': self.mfcc13.flatten()
                      }).to_csv(path, index=False)

    # nacite statisticke parametry ze souboru
    # path - cesta k souboru
    def load(self, path):
        csv = pd.read_csv(path)
        self.rms.to_class(csv['RMS'].to_numpy())
        self.tempo.to_class(csv['TEMPO'].to_numpy())
        self.spec_cent.to_class(csv['SP_CENT'].to_numpy())
        self.spec_band.to_class(csv['SP_BAND'].to_numpy())
        self.spec_roll.to_class(csv['SP_ROLL'].to_numpy())
        self.zero.to_class(csv['ZERO'].to_numpy())
        self.plp.to_class(csv['PLP'].to_numpy())
        self.pyin.to_class(csv['PYIN'].to_numpy())
        self.mfcc02.to_class(csv['MFCC_02'].to_numpy())
        self.mfcc03.to_class(csv['MFCC_03'].to_numpy())
        self.mfcc04.to_class(csv['MFCC_04'].to_numpy())
        self.mfcc05.to_class(csv['MFCC_05'].to_numpy())
        self.mfcc06.to_class(csv['MFCC_06'].to_numpy())
        self.mfcc07.to_class(csv['MFCC_07'].to_numpy())
        self.mfcc08.to_class(csv['MFCC_08'].to_numpy())
        self.mfcc09.to_class(csv['MFCC_09'].to_numpy())
        self.mfcc10.to_class(csv['MFCC_10'].to_numpy())
        self.mfcc11.to_class(csv['MFCC_11'].to_numpy())
        self.mfcc12.to_class(csv['MFCC_12'].to_numpy())
        self.mfcc13.to_class(csv['MFCC_13'].to_numpy())

    # vraci stredni hodnoty parametru
    def get_mean(self):
        means = []
        names = []
        for field in fields(self):
            means.append(self.__dataclass_fields__[field.name].default.mean)
            names.append(field.name)
        return means, names

    # vraci rozptyl parametru
    def get_var(self):
        vars = []
        names = []
        for field in fields(self):
            vars.append(self.__dataclass_fields__[field.name].default.var)
            names.append(field.name)
        return vars, names

    # vraci smerodatnou odchylku parametru
    # set - vybira podmnozinu parametru na zaklade predchozi analyzy korelacnich koeficientu
    def get_stdev(self, set=None):
        stdevs = []
        names = []
        for field in fields(self):
            if field.name in ('spec_band', 'spec_roll', 'zero', 'mfcc02', 'mfcc04', 'mfcc05', 'mfcc13')\
                    and set == 'corr':
                continue
            else:
                stdevs.append(self.__dataclass_fields__[field.name].default.stdev)
                names.append(field.name)
        return stdevs, names


