#
# Copyright (C) 2011 - 2018 Satoru SATOH <ssato @ redhat.com>
# License: MIT
#
# type() is used to exactly match check instead of isinstance here.
# pylint: disable=unidiomatic-typecheck
r"""YAML backend by ruamel.yaml:

- Format to support: YAML, http://yaml.org
- Requirement: ruamel.yaml, https://bitbucket.org/ruamel/yaml
- Development Status :: 4 - Beta
- Limitations:

  - Multi-documents YAML stream load and dump are not supported.

- Special options:

  - All keyword options of yaml.safe_load, yaml.load, yaml.safe_dump and
    yaml.dump should work.

  - Use 'ac_safe' boolean keyword option if you prefer to call yaml.safe_load
    and yaml.safe_dump instead of yaml.load and yaml.dump. Please note that
    this option conflicts with 'ac_dict' option and these options cannot be
    used at the same time.

  - See also: http://pyyaml.org/wiki/PyYAMLDocumentation

Changelog:

.. versionchanged:: 0.9.8

   - Split from the common yaml backend and start to support ruamel.yaml
     specific features.

.. versionchanged:: 0.9.7

   - Add support of loading primitives other than mapping objects.

"""
from __future__ import absolute_import

import re
import ruamel.yaml as ryaml
import anyconfig.utils

from . import pyyaml


_ALPHA_RE = re.compile(r'[a-z]+[a-z_]+')
_YAML_INIT_KWARGS = ["typ", "pure", "plug_ins"]  # kwargs for ruamel.yaml.YAML
# .. todo:: Find out other better and reliable way to list options.
# _YAML = ryaml.YAML()
# _YAML_INSTANCE_MEMBERS = [
#     x for x in dir(_YAML)
#    if _ALPHA_RE.match(x) and not callable(getattr(_YAML, x))
# ]
_YAML_INSTANCE_MEMBERS = ['allow_duplicate_keys', 'allow_unicode',
                          'block_seq_indent', 'canonical', 'composer',
                          'constructor', 'default_flow_style', 'default_style',
                          'dump', 'dump_all', 'emitter', 'encoding',
                          'explicit_end', 'explicit_start',
                          'get_constructor_parser',
                          'get_serializer_representer_emitter', 'indent',
                          'line_break', 'load', 'load_all', 'map',
                          'map_indent', 'official_plug_ins', 'old_indent',
                          'parser', 'prefix_colon', 'preserve_quotes',
                          'reader', 'register_class', 'representer',
                          'resolver', 'scanner', 'seq', 'sequence_dash_offset',
                          'sequence_indent', 'serializer', 'stream', 'tags',
                          'top_level_block_style_scalar_no_indent_error_1_1',
                          'top_level_colon_align', 'version', 'width']

_YAML_OPTS = _YAML_INIT_KWARGS + _YAML_INSTANCE_MEMBERS


def yml_fnc(fname, *args, **options):
    """
    :param fname:
        "load" or "dump", not checked but it should be OK.
        see also :func:`yml_load` and :func:`yml_dump`
    :param args: [stream] for load or [cnf, stream] for dump
    :param options: keyword args may contain "ac_safe" to load/dump safely
    """
    if "ac_safe" in options:
        options["typ"] = "safe"  # Override it.

    iopts = anyconfig.utils.filter_options(_YAML_INIT_KWARGS, options)
    oopts = anyconfig.utils.filter_options(_YAML_INSTANCE_MEMBERS, options)

    yml = ryaml.YAML(**iopts)
    for attr, val in oopts.items():
        setattr(yml, attr, val)  # e.g. yml.preserve_quotes = True

    return getattr(yml, fname)(*args)


def yml_load(stream, container, **options):
    """.. seealso:: :func:`anyconfig.backend.yaml.pyyaml.yml_load`
    """
    return pyyaml.yml_load(stream, container, yml_fnc=yml_fnc, **options)


def yml_dump(data, stream, **options):
    """.. seealso:: :func:`anyconfig.backend.yaml.pyyaml.yml_dump`
    """
    return pyyaml.yml_dump(data, stream, yml_fnc=yml_fnc, **options)


class Parser(pyyaml.Parser):
    """Parser for YAML files.
    """
    _cid = "ruamel.yaml"
    _priority = 30  # Higher priority than PyYAML.
    _load_opts = _YAML_OPTS
    _dump_opts = _YAML_OPTS

    load_from_stream = anyconfig.backend.base.to_method(yml_load)
    dump_to_stream = anyconfig.backend.base.to_method(yml_dump)

# vim:sw=4:ts=4:et:
