#pragma once
#include <myapp/configuration/errors.h>
#include <algorithm>
#include <string>
#include <utility>
#include <yaml.h>

namespace myapp::configuration {

inline std::string
to_string(yaml_char_t* str, std::size_t len)
{
    if (str != nullptr)
        return { reinterpret_cast<const char*>(str), len };

    return std::string();
}

static void
throw_if_not_scalar_or_empty(yaml_node_t* node)
{
	if (!node || node->type != YAML_SCALAR_NODE)
		throw bad_document("key-value scalar expected");
	if (!node->data.scalar.value || node->data.scalar.length == 0)
		throw bad_document("null scalar is not allowed");
}

static std::string
get_scalar(yaml_node_t* node)
{
	throw_if_not_scalar_or_empty(node);
	return to_string(node->data.scalar.value, node->data.scalar.length);
}

static std::string
get_scalar(yaml_document_t* doc, int iNode)
{
	return get_scalar(::yaml_document_get_node(doc, iNode));
}

template<class T>
struct yaml_resource final
{
    template<class Initializer, class Deleter>
    explicit yaml_resource(Initializer && init, Deleter && deleter)
      : deleter_(std::forward<Deleter>(deleter))
      , instance_()
    {
        if (0 == init(&instance_))
            throw error("yaml error");
    }

    ~yaml_resource()
    {
        deleter_(&instance_);
    }

    T* operator&() noexcept
    {
        return &instance_;
    }

    T& operator->() noexcept
    {
        return instance_;
    }

private:
    void(*deleter_)(T*);
    T instance_;
};

template<class Visitor>
void yaml_visit(Visitor&& vis, yaml_node_t* node)
{
    switch (node->type)
    {
    case YAML_MAPPING_NODE:
        std::for_each(node->data.mapping.pairs.start, node->data.mapping.pairs.top, vis);
        break;
    case YAML_SEQUENCE_NODE:
        std::for_each(node->data.sequence.items.start, node->data.sequence.items.top, vis);
        break;
    case YAML_SCALAR_NODE:
        vis(node->data.scalar.value, node->data.scalar.length);
        break;
    case YAML_NO_NODE:
        break;
    }
}

} /* namespace myapp::configuration */
