#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/intuition_lib.h>
#include <pragma/jpeg_lib.h>
#include <pragma/utility_lib.h>
#include <datatypes/pictureclass.h>
#include <exec/memory.h>
#include <intuition/icclass.h>
#include <string.h>
#include "class.h"

struct Arg1
{
long *dct,*scale,*quality,*smooth,gray,prog,fup,async;
};

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

void ReadPrefs(Data *data)
{
char *var;
data->dct=0;
data->scale=1;
data->quality=75;
data->smooth=0;
data->gray=0;
data->prog=0;
data->fup=0;
data->async=0;
if(var=(char *)AllocVec(512,MEMF_PUBLIC))
	{
	if(GetVar("ENV:DataTypes/jpeg.prefs",var,512,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("DCT/A/N,SCALE/A/N,QUALITY/A/N,SMOOTH/A/N,GRAY/S,PROGRESSIVE/S,FANCYUP/S,ASYNCIO/S",(long *)&para,rdargs))
				{
				data->dct=*para.dct;
				if(data->dct<0) data->dct=0;
				else if(data->dct>2) data->dct=2;
				data->scale=*para.scale;
				if(data->scale<0) data->scale=0;
				else if(data->scale>3) data->scale=3;
				data->scale=1<<data->scale;
				data->quality=*para.quality;
				if(data->quality<1) data->quality=1;
				else if(data->quality>100) data->quality=100;
				data->smooth=*para.smooth;
				if(data->smooth<0) data->smooth=0;
				else if(data->smooth>100) data->smooth=100;
				data->gray=para.gray;
				data->prog=para.prog;
				data->fup=para.fup;
				data->async=para.async;
				FreeArgs(args);
				}
			FreeDosObject(DOS_RDARGS,rdargs);
			}
		}
	FreeVec(var);
	}
}

struct JPEGINFO
{
Object *obj;
UBYTE *buffer;
UWORD d,gray;
};

static void _getline(UBYTE *scanline,ULONG y,ULONG numb,JPEGINFO *info)
{
if(info->gray) DoMethod(info->obj,PDTM_WRITEPIXELARRAY,scanline,PBPAFMT_GREY8,numb,0,y,numb,1);
else
	{
	ULONG w=numb/3;
	if(info->d>8) DoMethod(info->obj,PDTM_WRITEPIXELARRAY,scanline,PBPAFMT_RGB,numb,0,y,w,1);
	else
		{
		register ULONG i;
		register UBYTE *p1,*p2;
		p1=info->buffer+w*y*4;
		p2=scanline;
		for(i=0;i<w;i++,p1+=4,p2+=3)
			{
			p1[1]=p2[0];
			p1[2]=p2[1];
			p1[3]=p2[2];
			}
		}
	}
}

static ULONG getline(register __a0 UBYTE *scanline,register __d0 ULONG line,register __d1 ULONG numb,register __a1 void *userdata)
{
_getline(scanline,line-1,numb,(JPEGINFO *)userdata);
return 0;
}

static void FillBMHD(Object *obj,BitMapHeader *bh,ULONG w,ULONG h,ULONG depth)
{
bh->bmh_Left=0;
bh->bmh_Top=0;
bh->bmh_Width=w;
bh->bmh_Height=h;
bh->bmh_PageWidth=w;
bh->bmh_PageHeight=h;
bh->bmh_Depth=depth;
bh->bmh_Compression=cmpByteRun1;
bh->bmh_XAspect=0;
bh->bmh_YAspect=0;
}

static ULONG GetBody_Gray(JPEGDecHandle *jph,BitMapHeader *bh,Object *obj,Data *data)
{
ULONG error=0;
UBYTE *buffer;
if(buffer=(UBYTE *)AllocBufferFromJPEG(jph,JPG_ScaleDenom,data->scale,TAG_END))
	{
	if(!DecompressJPEG(jph,JPG_DestRGBBuffer,buffer,JPG_ScaleDenom,data->scale,JPG_DCTMethod,data->dct,JPG_FancyUpsampling,data->fup,TAG_END))
		{
		DoMethod(obj,PDTM_WRITEPIXELARRAY,buffer,PBPAFMT_GREY8,bh->bmh_Width,0,0,bh->bmh_Width,bh->bmh_Height);
		}
	else error=ERROR_NO_FREE_STORE;
	FreeJPEGBuffer(buffer);
	}
else
	{
	JPEGINFO info={0,0,8,1};
	if(DecompressJPEG(jph,JPG_DecompressHook,getline,JPG_DecompressUserData,&info,JPG_ScaleDenom,data->scale,JPG_DCTMethod,data->dct,JPG_FancyUpsampling,data->fup,TAG_END)) error=ERROR_NO_FREE_STORE;
	else error=ERROR_NO_FREE_STORE;
	}
return error;
}

static ULONG GetBody_RGB(JPEGDecHandle *jph,BitMapHeader *bh,Object *obj,Data *data)
{
ULONG error=0;
UBYTE *buffer;
if(buffer=(UBYTE *)AllocBufferFromJPEG(jph,JPG_ScaleDenom,data->scale,TAG_END))
	{
	if(!DecompressJPEG(jph,JPG_DestRGBBuffer,buffer,JPG_ScaleDenom,data->scale,JPG_DCTMethod,data->dct,JPG_FancyUpsampling,data->fup,TAG_END))
		DoMethod(obj,PDTM_WRITEPIXELARRAY,buffer,PBPAFMT_RGB,bh->bmh_Width*3,0,0,bh->bmh_Width,bh->bmh_Height);
	else error=ERROR_NO_FREE_STORE;
	FreeJPEGBuffer(buffer);
	}
else
	{
	JPEGINFO info={obj,0,24,0};
	if(DecompressJPEG(jph,JPG_DecompressHook,getline,JPG_DecompressUserData,&info,JPG_ScaleDenom,data->scale,JPG_DCTMethod,data->dct,JPG_FancyUpsampling,data->fup,TAG_END)) error=ERROR_NO_FREE_STORE;
	}
return error;
}

static ULONG GetPicture(IClass *cl,Object *obj)
{
ULONG error=0;
long err;
BPTR file;
BitMapHeader *bh;
GetDTAttrs(obj,DTA_Name,&bh,TAG_END);
if(bh) SetDTAttrs(obj,0,0,DTA_ObjName,bh,TAG_END);
GetDTAttrs(obj,DTA_Handle,&file,PDTA_BitMapHeader,&bh,TAG_END);
if(bh)
	{
	JPEGDecHandle *jph;
	Data *data=(Data *)INST_DATA(cl,obj);
	if(!file)
		{
		GetDTAttrs(obj,DTA_SourceType,&err,TAG_END);
		if(err!=DTST_RAM) return ERROR_REQUIRED_ARG_MISSING;
		else return 0;
		}
	if(!AllocJPEGDecompress(&jph,JPG_SrcFile,file,JPG_UseAsyncIO,data->async,TAG_END))
		{
		UBYTE colorspace;
		ULONG w,h,bpp;
		ReadPrefs(data);
		if(!GetJPEGInfo(jph,JPG_Width,&w,JPG_Height,&h,JPG_ColourSpace,&colorspace,JPG_BytesPerPixel,&bpp,JPG_ScaleDenom,data->scale,TAG_END))
			{
			bh->bmh_Left=0;
			bh->bmh_Top=0;
			bh->bmh_Width=w;
			bh->bmh_Height=h;
			bh->bmh_PageWidth=w;
			bh->bmh_PageHeight=h;
			bh->bmh_Depth=(colorspace==JPCS_RGB)?24:8;
			bh->bmh_Compression=cmpByteRun1;
			bh->bmh_XAspect=0;
			bh->bmh_YAspect=0;
			SetDTAttrs(obj,0,0,DTA_ErrorNumber,&error,DTA_NominalHoriz,w,DTA_NominalVert,h,PDTA_SourceMode,PMODE_V43,PDTA_ModeID,0,TAG_END);
			if(!error)
				{
				switch(colorspace)
					{
					case JPCS_GRAYSCALE:
						error=GetBody_Gray(jph,bh,obj,data);
						break;
					case JPCS_RGB:
						error=GetBody_RGB(jph,bh,obj,data);
						break;
					default:
						error=ERROR_NOT_IMPLEMENTED;
					}
				}
			}
		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 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)):SaveJPEG(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_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;
}
