# AUTOGENERATED! DO NOT EDIT! File to edit: nbs/markups.ipynb (unless otherwise specified).

__all__ = ['isnamedtupleinstance', 'unpack', 'read_json', 'write_json', 'AttrDict', 'pretty_json', 'write_jsonl',
           'read_jsonl', 'read_yaml', 'write_yaml', 'Config']

# Cell
def isnamedtupleinstance(x):
    _type = type(x)
    bases = _type.__bases__
    if len(bases) != 1 or bases[0] != tuple:
        return False
    fields = getattr(_type, "_fields", None)
    if not isinstance(fields, tuple):
        return False
    return all(type(i) == str for i in fields)


def unpack(obj):
    if isinstance(obj, dict):
        return {key: unpack(value) for key, value in obj.items()}
    elif isinstance(obj, list):
        return [unpack(value) for value in obj]
    elif isnamedtupleinstance(obj):
        return {key: unpack(value) for key, value in obj._asdict().items()}
    elif isinstance(obj, tuple):
        return tuple(unpack(value) for value in obj)
    else:
        return obj

# Cell
import json
import jsonlines

from .loader import BB, L
from .paths import *
from .logger import *


def read_json(fpath):
    import json

    with open(fpath, "r") as f:
        return json.load(f)


def write_json(obj, fpath, silent=False):
    def set_default(obj):
        if isinstance(obj, set):
            return list(obj)
        raise TypeError

    if not silent:
        logger.opt(depth=1).log("DEBUG", f"Dumping json to {fpath}")
    with open(fpath, "w") as f:
        json.dump(obj, f, indent=4, default=set_default)
    return P(fpath)


class AttrDict(object):
    """Utility to call a dictionary like an object
    FEATURES:
    0. Get and Set any key (or a nested key) like it's an object, tab-completion supported
    self.key1.key2[0].key3 ...
    1. Keys and Values are recursively parsed as AttrDicts
    2. Free formattying with self.pretty()
    3. Convert everything to dict at any time via self.to_dict()
    3. Recursively drop keys using self.drop(key) from a json object

    GOTCHAS:
    1. Due to enforcing a self.key format all integer keys are converted to strings implicitly.
    You can still call self[int] but this internally converts the int to string
    """

    def __init__(self, data):
        for name, value in data.items():
            setattr(self, str(name), self._wrap(value))

    def items(self):
        return self.__dict__.items()

    def keys(self):
        return self.__dict__.keys()

    def values(self):
        return self.__dict__.values()

    def _wrap(self, value):
        if isinstance(value, (L, tuple, list, set, frozenset)):
            value = type(value)([self._wrap(v) for v in value])
            if isinstance(value, (list, L)):
                value = L(value)
            return value
        else:
            return AttrDict(value) if isinstance(value, dict) else value

    __getitem__ = lambda self, x: getattr(self, str(x))
    __setitem__ = lambda self, k, v: setattr(self, str(k), self._wrap(v))

    def get(self, key, default=None):
        key = str(key)
        return self[key] if key in self else default

    def __repr__(self):
        return "{%s}" % str(
            ", ".join("'%s': %s" % (k, repr(v)) for (k, v) in self.__dict__.items())
        )

    def __dir__(self):
        return self.__dict__.keys()

    def __contains__(self, key):
        key = str(key)
        return key in self.__dict__.keys()

    def __delitem__(self, key):
        key = str(key)
        del self.__dict__[key]

    def drop(self, key):
        if key in self:
            del self[key]
        for k in dir(self):
            v = self[k]
            if isinstance(v, AttrDict):
                v.drop(key)
            if isinstance(v, (L, tuple, list, set, frozenset)):
                v = [_v.drop(key) for _v in v if isinstance(_v, AttrDict)]

    def to_dict(self):
        d = {}
        for k in dir(self):
            v = self[k]
            if isinstance(v, AttrDict):
                v = v.to_dict()
            if isinstance(v, (L, tuple, list, set, frozenset)):
                v = [_v.to_dict() if isinstance(_v, AttrDict) else _v for _v in v]
            d[k] = v
        return d

    def pretty(self, print_with_logger=False, *args, **kwargs):
        pretty_json(
            self.to_dict(), print_with_logger=print_with_logger, *args, **kwargs
        )

    def __eq__(self, other):
        return AttrDict(other).to_dict() == self.to_dict()


def pretty_json(i, fpath=None, indent=4, print_with_logger=True):
    def set_default(obj):
        if isinstance(obj, (set, BB)):
            return list(obj)

    assert isinstance(i, (dict, list))
    i = unpack(i)
    dump = json.dumps(i, indent=indent, default=set_default)
    if fpath:
        makedir(parent(fpath))
        logger.info(f"Dumped a pretty file to {fpath}")
        with open(fpath, "w") as f:
            json.dump(i, f, indent=indent, default=set_default)
            return
    if print_with_logger:
        return logger.opt(depth=1).log("DEBUG", f"\n{dump}")
    else:
        print(dump)


def write_jsonl(items, dest):
    makedir(parent(dest))
    with jsonlines.open(dest, "a") as writer:
        writer.write_all(items)


def read_jsonl(file):
    return [json.loads(line) for line in readlines(file, silent=True)]

# Cell
import yaml


def read_yaml(file):
    with open(file, "r") as stream:
        try:
            return yaml.safe_load(stream)
        except yaml.YAMLError as exc:
            print(exc)


def write_yaml(content, fpath):
    with open(fpath, "w") as outfile:
        yaml.dump(content, outfile, default_flow_style=False)

# Cell
from .thinc_parser.parser import Config

Config = Config