//
// ********************************************************************
// * License and Disclaimer                                           *
// *                                                                  *
// * The  Geant4 software  is  copyright of the Copyright Holders  of *
// * the Geant4 Collaboration.  It is provided  under  the terms  and *
// * conditions of the Geant4 Software License,  included in the file *
// * LICENSE and available at  http://cern.ch/geant4/license .  These *
// * include a list of copyright holders.                             *
// *                                                                  *
// * Neither the authors of this software system, nor their employing *
// * institutes,nor the agencies providing financial support for this *
// * work  make  any representation or  warranty, express or implied, *
// * regarding  this  software system or assume any liability for its *
// * use.  Please see the license in the file  LICENSE  and URL above *
// * for the full disclaimer and the limitation of liability.         *
// *                                                                  *
// * This  code  implementation is the result of  the  scientific and *
// * technical work of the GEANT4 collaboration.                      *
// * By using,  copying,  modifying or  distributing the software (or *
// * any work based  on the software)  you  agree  to acknowledge its *
// * use  in  resulting  scientific  publications,  and indicate your *
// * acceptance of all terms of the Geant4 Software license.          *
// ********************************************************************
//
//
#ifndef MOLECULAR_OCTREE_NODE_HH
#define MOLECULAR_OCTREE_NODE_HH

#include "G4ThreeVector.hh"
#include "globals.hh"

#include <array>
#include <vector>

class G4VPhysicalVolume;

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

class OctreeNode
{
  public:
    // Uniform divisions constructor along each axis
    OctreeNode(const G4ThreeVector&, const G4ThreeVector&, G4int, OctreeNode* parent = nullptr);

    ~OctreeNode();

    inline G4bool HasChildren() const { return (fChildren[0] != nullptr); }

    inline OctreeNode* GetParent() const { return fParent; };

    inline const auto& GetHalfLengths() const { return fHalfLengths; };

    inline G4double GetHalfLengthsMag() const { return fHalfLengthsMag; };

    inline const G4ThreeVector& GetPosition() const { return fPosition; };

    inline const auto& GetChildren() const { return fChildren; };

    const std::vector<G4VPhysicalVolume*> SearchOctree(const G4ThreeVector&,
                                                       G4double _rad = 0) const;

    void SearchOctree(const G4ThreeVector& pos, std::vector<G4VPhysicalVolume*>& out,
                      G4double _rad = 0) const;

    const std::vector<G4VPhysicalVolume*> SearchOctree(const G4ThreeVector&) const;

    G4int GetNumberOfTerminalNodes();

    void AddPhysicalVolume(G4VPhysicalVolume*);

    std::vector<G4VPhysicalVolume*> GetContents() const;

    inline G4int GetMaxContents() const { return fMaxContents; };

  protected:
    void Split();

    const OctreeNode* GetChildFromPosition(G4ThreeVector const&) const;

    OctreeNode* GetChildFromPosition(G4ThreeVector const& pos);

  private:
    G4ThreeVector fPosition, fHalfLengths;
    G4int fMaxContents;

    std::vector<G4VPhysicalVolume*> fContents;

    OctreeNode* fParent;
    // fChildren is arranged logically to save on queries
    // The scheme is defined by quadrant as follows:
    // X Y Z Index | X Y Z Index
    // + + +   0   | - + +   4
    // + + -   1   | - + -   5
    // + - +   2   | - - +   6
    // + - -   3   | - - -   7
    std::array<OctreeNode*, 8> fChildren;
    G4double fHalfLengthsMag;
};

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

#endif  // MOLECULAR_OCTREE_NODE_HH
