import pandas as pd
import numpy as np
import sys
import yaml
import os

# import wandb

class FastLocalLogger():
    def __init__(self, autosync_file=None, autosync_freq=-1):
        self.accum = []
        self.globs = None

        self.autosync_file = autosync_file
        self.autosync_freq = autosync_freq if autosync_freq > 0 else sys.maxsize  # reasonable values is about 100k
        self.autosync_count = 0

        self.sync_on_globs = set({})

    def _autosync(self):
        # perform a synchronisation of unsynced buffer into a csv file with a corresponding index
        if self.autosync_file is None:
            print(f'Warning: autosyncing without the file!')
            return 0
        else:
            dumpable_records = pd.DataFrame(self.accum[-self.autosync_count:])
            dumpable_records.to_csv(f'{self.autosync_file}_{len(self.accum)-self.autosync_count}_{len(self.accum)}.csv')
            retval = self.autosync_count
            self.autosync_count = 0 # reset the number of unsynced
            return retval

    def autosync_manual_trigger(self):
        return self._autosync()

    def set_globs(self, **globs):
        self.globs = globs
    
    def set_glob(self, key, val, sync_on_change=False):
        if self.globs is None:
            self.globs = {}
        self.globs[key] = val
        # now if the glob is just changed, we perform autosync if its enabled for the glob
        if key in self.sync_on_globs:
            self._autosync()
        if sync_on_change:
            self.sync_on_globs.add(key)        

    def drop_globs(self):
        self.globs = None

    def drop_glob(self, key):
        del self.globs[key]
        self.sync_on_globs.discard(key)
        
    def log(self, key, value, **tglobs):
        # tglobs are temporary, are not set
        if self.globs is not None:
            tglobs = {**self.globs, **tglobs}

        tglobs['metric'] = key
        tglobs['value'] = value
        self.accum.append(tglobs)

        # autosync related
        self.autosync_count+=1
        if self.autosync_count >= self.autosync_freq:  # if freq == intmax then this is never done
            self._autosync()
    
    def log_iterable(self, key, value, counting_glob = "ord", counter_fn=None, **tglobs):
        def simplest_counter(n):
            return n
        
        if counter_fn == None:
            counter_fn = simplest_counter
        
        for i, v in enumerate(value):
            self.log(key, v, **tglobs, **{counting_glob: counter_fn(i)})

    def finalize(self):
        dfversion = pd.DataFrame(self.accum)
        return dfversion
    
    # def save_full_to_wandb(self, df=None, logger_id="flogger_output"):
    #     if df is None:
    #         df = self.finalize()
    #     wandb_table = wandb.Table(dataframe=df)
    #     wandb.log({logger_id: wandb_table})
    #     return df

    def save_summary_to_wandb(self):
        raise NotImplemented


class FastLocalLoggerDummy(FastLocalLogger):
    '''Doesnt cause errors when calling any of the methods.'''
    def __getattribute__(self, name):
        return FastLocalLoggerDummy._noop

    @staticmethod
    def _noop(*args, **kwargs):
        pass
