#include <clib/alib_protos.h>
#include <pragma/datatypes_lib.h>
#include <pragma/dos_lib.h>
#include <pragma/exec_lib.h>
#include <pragma/graphics_lib.h>
#include <pragma/iffparse_lib.h>
#include <pragma/intuition_lib.h>
#include <pragma/utility_lib.h>
#include <pragma/xpkmaster_lib.h>
#include <datatypes/pictureclass.h>
#include <exec/memory.h>
#include <intuition/icclass.h>
#include <string.h>
#include "class.h"
#include "rgfx.h"

struct Arg1
{
char *xpk;
long *xpkmode;
char *password;
};

ULONG SaveRGFX(IClass *cl,Object *obj,dtWrite *msg);

static long iff2dos[]=
{
0,
0,
DTERROR_INVALID_DATA,
ERROR_NO_FREE_STORE,
ERROR_SEEK_ERROR,
ERROR_SEEK_ERROR,
ERROR_SEEK_ERROR,
DTERROR_INVALID_DATA,
DTERROR_INVALID_DATA,
ERROR_OBJECT_WRONG_TYPE,
ERROR_REQUIRED_ARG_MISSING,
0xDEADDEAD
};

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,
ID_RGFX,ID_FVER
};

void ReadPrefs(Data *data)
{
char *var;
strcpy(data->xpk,"NUKE");
data->xpkmode=100;
if(data->password)
	{
	FreeVec(data->password);
	data->password=0;
	}
if(var=(char *)AllocVec(256,0))
	{
	if(GetVar("ENV:DataTypes/rgfx.prefs",var,256,LV_VAR|GVF_GLOBAL_ONLY)>=0)
		{
		RDArgs *rdargs;
      if(rdargs=(RDArgs *)AllocDosObject(DOS_RDARGS,0))
			{
			RDArgs *args;
			Arg1 para;
			rdargs->RDA_Source.CS_Buffer=var;
			rdargs->RDA_Source.CS_Length=strlen(var);
			rdargs->RDA_Source.CS_CurChr=0;
			memset(&para,0,sizeof(Arg1));
			if(args=ReadArgs("XPK/A,XPKMODE/A/N,PASSWORD",(long *)&para,rdargs))
				{
				strncpy(data->xpk,para.xpk,4);
				data->xpk[4]=0;
				data->xpkmode=*para.xpkmode;
				if(data->xpkmode<0) data->xpkmode=0;
				else if(data->xpkmode>100) data->xpkmode=100;
				if(para.password)
					{
					if(data->password=(char *)AllocVec(strlen(para.password)+1,MEMF_CLEAR)) strcpy(data->password,para.password);
					}
				FreeArgs(args);
				}
			FreeDosObject(DOS_RDARGS,rdargs);
			}
		}
	FreeVec(var);
	}
}

ULONG Colors2DT(Object *obj,UBYTE *color)
{
UBYTE *cr;
ULONG *cregs,i,num,error=0;
GetDTAttrs(obj,PDTA_NumColors,&num,PDTA_ColorRegisters,&cr,PDTA_CRegs,&cregs,TAG_END);
if(cr&&cregs)
	{
	for(i=0;i<num;i++,cr+=3,cregs+=3,color+=4)
		{
		cr[0]=color[1];
		cr[1]=color[2];
		cr[2]=color[3];
		cregs[0]=cr[0]<<24;
		cregs[1]=cr[1]<<24;
		cregs[2]=cr[2]<<24;
		}
	}
else error=ERROR_NO_FREE_STORE;
return error;
}

static void GetRGHD(Object *obj,RGHD *rh,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?rh->rgfx_PageWidth:bh->bmh_Width;
bh->bmh_PageHeight=rh->rgfx_PageHeight?rh->rgfx_PageHeight:bh->bmh_Height;
bh->bmh_Depth=rh->rgfx_Depth;
bh->bmh_Compression=cmpByteRun1;
bh->bmh_XAspect=rh->rgfx_XAspect;
bh->bmh_YAspect=rh->rgfx_YAspect;
}

static ULONG GetRSCM(Object *obj,IFFHandle *iff,BitMapHeader *bh,Data *data,ULONG rgfxdepth)
{
ULONG mode=0,numc=0;
StoredProperty *sp;
mode=INVALID_ID;
if(sp=FindProp(iff,ID_RGFX,ID_RSCM))
	{
	RSCM *rscm=(RSCM *)sp->sp_Data;
	if(!ModeNotAvailable(rscm->rscm_AGA))
		{
		ULONG dipf=0;
		if(rscm->rscm_AGA&HAM_KEY)
			{
			numc=(bh->bmh_Depth==8)?64:16;
			dipf=DIPF_IS_HAM;
			}
		else if(rscm->rscm_AGA&EXTRAHALFBRITE_KEY)
			{
			numc=32;
			dipf=DIPF_IS_EXTRAHALFBRITE;
			}
		if(dipf) mode=BestModeID(
		BIDTAG_NominalWidth,bh->bmh_PageWidth,BIDTAG_NominalHeight,bh->bmh_PageHeight,
		BIDTAG_DesiredWidth,bh->bmh_Width,BIDTAG_DesiredHeight,bh->bmh_Height,BIDTAG_Depth,bh->bmh_Depth,
		BIDTAG_DIPFMustHave,dipf,BIDTAG_DIPFMustNotHave,DIPF_IS_DUALPF|DIPF_IS_PF2PRI,TAG_END);
		}
	}
if(mode==INVALID_ID)
	{
	mode=BestModeID(
	BIDTAG_NominalWidth,bh->bmh_PageWidth,BIDTAG_NominalHeight,bh->bmh_PageHeight,
	BIDTAG_DesiredWidth,bh->bmh_Width,BIDTAG_DesiredHeight,bh->bmh_Height,
	BIDTAG_Depth,bh->bmh_Depth,
	BIDTAG_DIPFMustNotHave,DIPF_IS_DUALPF|DIPF_IS_PF2PRI|DIPF_IS_HAM|DIPF_IS_EXTRAHALFBRITE,TAG_END);
	numc=1<<bh->bmh_Depth;
	}
if(mode!=INVALID_ID)
	{
	SetDTAttrs(obj,0,0,PDTA_ModeID,mode,TAG_END);
	if(numc) SetDTAttrs(obj,0,0,PDTA_NumColors,numc,TAG_END);
	}
return mode;
}

static ULONG GetRCOL(Object *obj,IFFHandle *iff,RGHD *rh)
{
ULONG error=0;
if(rh->rgfx_Depth<=8)
	{
	StoredProperty *sp;
	if(sp=FindProp(iff,ID_RGFX,ID_RCOL))
		{
		RCOL *rcol=(RCOL *)sp->sp_Data;
		ULONG n,*cregs;
		UBYTE *cr,*p;
		GetDTAttrs(obj,PDTA_NumColors,&n,PDTA_ColorRegisters,&cr,PDTA_CRegs,&cregs,TAG_END);
		if(cr&&cregs)
			{
			ULONG i;
			n*=3;
			for(i=0,p=rcol->rcol_Colors[0];i<n;i++,p++,cr++,cregs++)
				{
				*cr=*p;
				*cregs=(*p)<<24;
				}
			}
		else error=ERROR_NO_FREE_STORE;
		}
	else error=ERROR_REQUIRED_ARG_MISSING;
	}
return error;
}

static ULONG GetBody_BP8(IFFHandle *iff,RGHD *rh,BitMap *bm)
{
ULONG error=0,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)
		{
		long err;
		if((err=ReadChunkBytes(iff,pl,rh->rgfx_BytesPerLine)<0))
			{
			error=iff2dos[-err];
			break;
			}
		}
	}
return error;
}

static ULONG GetBody_BC8(Object *obj,IFFHandle *iff,RGHD *rh)
{
ULONG error=0;
long err;
UBYTE *pix;
if(pix=(UBYTE *)AllocVec(rh->rgfx_Width*rh->rgfx_Height,0))
	{
	if((err=ReadChunkBytes(iff,pix,rh->rgfx_Width*rh->rgfx_Height))<0) error=iff2dos[-err];
	else DoMethod(obj,PDTM_WRITEPIXELARRAY,pix,PBPAFMT_LUT8,rh->rgfx_BytesPerLine,0,0,rh->rgfx_Width,rh->rgfx_Height);
	FreeVec(pix);
	}
else
	{
	if(pix=(UBYTE *)AllocVec(rh->rgfx_Width,0))
		{
		ULONG y;
		for(y=0;y<rh->rgfx_Height;y++)
			{
			long err;
			if((err=ReadChunkBytes(iff,pix,rh->rgfx_Width))<0)
				{
				error=iff2dos[-err];
				break;
				}
			DoMethod(obj,PDTM_WRITEPIXELARRAY,pix,PBPAFMT_LUT8,rh->rgfx_BytesPerLine,0,y,rh->rgfx_Width,1);
			}
		FreeVec(pix);
		}
	else error=ERROR_NO_FREE_STORE;
	}
return error;
}

static ULONG GetBody_RGB_V43(Object *obj,IFFHandle *iff,RGHD *rh)
{
ULONG error=0;
UBYTE *pix;
long err;
if(pix=(UBYTE *)AllocVec(rh->rgfx_BytesPerLine*rh->rgfx_Height,0))
	{
	if((err=ReadChunkBytes(iff,pix,rh->rgfx_BytesPerLine*rh->rgfx_Height))<0) error=iff2dos[-err];
	else DoMethod(obj,PDTM_WRITEPIXELARRAY,pix,PBPAFMT_RGB,rh->rgfx_BytesPerLine,0,0,rh->rgfx_Width,rh->rgfx_Height);
	FreeVec(pix);
	}
else if(pix=(UBYTE *)AllocVec(rh->rgfx_BytesPerLine,0))
	{
	ULONG y;
	for(y=0;y<rh->rgfx_Height;y++)
		{
		if((err=ReadChunkBytes(iff,pix,rh->rgfx_BytesPerLine))<0)
			{
			error=iff2dos[-err];
			break;
			}
		DoMethod(obj,PDTM_WRITEPIXELARRAY,pix,PBPAFMT_RGB,rh->rgfx_BytesPerLine,0,y,rh->rgfx_Width,1);
		}
	FreeVec(pix);
	}
else error=ERROR_NO_FREE_STORE;
return error;
}

static ULONG GetBody_XPKBP8(IFFHandle *iff,RGHD *rh,BitMap *bm)
{
ULONG error=0,d,y;
XpkFib *fib;
UBYTE *mem,*pl;
if(!XpkUnpackTags(XPK_InFH,iff->iff_Stream,XPK_GetOutBuf,&mem,XPK_GetOutBufLen,&y,TAG_END))
	{
	UBYTE *p=mem;
	for(d=0;d<rh->rgfx_Depth;d++)
		{
		pl=bm->Planes[d];
		for(y=0;y<rh->rgfx_Height;y++,pl+=bm->BytesPerRow,p+=rh->rgfx_BytesPerLine) CopyMem(p,pl,bm->BytesPerRow);
		}
	FreeMem(mem,y);
	}
else if(!XpkOpenTags(&fib,XPK_InFH,iff->iff_Stream,TAG_END))
	{
	if(mem=(UBYTE *)AllocVec(fib->xf_NLen,0))
		{
		long p=-1,clen=-1;
		for(d=0;d<rh->rgfx_Depth;d++)
			{
			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)
						{
						if((clen=XpkRead(fib,mem,fib->xf_NLen))<0)
							{
							error=DTERROR_INVALID_DATA;
							y=rh->rgfx_Height;
							d=rh->rgfx_Depth;
							break;
							}
						p=0;
						}
					pl[x]=mem[p];
					p++;
					}
				}
			}
		FreeVec(mem);
		}
	else error=ERROR_NO_FREE_STORE;
	XpkClose(fib);
	}
else error=DTERROR_INVALID_DATA;
return error;
}

static ULONG GetBody_XPKBC8(Object *obj,IFFHandle *iff,RGHD *rh)
{
ULONG error=0,pixlen;
UBYTE *pix;
if(!XpkUnpackTags(XPK_InFH,iff->iff_Stream,XPK_GetOutBuf,&pix,XPK_GetOutBufLen,&pixlen,TAG_END))
	{
	DoMethod(obj,PDTM_WRITEPIXELARRAY,pix,PBPAFMT_LUT8,rh->rgfx_BytesPerLine,0,0,rh->rgfx_Width,rh->rgfx_Height);
	FreeMem(pix,pixlen);
	}
else
	{
	ULONG y;
	XpkFib *fib;
	if(!XpkOpenTags(&fib,XPK_InFH,iff->iff_Stream,TAG_END))
		{
		UBYTE *mem;
		if(mem=(UBYTE *)AllocVec(fib->xf_NLen,0))
			{
			if(pix=(UBYTE *)AllocVec(rh->rgfx_Width,0))
				{
				long 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)
							{
							if((clen=XpkRead(fib,mem,fib->xf_NLen))<0)
								{
								error=DTERROR_INVALID_DATA;
								y=rh->rgfx_Height;
								break;
								}
							p=0;
							}
						pix[x]=mem[p];
						p++;
						}
					DoMethod(obj,PDTM_WRITEPIXELARRAY,pix,PBPAFMT_LUT8,rh->rgfx_BytesPerLine,0,y,rh->rgfx_Width,1);
					}
				FreeVec(pix);
				}
			else error=ERROR_NO_FREE_STORE;
			FreeVec(mem);
			}
		else error=ERROR_NO_FREE_STORE;
		XpkClose(fib);
		}
	else error=DTERROR_INVALID_DATA;
	}
return error;
}

static ULONG GetBody_XPKRGB_V43(Object *obj,IFFHandle *iff,RGHD *rh)
{
ULONG error=0,pixlen;
XpkFib *fib;
UBYTE *pix;
if(!XpkUnpackTags(XPK_InFH,iff->iff_Stream,XPK_GetOutBuf,&pix,XPK_GetOutBufLen,&pixlen,TAG_END))
	{
	DoMethod(obj,PDTM_WRITEPIXELARRAY,pix,PBPAFMT_RGB,rh->rgfx_BytesPerLine,0,0,rh->rgfx_Width,rh->rgfx_Height);
	FreeMem(pix,pixlen);
	}
else if(!XpkOpenTags(&fib,XPK_InFH,iff->iff_Stream,TAG_END))
	{
	UBYTE *mem;
	if(mem=(UBYTE *)AllocVec(fib->xf_NLen,0))
		{
		if(pix=(UBYTE *)AllocVec(rh->rgfx_BytesPerLine,0))
			{
			long p=-1,clen=-1;
			ULONG y;
			for(y=0;y<rh->rgfx_Height;y++)
				{
				ULONG x;
				for(x=0;x<rh->rgfx_BytesPerLine;x++)
					{
					if(p>=clen)
						{
						if((clen=XpkRead(fib,mem,fib->xf_NLen))<0)
							{
							error=DTERROR_INVALID_DATA;
							y=rh->rgfx_Height;
							break;
							}
						p=0;
						}
					pix[x]=mem[p];
					p++;
					}
				DoMethod(obj,PDTM_WRITEPIXELARRAY,pix,PBPAFMT_RGB,rh->rgfx_BytesPerLine,0,y,rh->rgfx_Width,1);
				}
			FreeVec(pix);
			}
		else error=ERROR_NO_FREE_STORE;
		FreeVec(mem);
		}
	else error=ERROR_NO_FREE_STORE;
	XpkClose(fib);
	}
else error=DTERROR_INVALID_DATA;
return error;
}

static ULONG GetPicture(IClass *cl,Object *obj)
{
ULONG error=0;
long err;
IFFHandle *iff;
BitMapHeader *bh;
GetDTAttrs(obj,DTA_Name,&bh,TAG_END);
if(bh) SetDTAttrs(obj,0,0,DTA_ObjName,bh,TAG_END);
GetDTAttrs(obj,DTA_Handle,&iff,PDTA_BitMapHeader,&bh,TAG_END);
if(bh)
	{
	if(!iff)
		{
		GetDTAttrs(obj,DTA_SourceType,&err,TAG_END);
		if(err!=DTST_RAM) return ERROR_REQUIRED_ARG_MISSING;
		else return 0;
		}
	if((err=PropChunks(iff,chunks,sizeof(chunks)/(2*sizeof(long))))<0) return iff2dos[-err];
	if((err=StopOnExit(iff,ID_RGFX,ID_RBOD))<0) return iff2dos[-err];
	for(;;)
		{
		StoredProperty *sp;
		if((err=ParseIFF(iff,IFFPARSE_SCAN))!=IFFERR_EOC)
			{
			error=iff2dos[-err];
			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_FVER)) SetDTAttrs(obj,0,0,DTA_ObjVersion,sp->sp_Data,TAG_END);
		if(sp=FindProp(iff,ID_RGFX,ID_RGHD))
			{
			RGHD *rh=(RGHD *)sp->sp_Data;
			Data *data=(Data *)INST_DATA(cl,obj);
			ContextNode *cn;
			ReadPrefs(data);
			GetRGHD(obj,rh,bh);
			if(rh->rgfx_Depth<=8)
				{
				if(GetRSCM(obj,iff,bh,data,rh->rgfx_Depth)==INVALID_ID) break;
				}
			if(error=GetRCOL(obj,iff,rh)) break;
			if((cn=CurrentChunk(iff))&&(cn->cn_ID=='RBOD'))
				{
				BitMap *bm;
				switch(rh->rgfx_BitMapType)
					{
					case RMBT_BYTEPLANAR8:
						if(bm=AllocBitMap(rh->rgfx_Width,rh->rgfx_Height,rh->rgfx_Depth,BMF_CLEAR|BMF_DISPLAYABLE|BMF_STANDARD,0)) SetDTAttrs(obj,0,0,DTA_NominalHoriz,bh->bmh_Width,DTA_NominalVert,bh->bmh_Height,PDTA_BitMap,bm,TAG_END);
						else error=ERROR_NO_FREE_STORE;
						break;
					case RMBT_BYTECHUNKY8:
					case RMBT_3BYTERGB24:
						SetDTAttrs(obj,0,0,DTA_ErrorNumber,&error,DTA_NominalHoriz,bh->bmh_Width,DTA_NominalVert,bh->bmh_Height,PDTA_SourceMode,PMODE_V43,PDTA_ModeID,0,TAG_END);
						break;
					default:
						error=ERROR_NOT_IMPLEMENTED;
					}
				if(!error)
					{
					switch(rh->rgfx_Compression)
						{
						case RCMT_NOCOMPRESSION:
							switch(rh->rgfx_BitMapType)
								{
								case RMBT_BYTEPLANAR8:
									error=GetBody_BP8(iff,rh,bm);
									break;
								case RMBT_BYTECHUNKY8:
									error=GetBody_BC8(obj,iff,rh);
									break;
								case RMBT_3BYTERGB24:
									error=GetBody_RGB_V43(obj,iff,rh);
								}
							break;
						case RCMT_XPK:
							switch(rh->rgfx_BitMapType)
								{
								case RMBT_BYTEPLANAR8:
									error=GetBody_XPKBP8(iff,rh,bm);
									break;
								case RMBT_BYTECHUNKY8:
									error=GetBody_XPKBC8(obj,iff,rh);
									break;
								case RMBT_3BYTERGB24:
									error=GetBody_XPKRGB_V43(obj,iff,rh);
								}
						}
					}
				break;
				}
			else error=DTERROR_INVALID_DATA;
			}
		else error=DTERROR_INVALID_DATA;
		}
	}
else error=ERROR_NO_FREE_STORE;
return error;
}

static ULONG mNew(IClass *cl,Object *obj,opSet *msg)
{
TagItem *ti;
if(ti=FindTagItem(DTA_SourceType,(((opSet *)msg)->ops_AttrList)))
	{
	if(ti->ti_Data!=DTST_FILE&&ti->ti_Data!=DTST_CLIPBOARD&&ti->ti_Data!=DTST_RAM)
		{
		SetIoErr(ERROR_OBJECT_WRONG_TYPE);
		return 0;
		}
	}
if(obj==(Object *)cl)
	{
	if(obj=(Object *)DoSuperMethodA(cl,obj,Msg(msg)))
		{
		ULONG error;
		if(error=GetPicture(cl,obj))
			{
			SetIoErr(error);
			CoerceMethod(cl,obj,OM_DISPOSE);
			obj=0;
			}
		}
	}
else
	{
	SetIoErr(ERROR_NOT_IMPLEMENTED);
	obj=0;
	}
return ULONG(obj);
}

static ULONG mDispose(IClass *cl,Object *obj,Msg msg)
{
Data *data=(Data *)INST_DATA(cl,obj);
if(data->password) FreeVec(data->password);
return DoSuperMethodA(cl,obj,msg);
}

static ULONG mSet(IClass *cl,Object *obj,opSet *msg)
{
ULONG retval;
if(retval=DoSuperMethodA(cl,obj,Msg(msg)))
	{
	RastPort *rp;
	if(rp=ObtainGIRPort(msg->ops_GInfo))
		{
		DoMethod(obj,GM_RENDER,msg->ops_GInfo,rp,GREDRAW_UPDATE);
		ReleaseGIRPort(rp);
		retval=0;
		}
	}
return retval;
}

static ULONG mWrite(IClass *cl,Object *obj,dtWrite *msg)
{
return (msg->dtw_Mode==DTWM_IFF)?DoSuperMethodA(cl,obj,Msg(msg)):SaveRGFX(cl,obj,msg);
}

extern "C" ULONG Dispatcher(register __a0 IClass *cl,register __a2 Object *obj,register __a1 Msg msg)
{
ULONG retval;
switch(msg->MethodID)
	{
	case OM_NEW:
		retval=mNew(cl,obj,(opSet *)msg);
		break;
	case OM_DISPOSE:
		retval=mDispose(cl,obj,msg);
		break;
	case OM_UPDATE:
		if(DoMethod(obj,ICM_CHECKLOOP)) break;
	case OM_SET:
		retval=mSet(cl,obj,(opSet *)msg);
		break;
	case DTM_WRITE:
		retval=mWrite(cl,obj,(dtWrite *)msg);
		break;
	default:
		retval=DoSuperMethodA(cl,obj,msg);
		}
return retval;
}

