"""
    Classes for representing hierarchical data
"""

import json
import logging
import sys

from neoteric.util.compat import NoneType
from neoteric.util.debug import get_caller
from neoteric.util.files import read_file, write_file
from neoteric.util.json_util import json_format
from neoteric.util.type import type_assert

__all__ = ['AbstractHData']

class AbstractHData:
    """ Inherit this class and implement __init__(self)
    """
    def __setattr__(self, key, value):
        type_assert(key, str)
        caller = get_caller()
        if key.startswith('secret_'):
            h = hash(value)
            msg = "Setting HData {0} to {1} -- {2}".format(key, h, caller)
        else:
            msg = "Setting HData {0} to {1} -- {2}".format(key, value, caller)
        logging.debug(msg)
        self.__dict__[key] = value

    def __getattr__(self, key, default=None):
        type_assert(key, str)
        if key in self.__dict__:
            return self.__dict__[key]
        elif key.startswith('__') and key.endswith('__'):
            raise AttributeError # magic methods
        else:
            return default

    if sys.version_info >= (3,):
        def __bool__(self):
            return bool(self.__dict__)
    else:
        def __nonzero__(self):
            return bool(self.__dict__)

    def get_value(self, key, default=None):
        """ Use this to return a default value if @key is not present

            @key:     str, The class member to return
            @default: mixed, The value to return if @key returns None
        """
        type_assert(key, str)
        return self.__getattr__(key=key, default=default)

    def merge(self, other):
        """ Merge another HData into this one.
        """
        for k, v in other.__dict__.items():
            setattr(self, k, v)

    def to_file(self, path):
        with open(path, 'w') as f:
            f.write(str(self))

    @staticmethod
    def from_file(path):
        type_assert(path, (str, NoneType))
        result = AbstractHData()
        if path is not None:
            text = read_file(path)
            data = json.loads(text)
            for k, v in data.items():
                result.__dict__[k] = v
        return result

    def __str__(self):
        return json_format(self.__dict__)
