from emoji import emojize
import logging
import numpy as np
import os
from torchup.base.models import BaseModel
import yaml


class GlobalConfig(BaseModel):
    '''
    This class is a python representation of the global config file `global.yml`
    '''
    dump_attrs = [
        'author',
        'experiment_count',
        'log_dir',
        'supervisor',
        'universtiy',
        'torchup_version'
    ]

    def experiment_log_dir(self):
        '''
        This method generates a directory path for the current experiment.
        The directory name itself follows naming convention of `exp-{experiment_count}`.
        This method also performs a check, whether the directory has already been created
        in order to prevent collisions when spawning multiple torchup agents at the same
        time and automatically incremets its local `experiment_count`, checking whether
        the directory exists, until it doesn't. Finally, it returns the absolute path
        and the relative path to `config.yml`.
        '''

        # Get the relative path
        dir_name = ('exp-{}'.format(self.experiment_count))

        # construct absolute path
        abs_path = os.path.join(os.getcwd(), self.log_dir, dir_name)

        # If the directory has already been created, increment the experiment
        # number and check again. We use a simple assertion for this and recursively
        # call `experiment_log_dir`, in case the directory already exists.
        try:
            assert not os.path.isdir(abs_path)
            file_path = os.path.join(self.log_dir, dir_name, 'config.yml')
            return abs_path, file_path
        except AssertionError:
            self.increment_and_save()
            return self.experiment_log_dir()

    def increment_and_save(self, **kwargs):
        '''
        Doctring blablabal
        '''
        self.experiment_count += 1
        data_dict = self.to_dict()
        # data_dict['experiment_count'] += 1
        self.save(data_dict)

    def save(self, data_dict, filepath='torchup/logging/global.yml'):
        '''
        Dumps the current python representation of the global config to `global.yml`
        '''
        with open(filepath, 'w') as file_desc:
            try:
                yaml.dump(data_dict, file_desc, default_flow_style=False)
            except Exception as e:
                logging.error('Encountered error while writing to global.yml')

    def __str__(self):
        return (emojize('\n :book:  Experiment Run No. {}\n :syringe:  Performed by {}'
                .format(self.experiment_count, self.author), use_aliases=True))

    def __repr__(self):
        return (emojize('\n :book:  Experiment Run No. {}\n :syringe:  Performed by {}'
                .format(self.experiment_count, self.author), use_aliases=True))


class ExperimentConfig(BaseModel):
    '''
    iein cleverer docstring
    '''
    def __init__(self, *args, **kwargs):
        super(ExperimentConfig, self).__init__()


class SimpleBuffer(object):
    '''
    This class holds temporary episode-specific data such as q-values, losses
    etc. and exposes a `mean` method, which returns a Mean Square Average over
    all its values.
    '''
    def __init__(self):
        '''
        Init SimpleBuffer class with a ``data`` attribute of type ``numpy.array``.
        The class implements simple methods to add data to the buffer and clear it.
        '''
        self.data = np.array([])

    def push(self, data):
        '''
        Push another value into our buffer.
        '''
        if len(self.data) is not 0:
            self.data = np.vstack((self.data, data))
        else:
            self.data = np.concatenate((self.data, data), axis=0)

    def clear(self):
        '''
        This method clears the buffer.
        '''
        del self.data
        self.data = np.array([])

    def __len__(self):
        return len(self.data)
