/*=============================================================================

  Program:   Visualization Toolkit
  Module:    vtkGeoTreeNode.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=============================================================================*/
/*-------------------------------------------------------------------------
  Copyright 2008 Sandia Corporation.
  Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
  the U.S. Government retains certain rights in this software.
-------------------------------------------------------------------------*/

#include "vtkObjectFactory.h"
#include "vtkGeoTreeNode.h"

vtkStandardNewMacro(vtkGeoTreeNode);


//----------------------------------------------------------------------------
vtkGeoTreeNode::vtkGeoTreeNode()
{
  this->Level = 0;
  this->Parent = nullptr;
  this->Older = nullptr;
  this->Newer = nullptr;
  this->Id = 0; // make valgrind happy
  this->LatitudeRange[0]  = this->LatitudeRange[1]  = 0.;
  this->LongitudeRange[0] = this->LongitudeRange[1] = 0.;
  this->Status = NONE;
}

//-----------------------------------------------------------------------------
vtkGeoTreeNode::~vtkGeoTreeNode()
{
  this->SetParent(nullptr);
}

//-----------------------------------------------------------------------------
void vtkGeoTreeNode::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os,indent);
  os << indent << "Level: " << this->Level << "\n";
  os << indent << "Parent: " << this->Parent << "\n";
  os << indent << "Older: " << this->Older << "\n";
  os << indent << "Newer: " << this->Newer << "\n";
  os << indent << "Id: " << this->Id << "\n";
  os << indent << "LatitudeRange: ["  << this->LatitudeRange[0]  << "," << this->LatitudeRange[1]  << "]\n";
  os << indent << "LongitudeRange: [" << this->LongitudeRange[0] << "," << this->LongitudeRange[1] << "]\n";
  os << indent << "Children:";
  for ( int i = 0; i < 4; ++ i )
  {
    os << " " << this->Children[i];
  }
  os << "\n";
}

//-----------------------------------------------------------------------------
void vtkGeoTreeNode::SetChild(vtkGeoTreeNode* node, int idx)
{
  if (idx < 0 || idx > 3)
  {
    vtkErrorMacro("Index out of range.");
    return;
  }
  this->Children[idx] = node;
}

//-----------------------------------------------------------------------------
int vtkGeoTreeNode::GetWhichChildAreYou()
{
  if (this->Level == 0)
  {
    vtkErrorMacro("Node does not have a parent.");
    return 0;
  }

  unsigned long id = this->Id;
  id = id >> (this->Level*2-1);
  id = id & 3;
  return id;
}

//-----------------------------------------------------------------------------
bool vtkGeoTreeNode::IsDescendantOf(vtkGeoTreeNode* elder)
{
  if (elder == nullptr)
  {
    return false;
  }
  if (this->Level <= elder->GetLevel())
  {
    return false;
  }
  // All descendants will have the same first N bits in their Id.
  int N = ((elder->GetLevel() * 2) + 1);
  unsigned long mask = (1 << N) - 1;
  if ((this->Id & mask) == elder->GetId())
  {
    return true;
  }
  return false;
}

//-----------------------------------------------------------------------------
int vtkGeoTreeNode::CreateChildren()
{
  // Create the four children.
  if (this->Children[0])
  { // This node is already has children.
    return VTK_OK;
  }
  int childLevel = this->GetLevel()+1;

  // Where the child index get coded in the node id.
  unsigned long idBit0 = 0;
  unsigned long idBit1 = 0;
  if (childLevel <= 15)
  {
    idBit0 = 1 << (2*childLevel - 1);
    idBit1 = 1 << (2*childLevel);
  }
  else
  {
    // if (childLevel > ((sizeof(unsigned long)*8) - 1) / 2)
    // this particular message gets printed too much and clutters the console...
    static bool msg_printed = false;
    if (!msg_printed)
    {
      vtkWarningMacro("Level too high to be encoded in node id. (this warning only emitted once)");
      msg_printed = true;
    }
  }
  unsigned long id = this->GetId();
  double longitudeRange[2];
  double latitudeRange[2];
  vtkGeoTreeNode* child;
  // Child 0
  this->GetLongitudeRange(longitudeRange);
  this->GetLatitudeRange(latitudeRange);
  double longitudeMid = 0.5 * (longitudeRange[0]+longitudeRange[1]);
  double latitudeMid = 0.5 * (latitudeRange[0]+latitudeRange[1]);
  // Child type is the same as parent.
  child = this->NewInstance();
  child->SetLevel(childLevel);
  child->SetId(id);
  longitudeRange[1] = longitudeMid;
  child->SetLongitudeRange(longitudeRange);
  latitudeRange[1] = latitudeMid;
  child->SetLatitudeRange(latitudeRange);
  this->SetChild(child, 0);
  child->SetParent(this);
  child->Delete();
  // Child 1
  this->GetLongitudeRange(longitudeRange);
  this->GetLatitudeRange(latitudeRange);
  // Child type is the same as parent.
  child = this->NewInstance();
  child->SetLevel(childLevel);
  child->SetId(id | idBit0);
  longitudeRange[0] = longitudeMid;
  child->SetLongitudeRange(longitudeRange);
  latitudeRange[1] = latitudeMid;
  child->SetLatitudeRange(latitudeRange);
  this->SetChild(child, 1);
  child->SetParent(this);
  child->Delete();
  // Child 2
  this->GetLongitudeRange(longitudeRange);
  this->GetLatitudeRange(latitudeRange);
  // Child type is the same as parent.
  child = this->NewInstance();
  child->SetLevel(childLevel);
  child->SetId(id | idBit1);
  longitudeRange[1] = longitudeMid;
  child->SetLongitudeRange(longitudeRange);
  latitudeRange[0] = latitudeMid;
  child->SetLatitudeRange(latitudeRange);
  this->SetChild(child, 2);
  child->SetParent(this);
  child->Delete();
  // Child 3
  this->GetLongitudeRange(longitudeRange);
  this->GetLatitudeRange(latitudeRange);
  // Child type is the same as parent.
  child = this->NewInstance();
  child->SetLevel(childLevel);
  child->SetId(id | idBit1 | idBit0);
  longitudeRange[0] = longitudeMid;
  child->SetLongitudeRange(longitudeRange);
  latitudeRange[0] = latitudeMid;
  child->SetLatitudeRange(latitudeRange);
  this->SetChild(child, 3);
  child->SetParent(this);
  child->Delete();
  //cerr << "creating child with range "
  //     << child->GetLongitudeRange()[0] << ","
  //     << child->GetLongitudeRange()[1] << ","
  //     << child->GetLatitudeRange()[0] << ","
  //     << child->GetLatitudeRange()[1] << endl;

  return VTK_OK;
}

vtkGeoTreeNode::NodeStatus vtkGeoTreeNode::GetStatus()
{
  return this->Status;
}

void vtkGeoTreeNode::SetStatus(NodeStatus status)
{
  this->Status = status;
}

void vtkGeoTreeNode::ShallowCopy(vtkGeoTreeNode *src)
{
  this->Level = src->Level;
  this->Id = src->Id;
  this->LongitudeRange[0] = src->LongitudeRange[0];
  this->LongitudeRange[1] = src->LongitudeRange[1];
  this->LatitudeRange[0] = src->LatitudeRange[0];
  this->LatitudeRange[1] = src->LatitudeRange[1];

  this->Children[0] = src->Children[0];
  this->Children[1] = src->Children[1];
  this->Children[2] = src->Children[2];
  this->Children[3] = src->Children[3];

  this->Status = src->Status;
  this->Parent = src->Parent;
}

void vtkGeoTreeNode::DeepCopy(vtkGeoTreeNode *src)
{
  this->Level = src->Level;
  this->Id = src->Id;
  this->LongitudeRange[0] = src->LongitudeRange[0];
  this->LongitudeRange[1] = src->LongitudeRange[1];
  this->LatitudeRange[0] = src->LatitudeRange[0];
  this->LatitudeRange[1] = src->LatitudeRange[1];

  this->Children[0] = src->Children[0];
  this->Children[1] = src->Children[1];
  this->Children[2] = src->Children[2];
  this->Children[3] = src->Children[3];

  this->Status = src->Status;
  this->Parent = src->Parent;
}
