/*
 * Implementation of class PyramidTree
 *
 * $Date$
 * $Revision$
 *
 */

#include "WTools/PyramidTree.hh"
#include <cassert>

#if !(defined __WATCOMC__ && __WATCOMC__ < 1230)
using namespace std;
#endif

PyramidTree::PyramidTree (PyramidTransform &t, int y, int x, int position,
                          PyramidTree *parent, int generations)
  : NTree < CoeffInformation > (4, parent, position)
{
  m_transform = &t;

  area channel;
  int subband;
  m_transform->where (y, x, subband, channel);

  if (channel == LL)
    {
      throw std::invalid_argument ("findChildPosition: invalid position (LL).");
    }

  m_data.set (m_transform->image (), y, x);

  if (generations > 1)
    {
      addGenerations (generations - 1);
    }
}

PyramidTree::~PyramidTree (void)
{
}

void 
PyramidTree::addGenerations (int depth)
{
  if (depth < 1)
    {
      return;
    }

  int y = 0;
  int x = 0;
  int addY = 0;
  int addX = 0;
  
  findChildPosition (m_data.ypos (), m_data.xpos (), y, x);
  
  for (int i = 0; i < this->aryness (); i++)
    {
      addY = (i & 0x02)? 1: 0;
      addX = (i & 0x01)? 1: 0;
      
      DELETENOTNULL (m_children[i]);
      m_children[i] = NEW (PyramidTree (*m_transform, y + addY, x + addX, 
                                        i, this));
      ((PyramidTree*)m_children[i])->addGenerations (depth - 1);
    }
}

void 
PyramidTree::findChildPosition (int y, int x, int &resY, int &resX)
{
  area channel;
  int subband;
  m_transform->where (y, x, subband, channel);

  if (channel == LL)
    {
      throw std::invalid_argument ("findChildPosition: invalid position (LL).");
    }
  
  if (subband < 2)
    {
      throw std::invalid_argument ("findChildPosition: "
                                   "invalid position (subband 1)");
      
    }

  int yoffs1, xoffs1, yoffs2, xoffs2, ysize1, xsize1, ysize2, xsize2;
  
  // get this subband's sizes
  m_transform->where (channel, subband, yoffs1, xoffs1, ysize1, xsize1);

  assert (x >= xoffs1 && x < xoffs1 + xsize1);
  assert (y >= yoffs1 && y < yoffs1 + ysize1);

  // get the children's subband's sizes
  m_transform->where (channel, subband - 1, yoffs2, xoffs2, ysize2, xsize2);

  
  /* calculate the destination (mapped) coordinates from their relative
   * channel position and the two channels' coordinates... */
  resY = (int)(0.5f + (int)((float)(y - yoffs1) 
                            / (float)ysize1 * ysize2) + yoffs2);
  resX = (int)(0.5f + (int)((float)(x - xoffs1) 
                            / (float)xsize1 * xsize2) + xoffs2);
  assert (resX >= xoffs2 && resX < xoffs2 + xsize2);
  assert (resY >= yoffs2 && resY < yoffs2 + ysize2);
}

int 
PyramidTree::depth (void)
{
  int rc = 1;
  int i;
  assert (m_children != NULL);
  for (i = 0; i < aryness () && m_children[i] == NULL; i++)
    {
    }

  assert (m_children != NULL);
  if (i < aryness () && m_children[i] != NULL)
    {
      rc += ((PyramidTree*)m_children[i])->depth ();
    }
  return rc;
}


void 
PyramidTree::moveTo (int y, int x, int position, PyramidTree *parent)
{
  if (y == m_data.ypos () && x == m_data.xpos ())
    {
      return;
    }
  
  m_parent = parent;
  m_position = position;
  this->shiftBy (y - m_data.ypos (), x - m_data.xpos ());
}

void 
PyramidTree::shiftBy (int shiftY, int shiftX)
{
  m_data.set (m_transform->image (), 
              m_data.ypos () + shiftY, m_data.xpos () + shiftX);
  
  for (int i = 0; i < aryness (); i++)
    {
      if (hasChildAt (i))
        {
          ((PyramidTree*)m_children[i])->shiftBy (2*shiftY, 2*shiftX);
        }
    }
}


