"""
Restores and writes variables to a file so that
non-volatile functionality can be achieved.

Stores any dictionary in a file. On dictionary
per file. Ideas for use:
    * scratchpad
    * runonboot
    * variables

| author     = "Seth Miers"
| copyright  = "Copyright, Xilinx Inc. All rights reserved."
| credits    = ["Seth Miers"]
| license    = "GPL"
| version    = "3.0"
| maintainer = "Seth Miers"
| email      = "sethm@xilinx.com"
| status     = "Development"


"""

##########################################
#               Imports                  #
##########################################

# --------------Packages---------------- #
import pickle

# --------------Local------------------- #


##########################################
#              Main Code                 #
##########################################

class FileComm():
    '''
    Handle a single connection to a file.
    The FileComm interperets the file as if it
    were a variable so that it can be used as
    if it were non-volatile. Each file represents
    a dictionary, and the dictionary can contain
    anything that can be pickled (integers, strings, etc...)

    Args:
        logger (logging): The logger that should be used.
        main_file (string): the name/path of the file that
            will be used
        file_type (string): the type of file this is (how it
            will be treated). Options are 'raw' and 'memory'

    Attributes:
        (file): The path of the file that is being read
            and written to
        obj (dict): The non-volatile variable
    '''
    def __init__(self, logger, main_file):
        self.logger = logger
        self.main_file = main_file
        self.obj = self.load()

    def load(self):
        '''
        initialize the file and load it into
        memory for use in the class. Load it for
        reading first, if that errors, then create
        the file using the 'x' flag.

        Attributes:
            obj_file (file): The file to open and
                close to save the non-volatile
                variable
            obj (dict): the temporary non-volatile
                variable that will be returned
        '''
        obj = dict()
        try:
            obj_file = open(self.main_file, 'rb+')
            obj = pickle.load(obj_file)
        except FileNotFoundError:
            obj_file = open(self.main_file, 'xb+')
            self.logger.debug("File does not exist..." +
                              " Creating the file")
        except EOFError:
            self.logger.debug("File is empty..." +
                              " Using blank dictionary")
        except pickle.UnpicklingError:
            self.logger.warning("This file does not appear" +
                                "to be a non-volatile type. " +
                                "It will be overwritten if " +
                                "the program continues.")
        obj_file.close()
        return obj

    def update(self, index, value):
        '''
        Update a value within the dictionary. Typically
        for scratchpads and runonboots, an index is used
        with a number, but anything is allowed here. The
        dictionary is then immediately saved back out to
        the file.

        Attributes:
            original_obj (dict): A copy of the oribinal
                obj in case something goes wrong
        '''
        obj_file = open(self.main_file, 'wb+')
        original_obj = self.obj
        try:
            self.obj[index] = value
            pickle.dump(self.obj, obj_file)
        except TypeError:
            self.logger.error("Can only store variables \
                               that can be pickled")
            self.obj = original_obj
        obj_file.close()

    def delete(self, index):
        '''
        Completely delete a key from the dicationary. The
        dictionary is then immediately saved back out to
        the file.
        '''
        obj_file = open(self.main_file, 'wb+')
        try:
            del self.obj[index]
            pickle.dump(self.obj, obj_file)
        except KeyError:
            self.logger.error("Index does not exist and has no value")
        obj_file.close()

    def get(self, index):
        '''
        Return a single value out of the non-volatile
        dictionary.

        Attributes:
            value: The value that the index in the
                dictionary holds
        '''
        try:
            value = self.obj[index]
        except KeyError:
            value = 0
            self.logger.error("Index does not exist and has no value")
        return value

    def get_keys(self):
        '''
        Return a list of all the keys used in
        the dictionary.
        '''
        return list(self.obj.keys())
