/*	Copyright Alex Hornby 1994/1995. All rights reserved.
 	See file README for details
*/

/* C++ code for sprite block copy wrapper */
 
#include "sprite.h"
#include <SLList.h>
#include "asmblock.h"
extern "C" {
#include "screendefs.h"
#include "btypes.h"
#include <assert.h>
}

#include <iostream.h>
#include <stdlib.h>
#include <string.h>

/* Static data members */
byte* Sprite :: gmem;
SLList<Sprite*> Sprite :: vlist;

/***************************************************************************
			LET THE FUNCTIONS BEGIN!
****************************************************************************/

Sprite::~Sprite() 
{
	if( backmem!=0) delete backmem;
	if (visibleflag==TRUE) setVisible(FALSE);
}

Sprite::Sprite() : Animation()
{
	if(gmem==0) 
		gmem=vga_getgraphmem();
	visibleflag=FALSE;
}

void
Sprite::copy( const Sprite& s)
{
	xp=s.xp;
	yp=s.yp;
	oldxp=s.oldxp;
	oldyp=s.oldyp;
	deltax=s.deltax;
	deltay=s.deltay;
	setVisible(s.getVisible());

	if(backmem!=0) 
		delete [] backmem;
	backmem=new byte [ByteLength];
	if(s.backmem!=0)
		memcpy(backmem, s.backmem, ByteLength);
} 

Sprite::Sprite( const Sprite& s) : Animation(s)
{
	copy(s);
}

Sprite&
Sprite::operator=( const Sprite& s) 
{
	if( &s == this)
		return *this;

	Animation::operator=(s);
	copy(s);
	return *this;
}

void
Sprite :: setSlot(int slot)
{
        assert(slot<MAXFRAME);
        assert(slot>=0);
        curslot=slot;
        blockmem=sprbank[slot].block;
        maskmem=sprbank[slot].mask;
        width=sprbank[slot].width;
        height=sprbank[slot].height;
        ByteLength=sprbank[slot].ByteLength;
}

void
Sprite :: setVisible( bool v)
{
	if( v==TRUE)	
	{
		if(visibleflag==FALSE)
		{
			Sprite *x=this;
			visibleflag=TRUE;		
			vlist.append(x);
		}
	}
	else
	{
		Pix here, prev;

		if(visibleflag==TRUE)
		{
			for(here=vlist.first(); here!=0; vlist.next(here))
			{
				if(vlist(here)==this)
				{
					if(here==vlist.first())
						vlist.del_front();
					else
						vlist.del_after(prev);
					here=0;
				}	
				prev=here;				
			}
		}
	visibleflag=FALSE;
	}
}

void 
Sprite :: Cut ( uint32 x1, uint32 y1, uint32 xw, uint32 yw, int slot)
{
	curslot=slot;
	sprbank[slot].width=width=xw;
	sprbank[slot].height=height=yw;
	sprbank[slot].ByteLength=ByteLength=xw*yw;
	xp=x1;yp=y1;

	/* if memory in use then free it */
	if( backmem!=0 ) delete backmem; 

	/* reserve block memory */
	blockmem=new byte [ByteLength];
	maskmem=new byte [ByteLength];
	backmem=new byte [ByteLength];
	sprbank[slot].block=blockmem;
	sprbank[slot].mask=maskmem;

	/* call assembly routines */
	cutblock( gmem+x1+(y1*SCREENWIDTH), blockmem, width, height); 
	cutblock( gmem+x1+(y1*SCREENWIDTH), backmem, width, height); 	
	MakeMask();
}

void
Sprite :: MakeMask(byte maskcolour=0)
{
	uint32 count;
	byte *maskptr, *blockptr;

	maskptr=maskmem;
	blockptr=blockmem;
	for( count=0; count<ByteLength; count++)
	{
		if(*blockptr==maskcolour)
			*maskptr=255;
		else
			*maskptr=0;
		maskptr++;
		blockptr++;
	}
}

void
Sprite :: Recut( uint32 x1, uint32 y1)
{
	/* call assembly routine */
	cutblock( gmem+x1+(y1*SCREENWIDTH), blockmem, width, height); 
	MakeMask();
}

void
Sprite :: Paste( uint32 x1, uint32 y1)
{
	/* Straight into the assembly bit */
	pasteblock( blockmem, gmem+x1+(y1*SCREENWIDTH), width, height );
}

void
Sprite :: getBack( uint32 x1, uint32 y1)
{
	xp=oldxp=x1;yp=oldyp=y1;

	/* get new background */
	if(backmem==0) backmem=new byte[ByteLength];
	cutblock( gmem+xp+(yp*SCREENWIDTH),backmem, width, height );
}

void
Sprite :: ReplaceAll(void)
{
	Pix cur, n;	// n is needed because of animation can cause NULL pix

	cur=vlist.first();
	assert(cur!=0);

	while( cur!=0 )
	{
		vlist(cur)->fixBack();
		n=cur;
		vlist.next(n);
		if( vlist(cur)->getAnim()==TRUE)
			vlist(cur)->doAnim();
		cur=n;
	}
}

void
Sprite :: PasteAll(void)
{
	Pix cur=vlist.first();
	while( cur!=0 )
	{
		vlist(cur)->Transpaste();
		vlist.next(cur);
	}
}

void
Sprite :: UpdateAll(void)
{
	ReplaceAll();
	PasteAll();	
}

void
Sprite :: hideAll(void)
{
	Pix cur, n;	// n is needed because of animation can cause NULL pix

	cur=vlist.first();
	assert(cur!=0);

	while( cur!=0 )
	{
		n=cur;
		vlist.next(n);
		vlist(cur)->setVisible(FALSE);
		cur=n;
	}
}

void
Sprite :: Update( int x1, int y1)
{
	xp=x1;yp=y1;

	// replace old bit
	pasteblock( backmem, gmem+oldxp+(oldyp*SCREENWIDTH), width, height );

	// get new background
	cutblock( gmem+xp+(yp*SCREENWIDTH),backmem, width, height );

	// display sprite
	Transpaste();
}

void
Sprite :: Transpaste( void)
{
	/*
	andpasteblock( maskmem, gmem+xp+(yp*SCREENWIDTH), width, height );
	xorpasteblock( blockmem, gmem+xp+(yp*SCREENWIDTH), width, height );
	*/
	transpasteblock( blockmem, maskmem, gmem+xp+(yp*SCREENWIDTH), width, height );
	oldxp=xp;oldyp=yp;
}

void 
Sprite::getAll()
{
	Pix cur=vlist.first();
	while( cur!=0 )
	{
		vlist(cur)->CutBack();
		vlist.next(cur);
	}
}

void
Sprite :: fixBack( void )
{
	// replace old bit
	pasteblock( backmem, gmem+oldxp+(oldyp*SCREENWIDTH), width, height );
}

void 
Sprite :: CutBack( void)
{
	// get new background
	cutblock( gmem+xp+(yp*SCREENWIDTH),backmem, width, height );
}
