#include <stdio.h>
#include "object.h"
#include "renderer.h"
#include "clut.h"

Object::Object( int aId ) : iId(aId)
{
  iRenderer = NULL;

}

Object::~Object()
{
  delete iRenderer;
}

int Object::Id()
{

  return iId;

}


void Object::AddToRegion( Region* aRegion, int x, int y )
{

  // Make this to be a list of <region,position> pairs to which to draw to
  // XXX Only support for object to be draw to a one region/position
  // Works for finnish dtv
  
  if ( iRenderer )
    delete iRenderer;
  iRenderer = new Renderer( aRegion, x, y );

}



inline void Object::Draw( int aX, int aY, int aPixelCode, int rl )
{
  
  // for each renderer
  if (iRenderer)
    iRenderer->Draw( aX, aY, aPixelCode, rl );

}


void Object::Decode(unsigned char* PixelData, int Size, int TopLength, int BottomLength)
{
  int x = 0;
  int y = 0;
  //  fprintf(stderr, "Size=%i, TopLength=%i, BottomLength=%i, s-t-b=%i\n",Size, TopLength, BottomLength, Size-TopLength-BottomLength );
 
  
  if (Size < TopLength )
  {
    //    fprintf(stderr, "Too little data for decoding Top(and bottom) field\n");
    return;
  }
  int index = 0;
  while ( index < TopLength )
  {
    SubBlock( PixelData, index, TopLength, x, y );
  }
  //  fprintf(stderr, "Index = %i, TopLength=%i \n",index, TopLength );
  unsigned char* bottom = PixelData+TopLength;
  if (Size < TopLength+BottomLength)
  {
    //    fprintf(stderr, "Too little data for decoding Bottom field\n");
    return;
  }
  
  index = 0;
  x = 0;
  y =1;
  while ( index < BottomLength )
  {
    SubBlock( bottom, index, BottomLength, x, y );
  }
}


inline unsigned char nibbleIterator( unsigned char* Buffer, int& Index, bool& Low )
{
  unsigned char ret = Buffer[ Index ];
  if ( Low )
  {
    ret &= 0x0F;
    Index++;
  }
  else
    ret >>= 4;
  
  Low = !Low;
  return ret;

}


void Object::SubBlock( unsigned char* Buffer, int& Index, int Size, int &x, int& y )
{
  
  unsigned char data_type = Buffer[Index++];
  switch ( data_type )
  {
  case 0x10:
    {
      //      fprintf(stderr, "2 bit per pixel objects not supported\n");
      break;
    }
  case 0x11:
    {
      if (iRenderer)
      {
	// If there's a fully transparent colour, plot one pixel using it.
	// This is to cause it to be included in the bitmap's palette (which
	// is extended as entries in this object's palette are used).
	const Clut *clut = iRenderer->GetClut ();
	const cSubtitlesPalette *palette = clut ? clut->GetPalette (4) : 0;
	if (palette)
	{
	  int entries = palette->GetNumColors ();
	  const uint32_t *colours = palette->GetColors ();
	  int index = -1;
	  while (++index < entries && (colours[index] & 0xFF000000))
	    ;
	  if (index < entries)
	    Draw (x, y, index);
	}
      }
      bool low = false;
      while ( Index < Size && decode4BppCodeString( Buffer, Index, low, x, y ) );
      //byte align
      if (low)
	Index++;
      break;
    }
  case 0x12:
    {
      //      fprintf(stderr, "8 bit per pixel objects not supported\n");
      break;
    }
  case 0x20:
    {
      break;
    }
  case 0x21:
    {

      break;
    }
  case 0x22:
    {

      break;
    }
  case 0xF0:
    {
      y += 2;
      x = 0;
      break;
    }
  default:
    {
      //fprintf(stderr, "!!!!!!!!!!!!!!!!!!!!!!!!!unknow data_type %02X\n", data_type );
    }
  }
  

}


inline bool Object::decode4BppCodeString(unsigned char* Buffer, int& Index, bool& Low,  int&x, int& y) 
{
  int code = nibbleIterator( Buffer, Index, Low );
  if ( code != 0 )
  {
    // Code=CCCC
    Draw( x++, y, code );
  }
  else
  {
    // code == 0000 XXXX
    code = nibbleIterator( Buffer, Index, Low );
    bool switch_1 = (code & 0x8) != 0;
    if ( !switch_1 )
    {
      // Code 0000 0XXX
      if ( code == 0x0 )
	return false; // end of string signal ( Code == 0000 0000 )
      // Code 0000 0RRR
      // rl RRR + 2
      Draw( x, y, 0, code + 2 );
      x += code + 2;
    }
    else
    {
      // CODE 0000 1XXX
      bool switch_2 = ( code & 0x04 ) != 0;
      if ( !switch_2 )
      {
	// CODE 0000 10XX
	int rl = ( code & 0x3 ) + 4;
	int code = nibbleIterator( Buffer, Index, Low );
	Draw( x, y, code, rl );
	x += rl;
      }
      else
      {
	// CODE 0000 11XX
	int switch_3 = code & 0x3;
	switch ( switch_3 )
	{
	case 0x0:
	  {
	    Draw( x++, y, 0);
	    break;
	  }
	case 0x1:
	  {
	    Draw( x, y, 0, 2);
	    x += 2;
	    break;
	  }
	case 0x2:
	  {
	    // Code 0000 1110 RRRR CCCC
	    int rl = nibbleIterator( Buffer,  Index, Low ) + 9;
	    code = nibbleIterator( Buffer, Index, Low );
	    Draw( x, y, code, rl );
	    x += rl;
	    break;
	  }
	case 0x3:
	  {
	    // Code 0000 1111 RRRR RRRR CCCC
	    int rl = ( nibbleIterator( Buffer, Index, Low ) << 4 ) +
	      nibbleIterator( Buffer, Index, Low ) + 25;
	    int code = nibbleIterator( Buffer, Index, Low );
	    Draw( x, y, code, rl );
	    x += rl;
	    break;
	  }
	}
	
      }
      
    }
  }
  return true;
}


