#include "pointer.hpp"

#include "tree.hpp"
#include "../parse.hpp"

#include <algorithm>
#include <stdexcept>


///////////////////////////////////////////////////////////////////////////////
::json::tree::node&
util::json::pointer::resolve (std::string const &path,
                              ::json::tree::node &root)
{
    // we only handle fragment represenations currently, which start with a '#'
    auto cursor = path.begin ();
    if (cursor == path.end () || *cursor != '#')
        throw std::invalid_argument ("must be fragment representation");
    ++cursor;

    auto obj = &root;

    // * step over each path segment
    // * index into the current node
    // * advance the node
    // * repeat until we reach the end of the query string
    for ( ; cursor != path.end (); ) {
        // step over the delimiting '/'
        if (*cursor != '/')
            break;
        ++cursor;

        // find the start of the next segment (or the end of the string)
        // and extract the key.
        auto next = std::find (cursor, path.end (), '/');
        std::string const key { cursor, next };

        // try to index into the node and advance the node we're operating on
        switch (obj->type ()) {
        case ::json::tree::OBJECT:
            obj = &(*obj)[key];
            break;

        case ::json::tree::ARRAY:
            obj = &(*obj)[util::parse<size_t> (key)];
            break;

        case ::json::tree::STRING:
        case ::json::tree::NUMBER:
        case ::json::tree::BOOLEAN:
        case ::json::tree::NONE:
            throw std::invalid_argument ("indexing into wrong type");
        }

        // step to the beginning of the next component
        cursor = next;
    }

    return *obj;
}