#include <clib/alib_protos.h>
#include <clib/cybergraphics_protos.h>
#include <clib/datatypes_protos.h>
#include <clib/dos_protos.h>
#include <clib/exec_protos.h>
#include <clib/graphics_protos.h>
#include <clib/iffparse_protos.h>
#include <clib/xpkmaster_protos.h>
#include <datatypes/pictureclass.h>
#include <exec/memory.h>
#include <libraries/cybergraphics.h>
#include <libraries/rgfx.h>

extern struct Library *SuperClassBase,*XpkBase;

static long chunks[]=
{
ID_RGFX,ID_RGHD,
ID_RGFX,ID_RSCM,
ID_RGFX,ID_RCOL,
ID_RGFX,ID_NAME,
ID_RGFX,ID_AUTH,
ID_RGFX,ID_ANNO,
ID_RGFX,ID_Copyright
};

static void GetRGHD(Object *obj,struct RGHD *rh,struct BitMapHeader *bh)
{
bh->bmh_Left=rh->rgfx_LeftEdge;
bh->bmh_Top=rh->rgfx_TopEdge;
bh->bmh_Width=rh->rgfx_Width;
bh->bmh_Height=rh->rgfx_Height;
bh->bmh_PageWidth=rh->rgfx_PageWidth;
bh->bmh_PageHeight=rh->rgfx_PageHeight;
bh->bmh_Depth=rh->rgfx_Depth;
bh->bmh_Compression=cmpByteRun1;
bh->bmh_XAspect=rh->rgfx_XAspect;
bh->bmh_YAspect=rh->rgfx_YAspect;
SetDTAttrs(obj,0,0,DTA_NominalHoriz,bh->bmh_Width,DTA_NominalVert,bh->bmh_Height,TAG_END);
if(bh->bmh_Depth<=8) SetDTAttrs(obj,0,0,PDTA_NumColors,1<<bh->bmh_Depth,TAG_END);
}

static ULONG GetRSCM(Object *obj,IFFHandle *iff,BitMapHeader *bh)
{
ULONG mode=INVALID_ID;
StoredProperty *sp;
if(sp=FindProp(iff,ID_RGFX,ID_RSCM))
	{
	struct RSCM *rscm=sp->sp_Data;
	if(!ModeNotAvailable(rscm->rscm_P96)) mode=rscm->rscm_P96;
	else if(!ModeNotAvailable(rscm->rscm_CGfx)) mode=rscm->rscm_CGfx;
	else if(!ModeNotAvailable(rscm->rscm_AGA)) mode=rscm->rscm_AGA;
	}
if(mode==INVALID_ID) mode=BestModeID(BIDTAG_NominalWidth,bh->bmh_Width,BIDTAG_NominalHeight,bh->bmh_Height,BIDTAG_Depth,bh->bmh_Depth,TAG_END);
if(mode!=INVALID_ID) SetDTAttrs(obj,0,0,PDTA_ModeID,mode,TAG_END);
return mode;
}

static BOOL GetRCOL(Object *obj,IFFHandle *iff,BitMapHeader *bh)
{
BOOL ok=0;
if(bh->bmh_Depth<=8)
	{
	StoredProperty *sp;
	if(sp=FindProp(iff,ID_RGFX,ID_RCOL))
		{
		struct RCOL *rcol=sp->sp_Data;
		ULONG i,n,*cregs;
		UBYTE *cr,*p;
		GetDTAttrs(obj,PDTA_NumColors,&n,PDTA_ColorRegisters,&cr,PDTA_CRegs,&cregs,TAG_END);
		n*=3;
		for(i=0,p=rcol->rcol_Colors[0];i<n;i++,p++,cr++,cregs++)
			{
			*cr=*p;
			*cregs=(*p)<<24;
			}
		ok=1;
		}
	}
else ok=1;
return ok;
}

static BOOL GetBody_BP8(struct IFFHandle *iff,struct RGHD *rh,struct BitMap *bm)
{
ULONG d;
for(d=0;d<rh->rgfx_Depth;d++)
	{
	ULONG y;
	UBYTE *pl=bm->Planes[d];
	for(y=0;y<rh->rgfx_Height;y++,pl+=bm->BytesPerRow) ReadChunkBytes(iff,pl,rh->rgfx_BytesPerLine);
	}
return 1;
}

static BOOL GetBody_BC8(struct IClass *cl,Object *obj,struct IFFHandle *iff,struct RGHD *rh)
{
BOOL ok=0;
UBYTE *pix;
if(pix=AllocVec(rh->rgfx_Width,MEMF_PUBLIC))
	{
	ULONG y;
	for(y=0;y<rh->rgfx_Height;y++)
		{
		ReadChunkBytes(iff,pix,rh->rgfx_Width);
		DoSuperMethod(cl,obj,PDTM_WRITEPIXELARRAY,pix,RECTFMT_LUT8,rh->rgfx_Width,0,y,rh->rgfx_Width,1);
		}
	FreeVec(pix);
	ok=1;
	}
return ok;
}

static BOOL GetBody_BC8_39(struct IFFHandle *iff,struct RGHD *rh,struct BitMap *bm)
{
BOOL ok=0;
struct BitMap *tbm;
if(tbm=AllocBitMap(rh->rgfx_Width,1,rh->rgfx_Depth,0,bm))
	{
	UBYTE *pix;
	if(pix=AllocVec(((rh->rgfx_Width+15)>>4)<<4,MEMF_PUBLIC))
		{
		ULONG y;
		struct RastPort rp,trp;
		InitRastPort(&rp);
		InitRastPort(&trp);
		rp.BitMap=bm;
		trp.BitMap=tbm;
		for(y=0;y<rh->rgfx_Height;y++)
			{
			ReadChunkBytes(iff,pix,rh->rgfx_Width);
			WritePixelLine8(&rp,0,y,rh->rgfx_Width,pix,&trp);
			}
		FreeVec(pix);
		ok=1;
		}
	FreeBitMap(tbm);
	}
return ok;
}

static BOOL GetBody_RGB(struct IClass *cl,Object *obj,struct IFFHandle *iff,struct RGHD *rh)
{
BOOL ok=0;
UBYTE *pix;
if(pix=AllocVec(rh->rgfx_BytesPerLine,MEMF_PUBLIC))
	{
	ULONG y;
	for(y=0;y<rh->rgfx_Height;y++)
		{
		ReadChunkBytes(iff,pix,rh->rgfx_BytesPerLine);
		DoSuperMethod(cl,obj,PDTM_WRITEPIXELARRAY,pix,RECTFMT_RGB,rh->rgfx_BytesPerLine,0,y,rh->rgfx_Width,1);
		}
	FreeVec(pix);
	ok=1;
	}
return ok;
}

static BOOL GetBody_XPKBP8(struct IFFHandle *iff,struct RGHD *rh,struct BitMap *bm)
{
BOOL ok=0;
struct XpkFib *fib;
if(!XpkOpenTags(&fib,XPK_InFH,iff->iff_Stream,TAG_END))
	{
	UBYTE *mem;
	if(mem=AllocVec(fib->xf_NLen,MEMF_PUBLIC))
		{
		ULONG d,p=-1,clen=-1;
		for(d=0;d<rh->rgfx_Depth;d++)
			{
			ULONG y;
			UBYTE *pl=bm->Planes[d];
			for(y=0;y<rh->rgfx_Height;y++,pl+=bm->BytesPerRow)
				{
				ULONG x;
				for(x=0;x<rh->rgfx_BytesPerLine;x++)
					{
					if(p>=clen)
						{
						clen=XpkRead(fib,mem,fib->xf_NLen);
						p=0;
						}
					pl[x]=mem[p];
					p++;
					}
				}
			}
		ok=1;
		FreeVec(mem);
		}
	XpkClose(fib);
	}
return ok;
}

static BOOL GetBody_XPKBC8(struct IClass *cl,Object *obj,struct IFFHandle *iff,struct RGHD *rh)
{
BOOL ok=0;
struct XpkFib *fib;
if(!XpkOpenTags(&fib,XPK_InFH,iff->iff_Stream,TAG_END))
	{
	UBYTE *mem;
	if(mem=AllocVec(fib->xf_NLen,MEMF_PUBLIC))
		{
		UBYTE *pix;
		if(pix=AllocVec(rh->rgfx_Width,MEMF_PUBLIC))
			{
			ULONG y,p=-1,clen=-1;
			for(y=0;y<rh->rgfx_Height;y++)
				{
				ULONG x;
				for(x=0;x<rh->rgfx_Width;x++)
					{
					if(p>=clen)
						{
						clen=XpkRead(fib,mem,fib->xf_NLen);
						p=0;
						}
					pix[x]=mem[p];
					p++;
					}
				DoSuperMethod(cl,obj,PDTM_WRITEPIXELARRAY,pix,RECTFMT_LUT8,rh->rgfx_Width,0,y,rh->rgfx_Width,1);
				}
			FreeVec(pix);
			ok=1;
			}
		FreeVec(mem);
		}
	XpkClose(fib);
	}
return ok;
}

static BOOL GetBody_XPKBC8_39(struct IFFHandle *iff,struct RGHD *rh,struct BitMap *bm)
{
BOOL ok=0;
struct XpkFib *fib;
if(!XpkOpenTags(&fib,XPK_InFH,iff->iff_Stream,TAG_END))
	{
	UBYTE *mem;
	if(mem=AllocVec(fib->xf_NLen,MEMF_PUBLIC))
		{
		struct BitMap *tbm;
		if(tbm=AllocBitMap(rh->rgfx_Width,1,rh->rgfx_Depth,0,bm))
			{
			UBYTE *pix;
			if(pix=AllocVec(((rh->rgfx_Width+15)>>4)<<4,MEMF_PUBLIC))
				{
				ULONG y,p=-1,clen=-1;
				struct RastPort rp,trp;
				InitRastPort(&rp);
				InitRastPort(&trp);
				rp.BitMap=bm;
				trp.BitMap=tbm;
				for(y=0;y<rh->rgfx_Height;y++)
					{
					ULONG x;
					for(x=0;x<rh->rgfx_Width;x++)
						{
						if(p>=clen)
							{
							clen=XpkRead(fib,mem,fib->xf_NLen);
							p=0;
							}
						pix[x]=mem[p];
						p++;
						}
					WritePixelLine8(&rp,0,y,rh->rgfx_Width,pix,&trp);
					}
				FreeVec(pix);
				ok=1;
				}
			FreeBitMap(tbm);
			}
		FreeVec(mem);
		}
	XpkClose(fib);
	}
return ok;
}

static BOOL GetBody_XPKRGB(struct IClass *cl,Object *obj,struct IFFHandle *iff,struct RGHD *rh)
{
BOOL ok=0;
struct XpkFib *fib;
if(!XpkOpenTags(&fib,XPK_InFH,iff->iff_Stream,TAG_END))
	{
	UBYTE *mem;
	if(mem=AllocVec(fib->xf_NLen,MEMF_PUBLIC))
		{
		UBYTE *pix;
		if(pix=AllocVec(rh->rgfx_BytesPerLine,MEMF_PUBLIC))
			{
			ULONG y,p=-1,clen=-1;
			for(y=0;y<rh->rgfx_Height;y++)
				{
				ULONG x;
				for(x=0;x<rh->rgfx_BytesPerLine;x++)
					{
					if(p>=clen)
						{
						clen=XpkRead(fib,mem,fib->xf_NLen);
						p=0;
						}
					pix[x]=mem[p];
					p++;
					}
				DoSuperMethod(cl,obj,PDTM_WRITEPIXELARRAY,pix,RECTFMT_RGB,rh->rgfx_BytesPerLine,0,y,rh->rgfx_Width,1);
				}
			FreeVec(pix);
			ok=1;
			}
		FreeVec(mem);
		}
	XpkClose(fib);
	}
return ok;
}

static BOOL GetPicture(struct IClass *cl,Object *obj)
{
BOOL ok=0;
struct IFFHandle *iff;
struct BitMapHeader *bh;
GetDTAttrs(obj,DTA_Handle,&iff,PDTA_BitMapHeader,&bh,TAG_END);
if(PropChunks(iff,chunks,8)) return 0;
if(StopOnExit(iff,ID_RGFX,ID_RBOD)) return 0;
for(;;)
	{
	struct StoredProperty *sp;
	long err=ParseIFF(iff,IFFPARSE_SCAN);
	if(err!=IFFERR_EOC) break;
	if(sp=FindProp(iff,ID_RGFX,ID_NAME))
		{
		SetDTAttrs(obj,0,0,DTA_ObjName,sp->sp_Data,TAG_END);
		}
	if(sp=FindProp(iff,ID_RGFX,ID_AUTH))
		{
		SetDTAttrs(obj,0,0,DTA_ObjAuthor,sp->sp_Data,TAG_END);
		}
	if(sp=FindProp(iff,ID_RGFX,ID_ANNO))
		{
		SetDTAttrs(obj,0,0,DTA_ObjAnnotation,sp->sp_Data,TAG_END);
		}
	if(sp=FindProp(iff,ID_RGFX,ID_Copyright))
		{
		SetDTAttrs(obj,0,0,DTA_ObjCopyright,sp->sp_Data,TAG_END);
		}
	if(sp=FindProp(iff,ID_RGFX,ID_RGHD))
		{
		struct RGHD *rh=sp->sp_Data;
		struct ContextNode *cn;
		if((rh->rgfx_Compression==RCMT_NOCOMPRESSION)||XpkBase)
			{
			GetRGHD(obj,rh,bh);
			if(GetRSCM(obj,iff,bh)==INVALID_ID) break;
			if(!GetRCOL(obj,iff,bh)) break;
			if((cn=CurrentChunk(iff))&&(cn->cn_ID=='RBOD'))
				{
				struct BitMap *bm;
				if(SuperClassBase->lib_Version==43)
					{
					if(rh->rgfx_BitMapType==RMBT_BYTEPLANAR8)
						{
						if(bm=AllocBitMap(rh->rgfx_Width,rh->rgfx_Height,rh->rgfx_Depth,BMF_CLEAR|BMF_DISPLAYABLE,0)) SetDTAttrs(obj,0,0,PDTA_BitMap,bm,TAG_END);
						}
					else
						{
						SetDTAttrs(obj,0,0,PDTA_SourceMode,PMODE_V43,TAG_END);
						GetDTAttrs(obj,PDTA_BitMap,&bm,TAG_END);
						}
					if(bm)
						{
						switch(rh->rgfx_Compression)
							{
							case RCMT_NOCOMPRESSION:
								switch(rh->rgfx_BitMapType)
									{
									case RMBT_BYTEPLANAR8:
										ok=GetBody_BP8(iff,rh,bm);
										break;
									case RMBT_BYTECHUNKY8:
										ok=GetBody_BC8(cl,obj,iff,rh);
										break;
									case RMBT_3BYTERGB24:
										ok=GetBody_RGB(cl,obj,iff,rh);
									}
								break;
							case RCMT_XPK:
								switch(rh->rgfx_BitMapType)
									{
									case RMBT_BYTEPLANAR8:
										ok=GetBody_XPKBP8(iff,rh,bm);
										break;
									case RMBT_BYTECHUNKY8:
										ok=GetBody_XPKBC8(cl,obj,iff,rh);
										break;
									case RMBT_3BYTERGB24:
										ok=GetBody_XPKRGB(cl,obj,iff,rh);
									}
							}
						}
					}
				else if(rh->rgfx_Depth!=24)
					{
					if(bm=AllocBitMap(rh->rgfx_Width,rh->rgfx_Height,rh->rgfx_Depth,BMF_CLEAR|BMF_DISPLAYABLE,0))
						{
						SetDTAttrs(obj,0,0,PDTA_BitMap,bm,TAG_END);
						switch(rh->rgfx_Compression)
							{
							case RCMT_NOCOMPRESSION:
								switch(rh->rgfx_BitMapType)
									{
									case RMBT_BYTEPLANAR8:
										ok=GetBody_BP8(iff,rh,bm);
										break;
									case RMBT_BYTECHUNKY8:
										ok=GetBody_BC8_39(iff,rh,bm);
									}
								break;
							case RCMT_XPK:
								switch(rh->rgfx_BitMapType)
									{
									case RMBT_BYTEPLANAR8:
										ok=GetBody_XPKBP8(iff,rh,bm);
										break;
									case RMBT_BYTECHUNKY8:
										ok=GetBody_XPKBC8_39(iff,rh,bm);
									}
							}
						}
					}
				break;
				}
			}
		}
	}
return ok;
}

static ULONG mNew(struct IClass *cl,Object *obj,Msg msg)
{
if(obj=(Object *)DoSuperMethodA(cl,obj,msg))
	{
	if(!GetPicture(cl,obj))
		{
		CoerceMethod(cl,obj,OM_DISPOSE);
		obj=0;
		}
	}
return ULONG(obj);
}

ULONG Dispatcher(struct IClass *cl,Object *obj,Msg msg)
{
ULONG retval;
switch(msg->MethodID)
	{
	case OM_NEW:
		retval=mNew(cl,obj,msg);
		break;
	default:
		retval=DoSuperMethodA(cl,obj,msg);
		}
return retval;
}

