//
// Copyright 2011 Ettus Research LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see .
//
#include 
#include 
#include 
#include 
#include 
#include 
class property_tree_impl : public uhd::property_tree{
public:
    property_tree_impl(const path_type &root = path_type()):
        _root(root)
    {
        _guts = boost::make_shared();
    }
    sptr subtree(const path_type &path_) const{
        const path_type path = _root / path_;
        boost::mutex::scoped_lock lock(_guts->mutex);
        property_tree_impl *subtree = new property_tree_impl(path);
        subtree->_guts = this->_guts; //copy the guts sptr
        return sptr(subtree);
    }
    void remove(const path_type &path_){
        const path_type path = _root / path_;
        boost::mutex::scoped_lock lock(_guts->mutex);
        node_type *parent = NULL;
        node_type *node = &_guts->root;
        BOOST_FOREACH(const std::string &branch, path){
            if (not node->has_key(branch)) throw_path_not_found(path);
            parent = node;
            node = &(*node)[branch];
        }
        if (parent == NULL) throw uhd::runtime_error("Cannot uproot");
        parent->pop(path.leaf());
    }
    bool exists(const path_type &path_) const{
        const path_type path = _root / path_;
        boost::mutex::scoped_lock lock(_guts->mutex);
        node_type *node = &_guts->root;
        BOOST_FOREACH(const std::string &branch, path){
            if (not node->has_key(branch)) return false;
            node = &(*node)[branch];
        }
        return true;
    }
    std::vector list(const path_type &path_) const{
        const path_type path = _root / path_;
        boost::mutex::scoped_lock lock(_guts->mutex);
        node_type *node = &_guts->root;
        BOOST_FOREACH(const std::string &branch, path){
            if (not node->has_key(branch)) throw_path_not_found(path);
            node = &(*node)[branch];
        }
        return node->keys();
    }
    void _create(const path_type &path_, const boost::shared_ptr &prop){
        const path_type path = _root / path_;
        boost::mutex::scoped_lock lock(_guts->mutex);
        node_type *node = &_guts->root;
        BOOST_FOREACH(const std::string &branch, path){
            if (not node->has_key(branch)) (*node)[branch] = node_type();
            node = &(*node)[branch];
        }
        if (node->prop.get() != NULL) throw uhd::runtime_error("Cannot create! Property already exists at: " + path.string());
        node->prop = prop;
    }
    boost::shared_ptr &_access(const path_type &path_) const{
        const path_type path = _root / path_;
        boost::mutex::scoped_lock lock(_guts->mutex);
        node_type *node = &_guts->root;
        BOOST_FOREACH(const std::string &branch, path){
            if (not node->has_key(branch)) throw_path_not_found(path);
            node = &(*node)[branch];
        }
        if (node->prop.get() == NULL) throw uhd::runtime_error("Cannot access! Property uninitialized at: " + path.string());
        return node->prop;
    }
private:
    void throw_path_not_found(const path_type &path) const{
        throw uhd::lookup_error("Path not found in tree: " + path.string());
    }
    //basic structural node element
    struct node_type : uhd::dict{
        boost::shared_ptr prop;
    };
    //tree guts which may be referenced in a subtree
    struct tree_guts_type{
        node_type root;
        boost::mutex mutex;
    };
    //members, the tree and root prefix
    boost::shared_ptr _guts;
    const path_type _root;
};
uhd::property_tree::sptr uhd::property_tree::make(void){
    return sptr(new property_tree_impl());
}