#include <clib/alib_protos.h>
#include <clib/datatypes_protos.h>
#include <clib/dos_protos.h>
#include <clib/exec_protos.h>
#include <clib/intuition_protos.h>
#include <clib/graphics_protos.h>
#include <clib/render_protos.h>
#include <clib/utility_protos.h>
#include <cybergraphx/cybergraphics.h>
#include <datatypes/pictureclass.h>
#include <dos/dostags.h>
#include <exec/memory.h>
#include <intuition/icclass.h>
#include <stdio.h>
#include <string.h>

struct Arg1
{
long *mode,*res,*dither,*depth;
};

struct Arg2
{
long mode,res,dither,depth;
};

extern Library *SuperClassBase,*RenderBase;

static void ReadPrefs(Arg2 *arg)
{
char *var;
arg->mode=1;
arg->res=2;
arg->dither=0;
arg->depth=8;
if(var=(char *)AllocVec(256,0))
	{
	if(GetVar("ENV:DataTypes/pcd.prefs",var,256,LV_VAR|GVF_GLOBAL_ONLY)>=0)
		{
		RDArgs *rdargs;
      if(rdargs=(RDArgs *)AllocDosObject(DOS_RDARGS,0))
			{
			RDArgs *args;
			Arg1 para={0,0,0,0};
			rdargs->RDA_Source.CS_Buffer=var;
			rdargs->RDA_Source.CS_Length=strlen(var);
			rdargs->RDA_Source.CS_CurChr=0;
			if(args=ReadArgs("MODE/A/N,RESOLUTION/A/N,DITHER/A/N,DEPTH/A/N",(long *)&para,rdargs))
				{
				if(para.mode)
					{
					arg->mode=*para.mode;
					if(arg->mode<0) arg->mode=0;
					else if(arg->mode>1) arg->mode=1;
					}
				if(para.res)
					{
					arg->res=*para.res;
					if(arg->res<0) arg->res=0;
					else if(arg->res>5) arg->res=5;
					}
				if(para.dither)
					{
					arg->dither=*para.dither;
					if(arg->dither<0) arg->dither=0;
					else if(arg->dither>1) arg->dither=1;
					}
				if(para.depth)
					{
					arg->depth=*para.depth;
					if(arg->depth<3) arg->depth=3;
					else if(arg->depth>8) arg->depth=8;
					}
				FreeArgs(args);
				}
			FreeDosObject(DOS_RDARGS,rdargs);
			}
		}
	FreeVec(var);
	}
if(SuperClassBase->lib_Version!=43) arg->mode=0;
}

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 ULONG ReadP6(Object *obj,BitMapHeader *bh,BitMap *bm,BPTR file,Arg2 *arg)
{
ULONG error=0;
UBYTE *rgb;
if(rgb=(UBYTE *)AllocVec(bh->bmh_Width*bh->bmh_Height*4,MEMF_CLEAR))
	{
	UBYTE *buffer;
	if(buffer=(UBYTE *)AllocVec(bh->bmh_Width*3,0))
		{
		UWORD y;
		UBYTE *p1=rgb;
		for(y=0;y<bh->bmh_Height;y++)
			{
			UWORD x;
			UBYTE *p2=buffer;
			if(Read(file,buffer,bh->bmh_Width*3)!=bh->bmh_Width*3)
				{
				error=DTERROR_NOT_ENOUGH_DATA;
				break;
				}
			for(x=0;x<bh->bmh_Width;x++,p1+=4,p2+=3)
				{
				p1[1]=p2[0];
				p1[2]=p2[1];
				p1[3]=p2[2];
				}
			}
		FreeVec(buffer);
		if(!error)
			{
			APTR rmh;
			if(rmh=CreateRMHandler(RND_MemType,RMHTYPE_POOL,TAG_END))
				{
				ULONG *palette;
				if(palette=CreatePalette(RND_RMHandler,rmh,TAG_END))
					{
					APTR hst;
					if(hst=CreateHistogram(RND_RMHandler,rmh,TAG_END))
						{
						if(AddRGBImage(hst,(ULONG *)rgb,bh->bmh_Width,bh->bmh_Height,TAG_END)==ADDH_SUCCESS)
							{
							if(ExtractPalette(hst,palette,1<<bh->bmh_Depth,TAG_END)==EXTP_SUCCESS)
								{
								UBYTE *chunky;
								if(chunky=(UBYTE *)AllocVec(bh->bmh_Width*bh->bmh_Height,0))
									{
									if(Render((ULONG *)rgb,bh->bmh_Width,bh->bmh_Height,chunky,palette,RND_DitherMode,arg->dither,TAG_END)==REND_SUCCESS)
										{
										UBYTE *color;
										if(color=(UBYTE *)AllocVec((1<<bh->bmh_Depth)*4,0))
											{
											ExportPalette(palette,color,TAG_END);
											if(!(error=Colors2DT(obj,color))) Chunky2BitMap(chunky,0,0,bh->bmh_Width,bh->bmh_Height,bm,0,0,TAG_END);
											FreeVec(color);
											}
										else error=ERROR_NO_FREE_STORE;
										}
									else error=ERROR_NO_FREE_STORE;
									FreeVec(chunky);
									}
								else error=ERROR_NO_FREE_STORE;
								}
							else error=ERROR_NO_FREE_STORE;
							}
						else error=ERROR_NO_FREE_STORE;
						DeleteHistogram(hst);
						}
					else error=ERROR_NO_FREE_STORE;
					DeletePalette(palette);
					}
				else error=ERROR_NO_FREE_STORE;
				DeleteRMHandler(rmh);
				}
			else error=ERROR_NO_FREE_STORE;
			}
		}
	else error=ERROR_NO_FREE_STORE;
	FreeVec(rgb);
	}
else error=ERROR_NO_FREE_STORE;
return error;
}

static ULONG ReadP6_V43(Object *obj,BitMapHeader *bh,BPTR file)
{
ULONG error=0,len=bh->bmh_Width*bh->bmh_Height*3;
UBYTE *pix;
if(pix=(UBYTE *)AllocVec(len,0))
	{
	if(Read(file,pix,len)==len) DoMethod(obj,PDTM_WRITEPIXELARRAY,pix,RECTFMT_RGB,bh->bmh_Width*3,0,0,bh->bmh_Width,bh->bmh_Height);
	else error=DTERROR_NOT_ENOUGH_DATA;
	FreeVec(pix);
	}
else if(pix=(UBYTE *)AllocVec(bh->bmh_Width*3,0))
	{
	UWORD y;
	for(y=0;y<bh->bmh_Height;y++)
		{
		if(Read(file,pix,bh->bmh_Width*3)!=bh->bmh_Width*3)
			{
			error=DTERROR_NOT_ENOUGH_DATA;
			break;
			}
		DoMethod(obj,PDTM_WRITEPIXELARRAY,pix,RECTFMT_RGB,bh->bmh_Width*3,0,y,bh->bmh_Width,1);
		}
	FreeVec(pix);
	}
else error=ERROR_NO_FREE_STORE;
return error;
}

static ULONG ReadNum(BPTR file)
{
ULONG retval=0;
for(;;)
	{
	char c;
	if(Read(file,&c,1)!=1) return 0;
	if(c==10||c==32) break;
	retval*=10;
	retval+=c-'0';
	}
return retval;
}

static ULONG GetPicture(IClass *cl,Object *obj)
{
ULONG error=0;
BPTR nil;
if(nil=Open("NIL:",MODE_OLDFILE))
	{
	char *fname,*dname,tmp[L_tmpnam];
	Arg2 arg;
	ReadPrefs(&arg);
	GetDTAttrs(obj,DTA_Name,&dname,TAG_END);
	if(dname)
		{
		SetDTAttrs(obj,0,0,DTA_ObjName,dname,TAG_END);
		tmpnam(tmp);
		strncpy(tmp,"PIPE:",5);
		if(fname=(char *)AllocVec(strlen(dname)+L_tmpnam+50,0))
			{
			SPrintf(fname,"run pcdtoppm >%s -%lu -ppm \"%s%\"",tmp,arg.res+1,dname);
			if(Execute(fname,nil,nil)!=DOSFALSE)
				{
				BPTR file;
				if(file=Open(tmp,MODE_OLDFILE))
					{
					BitMapHeader *bh;
					char head[3];
					GetDTAttrs(obj,PDTA_BitMapHeader,&bh,TAG_END);
					if(bh)
						{
						if(Read(file,head,3)==3)
							{
							bh->bmh_Width=ReadNum(file);
							bh->bmh_PageWidth=bh->bmh_Width;
							bh->bmh_Height=ReadNum(file);
							bh->bmh_PageHeight=bh->bmh_Height;
							bh->bmh_Compression=1;
							ReadNum(file);
							if(!arg.mode)
								{
								if(RenderBase)
									{
									BitMap *bm;
									bh->bmh_Depth=arg.depth;
									SetDTAttrs(obj,0,0,DTA_NominalHoriz,bh->bmh_Width,DTA_NominalVert,bh->bmh_Height,PDTA_NumColors,1<<arg.depth,TAG_END);
									if(bm=AllocBitMap(bh->bmh_Width,bh->bmh_Height,bh->bmh_Depth,BMF_CLEAR|BMF_STANDARD|BMF_DISPLAYABLE,0))
										{
										SetDTAttrs(obj,0,0,PDTA_ModeID,BestModeID(BIDTAG_NominalWidth,bh->bmh_Width,BIDTAG_NominalHeight,bh->bmh_Height,BIDTAG_Depth,bh->bmh_Depth,TAG_END),PDTA_BitMap,bm,TAG_END);
										error=ReadP6(obj,bh,bm,file,&arg);
										}
									else error=ERROR_NO_FREE_STORE;
									}
								else error=ERROR_NOT_IMPLEMENTED;
								}
							else
								{
								BitMap *bm;
								bh->bmh_Depth=24;
								SetDTAttrs(obj,0,0,DTA_NominalHoriz,bh->bmh_Width,DTA_NominalVert,bh->bmh_Height,PDTA_ModeID,0,PDTA_SourceMode,PMODE_V43,TAG_END);
								GetDTAttrs(obj,PDTA_BitMap,&bm,TAG_END);
								if(bm) error=ReadP6_V43(obj,bh,file);
								else error=ERROR_NO_FREE_STORE;
								}
							}
						else error=DTERROR_NOT_ENOUGH_DATA;
						}
					else error=ERROR_NO_FREE_STORE;
					Close(file);
					}
				else error=IoErr();
				}
			else error=IoErr();
			FreeVec(fname);
			}
		else error=ERROR_NO_FREE_STORE;
		}
	else error=ERROR_NO_FREE_STORE;
	Close(nil);
	}
else error=IoErr();
return error;
}

static ULONG mNew(IClass *cl,Object *obj,opSet *msg)
{
TagItem *ti;
if(ti=FindTagItem(DTA_SourceType,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 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;
}

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

