#include"octtree.h"
#include"image.h"



// Build octree
BOOL OctQuant::BuildTree()
{
  int row;              // Row counter
  int col;              // Column counter
  BOOL status = TRUE;   // Return status
  ImgRGBColor color;       // Pixel color

  // Allocate octree root node
  proot=MakeNode(0);

  // Build the octree
    for (row = 0; row < height; row++)
      for (col = 0; col < width; col++)
      {
		color.Set(*(RGBPixel *)(olddata+col+row*image->Pitch),image->Mask);
		InsertNode(proot, color);
        if (num_leaf > max_color) ReduceTree();
      }
	
  return status;
}

// Recursively delete octree nodes
void OctQuant::DeleteNode( OctNode *pn )
{
  int i;        // Loop index
  OctNode *pc;  // Child node pointer

  if (pn==NULL) return;
  if (pn->IsLeaf() == FALSE)
  {
    // Delete child nodes
    for (i = 0; i < 8; i++)
      if ((pc = pn->GetChild(i)) != NULL)
      {
        DeleteNode(pc);
        pn->SetChild(i, NULL);
        pn->DecNumChild();
      }
  }
  else num_leaf--;

  delete pn;
}

// Set color palette entries
void OctQuant::FillPalette( OctNode *pn, int *pindex)
{
  int i;    // Loop index

  // Perform recursive depth-first traversal of octree
  if (pn != NULL)
  {
    if ((pn->IsLeaf()==TRUE) || pn->GetLevel()==leaf_level)
    {
      // Set color palette entry
	  image->AllocPal();
      image->pal[*pindex] = pn->GetColor();

      // Set node color palette index
       pn->SetIndex(*pindex);

      // Advance to next color palette entry
      *pindex = *pindex + 1;
    }
    else
    {
      // Visit child nodes
      for (i = 0; i < 8; i++)
        FillPalette(pn->GetChild(i), pindex);
    }
  }
}

// Get next reducible node pointer
OctNode *OctQuant::GetReducible()
{
  int new_level;        // New reducible node level
  OctNode *prn;         // Reducible node pointer
  OctNode *plcn = NULL; // Largest pixel count node
  OctNode *pnext;       // Next node
  OctNode *pprev;       // Previous node

  new_level = leaf_level - 1;

  // Find lowest reducible node level 
  while (prnl[new_level] == NULL)
    new_level--;

  // Find node with largest pixel count
  prn = prnl[new_level];
  while (prn != NULL)
  {
    if (plcn == NULL)
      plcn = prn;
    else if (prn->GetCount() < plcn->GetCount())
      plcn = prn;
    prn = prn->GetNext();
  }
  
  // Remove node from reducible list
  pnext = plcn->GetNext();
  pprev = plcn->GetPrev();
  if (pprev == NULL)
  {
    prnl[new_level] = pnext;
    if (pnext != NULL)
      pnext->SetPrev(NULL);
  }
  else
  {
    pprev->SetNext(pnext);
    if (pnext != NULL)
      pnext->SetPrev(pprev);
  }

  plcn->SetNext(NULL);
  plcn->SetPrev(NULL);
  plcn->SetMark(FALSE);
  
  return plcn;
}

void OctQuant::ReduceTree()     // Reduce octree
{
  int i;        // Loop index
  OctNode *pn;  // Node pointer
  OctNode *pc;  // Child node pointer

  pn = GetReducible();  // Get next reducible node

  // Delete children
  for (i = 0; i < 8; i++)
  {
    if ((pc = pn->GetChild(i)) != NULL)
    {
      DeleteNode(pc);
      pn->SetChild(i, NULL);
      pn->DecNumChild();
    }
  }

  pn->SetLeaf(TRUE);    // Mark node as leaf
  num_leaf++;           // Increment leaf count

  // Update reduction and leaf levels
  if (pn->GetLevel() < (leaf_level - 1))
    leaf_level = pn->GetLevel() + 1;
}

// Insert node into octree
BOOL OctQuant::InsertNode( OctNode *pn, ImgRGBColor &c)
{
  int c_index;          // Child index
  int level;            // Node level
  BOOL status = TRUE;   // Return status
  OctNode *pc;          // Child node pointer

  level = pn->GetLevel();       // Get node level
  pn->AddColor(c);              // Add RGB color to node

  if (pn->IsLeaf() == FALSE && level < leaf_level)
  {
    // Find child node
    c_index = pn->FindChild(c);

    if ((pc = pn->GetChild(c_index)) == NULL)
    {
      // Allocate child node
      if ((pc = MakeNode(level + 1)) == NULL)
        return FALSE;

      // Set child node pointer
      pn->SetChild(c_index, pc);

      pn->IncNumChild();      // Increment child count
    }
		
    if ((pn->GetNumChild() > 1) && (pn->IsMark() == FALSE))
    {
      // Mark node as candidate for reduction
      MakeReducible(pn);
    }

    // Insert child node into octree
    status = InsertNode(pc, c);
  }

  return status;
}


BOOL OctQuant::MapColors()
{
  int row,col;              // Row counter
  ImgRGBColor color;       // 24-bit RGB color

int psize=(image->Bpp>>3);
int x,axl=width*psize;

  // Quantize the bitmap colors
  for (row=0; row < height; row++)
    for (x=0,col=0; x<axl; col++,x+=psize)
    {
	color.Set(*(RGBPixel *)(olddata+x+row*image->Pitch),image->Mask);     
    newdata[col+row*image->xlen]=(BYTE)QuantizeColor(proot, color);
    }

  return TRUE;
}

BOOL OctQuant::Quantize(Image *img)
{
  BOOL status = TRUE;   // Return status
image=img; 
newdata=new BYTE[image->xlen*image->ylen]; 
olddata=image->Lock(); 
image->AllocPal();
for(int i=0;i<O_NumColor;i++) image->pal[i]=0;

 width=image->xlen;
 height=image->ylen;

int ncol=0;
status=BuildTree();
FillPalette(proot,&ncol);
status=MapColors();

 DeleteTree();     // Delete color quantization octree

image->UnLock();
image->Bpp=8;
image->itype=ITYPE_INDEXED;
image->IAlloc();
image->AllocPal();
image->SetData(newdata);
image->RealizePalette();
delete newdata;

  return status;
}
