#include "operatingsystem.h"
#include "c4d_basebitmap.h"
#include "c4d_memory.h"
#include "c4d_file.h"
#include "c4d_general.h"
#include "c4d_basecontainer.h"
#include "c4d_filterplugin.h"
#include "lib_iconcollection.h"
#include "c4d_gui.h"

//#define BmCall(fnc) (this->*C4DOS.Bm->fnc)
///#define BmCall(fnc, ...) RE_C4DOS.Bm->fnc(this,##__VA_ARGS__) 
#define BmCall(fnc, ...) C4DOS.Bm->fnc(this,##__VA_ARGS__) 

BaseBitmapLink *BaseBitmapLink::Alloc(void)
{
	return C4DOS.Bm->BBL_Alloc();
}

void BaseBitmapLink::Free(BaseBitmapLink *&bll)
{
	C4DOS.Bm->BBL_Free(bll);
	bll=NULL;
}

BaseBitmap *BaseBitmapLink::Get() const
{
	return C4DOS.Bm->BBL_Get((BaseBitmapLink*)this);
}

void BaseBitmapLink::Set(BaseBitmap *bmp)
{
	C4DOS.Bm->BBL_Set(this,bmp);
}

BaseBitmap *BaseBitmap::Alloc(void)
{
	return C4DOS.Bm->Alloc();
}

void BaseBitmap::Free(BaseBitmap *&bm)
{
	C4DOS.Bm->Free(bm);
	bm=NULL;
}

void MultipassBitmap::Free(MultipassBitmap *&bm)
{
	C4DOS.Bm->Free(bm);
	bm=NULL;
}

COLORMODE BaseBitmap::GetColorMode(void) const
{
	return (COLORMODE)C4DOS.Bm->MPB_GetParameter((const MultipassBitmap*)this,MPBTYPE_COLORMODE).GetLong();
}

BaseBitmap *BaseBitmap::GetClone(void) const
{
	return C4DOS.Bm->GetClone(this);
}

Bool BaseBitmap::CopyTo(BaseBitmap *dst) const
{
	return C4DOS.Bm->CopyTo(this,dst);
}

Bool BaseBitmap::CopyPartTo(BaseBitmap *dst, LONG x, LONG y, LONG w, LONG h) const
{
	return C4DOS.Bm->CopyPartTo(this,dst,x,y,w,h);
}

BaseBitmap *BaseBitmap::GetClonePart(LONG x, LONG y, LONG w, LONG h) const
{
	return C4DOS.Bm->GetClonePart(this,x,y,w,h);
}

void BaseBitmap::FlushAll(void)
{
	C4DOS.Bm->FlushAll(this);
}
		
IMAGERESULT BaseBitmap::Init(LONG x, LONG y, LONG depth, INITBITMAPFLAGS flags)
{
	return C4DOS.Bm->Init1(this,x,y,depth,flags);
}

IMAGERESULT BaseBitmap::Init(const Filename &name, LONG frame, Bool *ismovie)
{
	return C4DOS.Bm->Init2(this,&name,frame,ismovie);
}

IMAGERESULT BaseBitmap::Init(BaseBitmap *&res, const Filename &name, LONG frame, Bool *ismovie, BitmapLoaderPlugin **loaderplugin)
{
	return C4DOS.Bm->Init3(res,name,frame,ismovie,loaderplugin);
}

IMAGERESULT BaseBitmap::Save(const Filename &name, LONG format, BaseContainer *data, SAVEBIT savebits) const
{
	return BmCall(Save,name,format,data,savebits);
}

Bool MultipassBitmap::SetTempColorProfile(const ColorProfile *profile, Bool dithering)
{
	return BmCall(SetTempColorProfile,profile,dithering);
}

LONG MultipassBitmap::GetUserID(void) const
{
	return BmCall(GetUserID);
}

void BaseBitmap::SetCMAP(LONG i, LONG r, LONG g, LONG b)
{
	C4DOS.Bm->SetCMAP(this,i,r,g,b);
}

void BaseBitmap::ScaleBicubic(BaseBitmap *dest, LONG src_xmin, LONG src_ymin, LONG src_xmax, LONG src_ymax, LONG dst_xmin, LONG dst_ymin, LONG dst_xmax, LONG dst_ymax) const
{
	C4DOS.Bm->ScaleBicubic(this,dest,src_xmin,src_ymin,src_xmax,src_ymax,dst_xmin,dst_ymin,dst_xmax,dst_ymax);
}

void BaseBitmap::ScaleIt(BaseBitmap *dst, LONG intens, Bool sample, Bool nprop) const
{
	C4DOS.Bm->ScaleIt(this,dst,intens,sample,nprop);
}

void BaseBitmap::Clear(LONG r, LONG g, LONG b)
{
	C4DOS.Bm->Clear(this,0,0,GetBw()-1,GetBh()-1,r,g,b);
}

void BaseBitmap::Clear(LONG x1, LONG y1, LONG x2, LONG y2, LONG r, LONG g, LONG b)
{
	C4DOS.Bm->Clear(this,x1,y1,x2,y2,r,g,b);
}

BaseBitmap *BaseBitmap::AddChannel(Bool internal, Bool straight)
{
	return C4DOS.Bm->AddChannel(this,internal,straight);
}

void BaseBitmap::RemoveChannel(BaseBitmap *channel)
{
	C4DOS.Bm->RemoveChannel(this,channel);
}

const BaseBitmap *BaseBitmap::GetInternalChannel(void) const
{
	return C4DOS.Bm->GetInternalChannel(((BaseBitmap*)this));
}

BaseBitmap *BaseBitmap::GetInternalChannel(void)
{
	return C4DOS.Bm->GetInternalChannel(this);
}

LONG BaseBitmap::GetChannelCount(void) const
{
	return C4DOS.Bm->GetChannelCount(this);
}

BaseBitmap *BaseBitmap::GetChannelNum(LONG num)
{
	return C4DOS.Bm->GetChannelNum(this,num);
}

const BaseBitmap *BaseBitmap::GetChannelNum(LONG num) const
{
	return C4DOS.Bm->GetChannelNum(((BaseBitmap*)this),num);
}

Bool BaseBitmap::SetData(LONG id,const GeData &data)
{
	return C4DOS.Bm->SetBaseBitmapData(this,id,data);
}

GeData BaseBitmap::GetData(LONG id, const GeData &t_default) const
{
	return C4DOS.Bm->GetBaseBitmapData(this,id,t_default);
}

Bool BaseBitmap::IsMultipassBitmap(void) const
{
	return C4DOS.Bm->IsMultipassBitmap(this);
}

MovieLoader *MovieLoader::Alloc( void )
{
	return gNew MovieLoader();
}

void MovieLoader::Free( MovieLoader *&ml )
{
	gDelete( ml );
}

MovieLoader::MovieLoader( void )
{
	InitData();
}

MovieLoader::~MovieLoader( void )
{
	FreeData();
}

void	MovieLoader::InitData( void )
{
	plugin = NULL;
	bm = NULL;
	is_movie = FALSE;
	frame_cnt = 0;
	fps = 0.0;
	frame_idx = -2;
	result = IMAGERESULT_OK;
}

void	MovieLoader::FreeData( void )
{
	if ( plugin )
	{
		plugin->BmLoadAnimated( &plugin_data, BITMAPLOADERACTION_FREE, NULL, 0 );
		plugin = NULL;
	}

	if ( bm )
		BaseBitmap::Free( bm );
}
	
IMAGERESULT  MovieLoader::Open( const Filename &fn )
{
#ifdef _DEBUG
	if ( plugin )
		GeBoom();																								// call Close() before reusing a MovieLoader
	if ( bm )
		GeBoom();
	FreeData();																								// release a previously used bitmap/plugin
	InitData();																								// back to start
#endif

	result = BaseBitmap::Init( bm, fn, frame_idx, &is_movie, &plugin );
	if (( result != IMAGERESULT_OK ) || ( plugin == NULL ))
	{
		if (( plugin == NULL ) && ( result == IMAGERESULT_OK ))				// this shouldn't happen
			result = IMAGERESULT_WRONGTYPE;

		plugin = NULL;
		BaseBitmap::Free( bm );
		return result;
	}

	if ( is_movie )
	{
		if ( GeGetMovieInfo( fn,&frame_cnt, &fps ) == FALSE )
		{
			frame_cnt = 0;
			fps = 0.0;
			GeBoom();																							// don't return an error here - examine the problem
		}

		plugin_data.moviename = fn;
		result = plugin->BmLoadAnimated( &plugin_data, BITMAPLOADERACTION_INIT, NULL, 0 );
		if ( result != IMAGERESULT_OK )
		{
			FreeData();
			return result;
		}
		frame_idx = -2;																					// nothing loaded yet
	}

	return result;
}

void	MovieLoader::Close( void )
{
	FreeData();
	InitData();																								// back to start
}

//----------------------------------------------------------------------------------------
// Read a frame and return the bitmap pointer
// Function result:		BaseBitmap (MovieLoader is the owner) or NULL (check _result)
// new_frame_idx:			frame index
// _result:						used to return the result code (IMAGERESULT_OK or error code), can be NULL
//----------------------------------------------------------------------------------------
BaseBitmap	*MovieLoader::Read( LONG new_frame_idx, LONG *_result )
{
	if ( is_movie == FALSE )
		return bm;																							// always return the image initally loaded at the init call

	if ( new_frame_idx == frame_idx )													// frame already loaded?
		return bm;

	result = plugin->BmLoadAnimated( &plugin_data, BITMAPLOADERACTION_LOAD, bm, new_frame_idx );	// read the requested frame
	if ( result == IMAGERESULT_OK )
	{
		frame_idx = new_frame_idx;
		if ( _result )
			*_result = IMAGERESULT_OK;
		return bm;
	}
	
	frame_idx = -2;																						// loading a new frame failed, content of bm is unknown
	if ( _result )
		*_result = result;
	return NULL;
}

//----------------------------------------------------------------------------------------
// Get movie/bitmap info
// Function result:		number of frames (0: Open failed)
// _fps:							used to return frame rate
//----------------------------------------------------------------------------------------
LONG MovieLoader::GetInfo( Real *_fps )
{
	if ( _fps )
		*_fps = fps;

	if ( is_movie )
		return frame_cnt;
	
	if ( result == IMAGERESULT_OK )
		return 1;

	return 0;																									// Open failed
}

MovieSaver *MovieSaver::Alloc(void)
{
	return C4DOS.Ms->Alloc();
}

void MovieSaver::Free(MovieSaver *&ms)
{
	C4DOS.Ms->Free(ms);
	ms=NULL;
}

IMAGERESULT MovieSaver::Open(const Filename &name, BaseBitmap *bm, LONG fps, LONG id, BaseContainer *data, SAVEBIT savebits, BaseSound *sound)
{
	return C4DOS.Ms->Open(this,&name,bm,fps,id,data,savebits,sound);
}

IMAGERESULT MovieSaver::Write(BaseBitmap *bm)
{
	return C4DOS.Ms->Write(this,bm);
}

void MovieSaver::Close(void)
{
	C4DOS.Ms->Close(this);
}

Bool MovieSaver::Choose(LONG id, BaseContainer *bc)
{
	return C4DOS.Ms->Choose(this,id,bc);
}

#define CallBmLoader(fnc) (((BitmapLoaderData*)(((BITMAPLOADERPLUGIN*)GetPluginStructure())->adr))->*((BITMAPLOADERPLUGIN*)GetPluginStructure())->fnc)
#define CallBmSaver(fnc)  (((BitmapSaverData*)(((BITMAPSAVERPLUGIN*)GetPluginStructure())->adr))->*((BITMAPSAVERPLUGIN*)GetPluginStructure())->fnc)

Bool BitmapLoaderPlugin::BmIdentify(const Filename &name, UCHAR *probe, LONG size)
{
	return CallBmLoader(Identify)(name,probe,size);
}

IMAGERESULT BitmapLoaderPlugin::BmLoad(const Filename &name, BaseBitmap *bm, LONG frame)
{
	return CallBmLoader(Load)(name,bm,frame);
}

LONG BitmapLoaderPlugin::BmGetSaver(void)
{
	if (!((BITMAPLOADERPLUGIN*)GetPluginStructure())->GetSaver) return 0;
	return CallBmLoader(GetSaver)();
}

Bool BitmapLoaderPlugin::BmGetInformation(const Filename &name, LONG *frames, Real *fps)
{
	if (!((BITMAPLOADERPLUGIN*)GetPluginStructure())->GetInformation) return FALSE;
	return CallBmLoader(GetInformation)(name,frames,fps);
}

IMAGERESULT BitmapLoaderPlugin::BmLoadAnimated(BitmapLoaderAnimatedData *bd, BITMAPLOADERACTION action, BaseBitmap *bm, LONG frame)
{
	if (!((BITMAPLOADERPLUGIN*)GetPluginStructure())->LoadAnimated) return IMAGERESULT_NOTEXISTING;
	return CallBmLoader(LoadAnimated)(bd, action, bm, frame);
}

IMAGERESULT BitmapLoaderPlugin::BmExtractSound(BitmapLoaderAnimatedData *bd, BaseSound *snd)
{
	if (!((BITMAPLOADERPLUGIN*)GetPluginStructure())->ExtractSound) return IMAGERESULT_NOTEXISTING;
	return CallBmLoader(ExtractSound)(bd, snd);
}

IMAGERESULT BitmapLoaderPlugin::BmHasSound(BitmapLoaderAnimatedData *bd)
{
	if (!((BITMAPLOADERPLUGIN*)GetPluginStructure())->HasSound) return IMAGERESULT_NOTEXISTING;
	return CallBmLoader(HasSound)(bd);
}

void BitmapSaverPlugin::BmGetDetails(LONG *alpha, String *suffix)
{
	if (alpha)
	{
		BaseContainer bc;
		*alpha = CallBmSaver(GetMaxAlphas)(&bc);
	}
	
	if (suffix)
	{
		*suffix=String();
		String *str=((BITMAPSAVERPLUGIN*)GetPluginStructure())->suffix;
		if (str) *suffix=*str;
	}
}

LONG BitmapSaverPlugin::BmGetMaxResolution(Bool layers)
{
	if (!((BITMAPSAVERPLUGIN*)GetPluginStructure())->GetMaxResolution) return 32000;
	return CallBmSaver(GetMaxResolution)(layers);
}

Bool BitmapSaverPlugin::BmEdit(BaseContainer *data)
{
	return CallBmSaver(Edit)(data);
}

IMAGERESULT BitmapSaverPlugin::BmSave(const Filename &name, BaseBitmap *bm, BaseContainer *data, SAVEBIT savebits)
{
	LONG maxres = BmGetMaxResolution((savebits&SAVEBIT_MULTILAYER)!=SAVEBIT_0);
	if (bm->GetBw()>maxres || bm->GetBh()>maxres) return IMAGERESULT_PARAM_ERROR;

	return CallBmSaver(Save)(name,bm,data,savebits);
}

IMAGERESULT BitmapSaverPlugin::BmOpen(PluginMovieData *&md, const Filename &name, BaseBitmap *bm, BaseContainer *data, SAVEBIT savebits, LONG fps)
{
	if (!((BITMAPSAVERPLUGIN*)GetPluginStructure())->Open) return IMAGERESULT_OUTOFMEMORY;
	return CallBmSaver(Open)(md,name,bm,data,savebits,fps);
}

IMAGERESULT BitmapSaverPlugin::BmWrite(PluginMovieData *md, BaseBitmap *bm)
{
	if (!((BITMAPSAVERPLUGIN*)GetPluginStructure())->Write) return IMAGERESULT_OUTOFMEMORY;
	return CallBmSaver(Write)(md,bm);
}

void BitmapSaverPlugin::BmClose(PluginMovieData *&md)
{
	if (!((BITMAPSAVERPLUGIN*)GetPluginStructure())->Close) return;
	CallBmSaver(Close)(md);
}

BaseBitmap *InitResourceBitmap(LONG resource_id)
{
	IconData dat;
	if (!GetIcon(resource_id,&dat) || !dat.bmp) return NULL;

	return dat.bmp->GetClonePart(dat.x,dat.y,dat.w,dat.h);
}

Bool MultipassBitmap::GetLayers(c4d_misc::BaseArray<BaseBitmap*> &list, MPB_GETLAYERS flags)
{
	list.Flush();
	if (!IsMultipassBitmap())
	{
		// must never happen. if this is the case the pointer is not a multipassbmp the call must be catched outside!
		GeBoom();
		return FALSE;
	}
	LONG count;
	BaseBitmap **arr=NULL;

	if (!C4DOS.Bm->MPB_GetLayers(this,flags,arr,count)) return FALSE;

	LONG i;
	for (i=0;i<count;i++)
	{
		if (!arr[i]) continue;
		list.Append(arr[i]);
	}
	GeFree(arr);
	return TRUE;
}

Bool MultipassBitmap::GetLayers(c4d_misc::BaseArray<MultipassBitmap*> &list, MPB_GETLAYERS flags)
{
	list.Flush();
	if (!IsMultipassBitmap())
	{
		// must never happen. if this is the case the pointer is not a multipassbmp the call must be catched outside!
		GeBoom();
		return FALSE;
	}
	LONG count;
	BaseBitmap **arr=NULL;

	if (!C4DOS.Bm->MPB_GetLayers(this,flags,arr,count)) return FALSE;
	if (!arr) return FALSE;

	LONG i;
	for (i=0;i<count;i++)
	{
		if (!arr[i] || !arr[i]->IsMultipassBitmap()) continue;
		list.Append((MultipassBitmap*)arr[i]);
	}
	GeFree(arr);
	return TRUE;
}



// ColorProfile Stuff starts here

ColorProfile *ColorProfile::Alloc(void)
{
	return C4DOS.Bm->ProfileAlloc();
}

void ColorProfile::Free(ColorProfile *&profile)
{
	C4DOS.Bm->ProfileFree(profile);
	profile=NULL;
}

ColorProfile& ColorProfile::operator = (const ColorProfile &src)
{
	C4DOS.Bm->ProfileCopy(&src,this);
	return *this;
}

Bool ColorProfile::operator == (const ColorProfile &src) const
{
	return C4DOS.Bm->ProfileEqual(&src,this);
}

Bool ColorProfile::operator != (const ColorProfile &src) const
{
	return !C4DOS.Bm->ProfileEqual(&src,this);
}

const ColorProfile *ColorProfile::GetDefaultSRGB()
{
	return C4DOS.Bm->ProfileSRGB();
}

const ColorProfile *ColorProfile::GetDefaultLinearRGB()
{
	return C4DOS.Bm->ProfileLinearRGB();
}

const ColorProfile *ColorProfile::GetDefaultSGray()
{
	return C4DOS.Bm->ProfileSGray();
}

const ColorProfile *ColorProfile::GetDefaultLinearGray()
{
	return C4DOS.Bm->ProfileLinearGray();
}

Bool ColorProfile::CreateDefaultWindow(GeDialog *dlg)
{
	return C4DOS.Bm->ProfileWindow(this,dlg ? dlg->Get() : NULL);
}

Bool ColorProfile::OpenProfileFromFile(const Filename &fn)
{
	return C4DOS.Bm->ProfileFromFile(this,fn);
}

Bool ColorProfile::OpenProfileFromMemory(const void *mem, LLONG memsize)
{
	return C4DOS.Bm->ProfileFromMemory(this,mem,memsize);
}

Bool ColorProfile::WriteProfileToMemory(void *&mem, LLONG &memsize) const
{
	return C4DOS.Bm->ProfileToMemory(this,mem,memsize);
}

Bool ColorProfile::WriteProfileToFile(const Filename &fn) const
{
	return C4DOS.Bm->ProfileToFile(this,fn);
}

String ColorProfile::GetInfo(COLORPROFILEINFO info) const
{
	return C4DOS.Bm->ProfileInfo(this,info);
}

Bool ColorProfile::HasProfile() const
{
	return C4DOS.Bm->ProfileHasProfile(this);
}

Bool ColorProfile::CheckColorMode(COLORMODE colormode) const
{
	return C4DOS.Bm->ProfileCheckColorMode(this,colormode);
}

Bool ColorProfile::IsMonitorProfileMode() const
{
	return C4DOS.Bm->ProfileIsMonitorMode(this);
}

Bool ColorProfile::SetMonitorProfileMode(Bool on)
{
	return C4DOS.Bm->ProfileSetMonitorMode(this,on);
}


ColorProfileConvert *ColorProfileConvert::Alloc(void)
{
	return C4DOS.Bm->ProfileConvAlloc();
}

void ColorProfileConvert::Free(ColorProfileConvert *&convert)
{
	C4DOS.Bm->ProfileConvFree(convert);
	convert = NULL;
}

Bool ColorProfileConvert::PrepareTransform(COLORMODE srccolormode, const ColorProfile *srcprofile, COLORMODE dstcolormode, const ColorProfile *dstprofile, Bool bgr)
{
	return C4DOS.Bm->ProfileConvTransform(this,srccolormode,srcprofile,dstcolormode,dstprofile,bgr);
}

void ColorProfileConvert::Convert(const PIX *src, PIX *dst, LONG cnt, LONG SkipInputComponents, LONG SkipOutputComponents) const
{
	return C4DOS.Bm->ProfileConvConvert(this,src,dst,cnt,SkipInputComponents,SkipOutputComponents);
}
