/*
 * (C) Copyright 1996-2017 ECMWF.
 *
 * This software is licensed under the terms of the Apache Licence Version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
 * In applying this licence, ECMWF does not waive the privileges and immunities
 * granted to it by virtue of its status as an intergovernmental organisation nor
 * does it submit to any jurisdiction.
 */

#ifndef KDTree_H
#define KDTree_H

#include "eckit/exception/Exceptions.h"
#include "eckit/container/sptree/SPTree.h"
#include "eckit/container/kdtree/KDNode.h"

#include "KDMemory.h"
#include "KDMapped.h"


namespace eckit {

template<class Traits>
class KDTreeX : public SPTree<Traits, KDNode<Traits> > {
public:
    typedef KDNode<Traits>         Node;
    typedef SPTree<Traits,Node>    SPTreeType; // cannot redefine as SPTree since some compilers in-class redefinitions
    typedef typename Traits::Alloc Alloc;

    typedef typename SPTreeType::Value   Value;
    typedef typename SPTreeType::Point   Point;
    typedef typename SPTreeType::Payload Payload;

public:
    KDTreeX(Alloc& alloc): SPTreeType(alloc) {

    }

    /// ITER must be a random access iterator
    /// WARNING: container is changed (sorted)
    template<typename ITER>
    void build(ITER begin, ITER end)
    {
        Alloc& a = this->alloc_;
        this->root_ = a.convert(Node::build(a, begin, end));
        a.root(this->root_);
    }

    /// Container must be a random access
    /// WARNING: container is changed (sorted)
    template<typename Container>
    void build(Container& c)
    {
        typename Container::iterator b = c.begin();
        typename Container::iterator e = c.end();
        build(b, e);
    }

    //
    void insert(const Value& value)
    {
        Alloc& a = this->alloc_;
        Node* root = Node::insert(a, value, a.convert(this->root_, (Node*)0), 0);

        if(a.convert(root) != this->root_)
        {
            a.root(a.convert(root));
            this->root_ = a.convert(root);
        }
    }

};



template<class Traits>
class KDTreeMemory : public KDTreeX< TT<Traits,KDMemory>  > {
    KDMemory alloc_;
public:

    typedef KDTreeX< TT<Traits,KDMemory>  > KDTree;
    typedef typename KDTree::Value   Value;
    typedef typename KDTree::Point   Point;
    typedef typename KDTree::Payload Payload;

public:
    KDTreeMemory() : KDTree(alloc_) {}
};

template<class Traits>
class KDTreeMapped : public KDTreeX< TT<Traits,KDMapped> > {
    KDMapped alloc_;
public:

    typedef KDTreeX< TT<Traits,KDMapped>  > KDTree;
    typedef typename KDTree::Value   Value;
    typedef typename KDTree::Point   Point;
    typedef typename KDTree::Payload Payload;
    typedef typename KDTree::Node    Node;

public:
    KDTreeMapped(const eckit::PathName& path,  size_t itemCount, size_t metadataSize):
        KDTree(alloc_),
        alloc_(path, itemCount, sizeof(Node), metadataSize)
    {
    }

};

} // end namespace


#endif
