import numpy as np


class CoreMath:

    def __init__(self, base_df, params=None):
        """
        Этот класс нужен для того, чтобы проводить операции над датафреймами
        :param base_df: pandas.DataFrame , датафрейм, над которым будут проведены математические операции
        :param params: словарь, который определяет какие данные пришли в класс, и что с ними нужно делать, и как
        """
        if params is None:
            params = {
                'dataType': 'ohcl',
                'action': None,
                'actionOptions': {}
            }

        # нужно переопределить индексы, потому что нам ничего не известно об индексации входного файла
        self.base_df = base_df.reset_index(drop=True)

        self.params = params

        # Эта часть определяет с какой частью данных нужно проводить вычисления
        if self.params['dataType']=='ohcl':
            self.col=self.base_df[self.params['actionOptions']['valueType']]
        elif self.params['dataType']=='series':
            self.col=self.base_df

        # собственно производим вычисления
        self.ans=self.getAns()

    def getAns(self):
        """
        Эта функция занимается собственно рутингом вычислений, в зависимости от параметров
        :return: ans, неопределенный тип данных, в заивисимости от action
        """
        ans=None

        # в зависимости от параметра action производятся соответсвующие действия
        if self.params['action']=='findExt':
            ans = self.getExtremumValue()
        elif self.params['action']=='findMean':
            ans = self.getMeanValue()
        elif self.params['action']=='findSTD':
            ans = self.getSTD()

        return ans


    def getExtremumValue(self):
        """
        Эта функция возвращает экстремум произвольного типа внутри одного столбца
        Тип контролируется разделом внутри словаря параметров `self.params` по ключу `actionOptions`:
            'extremumtype': -- тип экстремума
                'min'
                'max'
        :return ans, экстремум произвольного типа
        """
        ans=None
        if self.params['actionOptions']['extremumtype']=='max':
            ans=max(self.col)

        if self.params['actionOptions']['extremumtype']=='min':
            ans=min(self.col)

        return ans

    def getMeanValue(self):
        """
        Божественный код
        Эта функция возвращает среднее значение одного из следующих типов.
        Для определения типа используется словарь `self.params`, по ключу `actionOptions`, релевантные ключи выглядят
        так:
            'MeanType':
                'MA' -- среднее по всему столбцу
                'SMA'  -- скользящее среднее
                'EMA' -- экспоненциальное скользящее среднее
                'WMA' -- взвешенное скользящее среднее
            'window' -- размер окна
            'span' -- >=1 , аналог окна для экспоненциального среднего, чем он больше тем меньше коэффициент сглаживания
            'weights' -- numpy.ndarray, список размером в параметр `window`, конкретные веса для каждого элемента
        """

        ans=None

        if self.params['actionOptions']['MeanType']=='MA':
            ans = self.col.mean()
        if self.params['actionOptions']['MeanType']=='SMA':
            ans=np.convolve(self.col, np.ones(self.params['actionOptions']['window']), 'valid') / self.params['actionOptions']['window']
        if self.params['actionOptions']['MeanType']=='EMA':
            ans=self.col.ewm(span=self.params['actionOptions']['span'], adjust=False).mean().to_list()
        if self.params['actionOptions']['MeanType']=='WMA':
            try:
                weights=self.params['actionOptions']['weights']
            except KeyError:
                weights=np.arange(1,self.params['actionOptions']['window']+1)
            ans=self.col.rolling(window=self.params['actionOptions']['window']).apply(lambda x: np.sum(weights*x) / weights.sum(), raw=False).to_list()

        return ans

    def getSTD(self):
        '''
        actionOptions:
            window
        
        
        
        
        
        '''

        ans=None


        try:
            window=self.params['actionOptions']['window']
            ans=np.asarray([])
            for i in range(len(self.col)-window+1):
                ans=np.append(ans, np.std(self.col[i:i+window], ddof=1))

        except:
            #window = len(self.col)
            ans=np.std(self.col, ddof=1)

        return ans

