/*
 * (C) Copyright 1996- 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 SPNode_H
#define SPNode_H

#include "eckit/eckit.h"

#include "eckit/container/sptree/SPNodeInfo.h"
#include "eckit/container/sptree/SPNodeQueue.h"
#include "eckit/container/sptree/SPValue.h"

namespace eckit {


template<class Traits, class NodeType>
class SPNode {
public:
    typedef typename Traits::Point   Point;
    typedef typename Traits::Payload Payload;
    typedef typename Traits::Alloc   Alloc;

    typedef SPNodeInfo<Traits, NodeType>          NodeInfo;
    typedef typename NodeInfo::NodeList NodeList;
    typedef SPValue<Traits>             Value;
    typedef SPNodeQueue<Traits, NodeType>         NodeQueue;

    typedef NodeType Node;

protected:

    Value     value_;

    typedef typename Alloc::Ptr Ptr;
    Ptr left_;
    Ptr right_;
    Ptr next_; // For fast transversal

    friend struct SPMemory;


public:
    // SPNode(const Value& value);

    template<class V>
    SPNode(const V& value);

    ~SPNode() {}

    NodeInfo nearestNeighbour(Alloc& a,const Point& p);
    NodeList findInSphere(Alloc& a,const Point& p, double radius);
    NodeList kNearestNeighbours(Alloc& a,const Point& p,size_t k);

    const Point& point() const     { return value_.point();   }
    const Payload& payload() const { return value_.payload(); }
    Value& value()                 { return value_;           }
    const Value& value() const     { return value_;           }

    template<typename ITER>
    static SPNode* build(Alloc& a,const ITER& begin, const ITER& end, int depth = 0);

    // For testing only

    NodeInfo nearestNeighbourBruteForce(Alloc& a,const Point& p);
    NodeList findInSphereBruteForce(Alloc& a,const Point& p, double radius);
    NodeList kNearestNeighboursBruteForce(Alloc& a,const Point& p,size_t k);

    // -------
    template<class Visitor>
    void visit(Alloc& a, Visitor& v, int depth = 0);

    // ---------
    void linkNodes(Alloc& a, Node*& prev = 0);

    const Node* asNode() const { return static_cast<const Node*>(this); }
    Node* asNode() { return static_cast<Node*>(this); }

public: // because of a clang bug. Should be protected
    // void nearestNeighbourX(Alloc& a,const Point& p, Node*& best, double& max, int depth) = 0;
    void nearestNeighbourBruteForceX(Alloc& a,const Point& p, Node*& best,double& max, int depth);
    // void findInSphereX(Alloc& a,const Point& p ,double radius, NodeList& result, int depth) = 0;
    void findInSphereBruteForceX(Alloc& a,const Point& p, double radius, NodeList& result, int depth) ;
    // void kNearestNeighboursX(Alloc& a,const Point& p ,size_t k, NodeQueue& result, int depth) = 0;
    void kNearestNeighboursBruteForceX(Alloc& a,const Point& p, size_t k, NodeQueue& result, int depth) ;

    //==========================
public:
    Node* left(Alloc& a)  const { return a.convert(left_,  (Node*)0);   }
    Node* right(Alloc& a) const { return a.convert(right_, (Node*)0);  }
    Node* next(Alloc& a)  const { return a.convert(next_,  (Node*)0);   }

    void  left(Alloc& a,  Node* n) { left_  = a.convert(n); }
    void  right(Alloc& a, Node* n) { right_ = a.convert(n); }
    void  next(Alloc& a,  Node* n) { next_  = a.convert(n); }

    friend class SPIterator<Traits, NodeType>;

};


} // end namespace

#include "SPNode.cc"

#endif
