from __future__ import absolute_import

import numpy as np

from custom_logging import get_logger
from pandas import DataFrame
from collections import defaultdict


class AdversarialSample(object):
    """
    The lowest level in the trial, it generally encompasses domain-specific implements.
    """
    def __init__(self, original, perturbed):
        self.original = original
        self.perturbed = perturbed

    def get_perturbed(self):
        return self.perturbed

    def get_original(self):
        return self.original


class TrialOutcome(object):
    """
    Trial and experiment are often used interchangeably.
    A trial in this case is an action or set of actions taken during an experiment.
    """
    def __init__(self, *args, **kwargs):
        self.logger = get_logger(type(self).__name__)

    def stopping_criteria(self):
        pass

    def __getstate__(self):
        d = dict(self.__dict__)
        del d['logger']
        return d


class ExperimentOutcome(object):
    """
    The experiment encompasses the trials performed to test a part of the hypothesis.
    """
    def __init__(self, *args, **kwargs):
        self.logger = get_logger(type(self).__name__)

    def __getstate__(self):
        d = dict(self.__dict__)
        del d['logger']
        return d


class BlockOutcome(object):
    """
    The block encompasses all the experiments necessary to prove or disprove a hypothesis.
    """
    def __init__(self, *args, **kwargs):
        self.logger = get_logger(type(self).__name__)
        self.experiment_outcomes = {}
        self.seed = None

    def add_experiment_outcome(self, tag, exp_obj):
        self.experiment_outcomes[tag] = exp_obj

    def set_seed(self, seed):
        self.seed = seed

    def __getstate__(self):
        d = dict(self.__dict__)
        del d['logger']
        return d


class Session(object):
    def __init__(self, cfg):
        """
        Keep track of hypothesis blocks for each system under test
        Want one session per hypothesis ideally.
        """
        self.cfg = cfg
        self.hypotheses = []
        self.hyp_to_collection_dict = {}

    def set_collection(self, hyp, collection):
        self.hyp_to_collection_dict[hyp] = collection

        if hyp not in self.hypotheses:
            self.hypotheses.append(hyp)

    def update_namespace(self, cfg):
        self.cfg = cfg

