//------------------------------------------------------------------------------
// emRes.h
//
// Copyright (C) 2006-2008,2010,2012,2014 Oliver Hamann.
//
// Homepage: http://eaglemode.sourceforge.net/
//
// This program is free software: you can redistribute it and/or modify it under
// the terms of the GNU General Public License version 3 as published by the
// Free Software Foundation.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
// more details.
//
// You should have received a copy of the GNU General Public License version 3
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//------------------------------------------------------------------------------

#ifndef emRes_h
#define emRes_h

#ifndef emImage_h
#include <emCore/emImage.h>
#endif

#ifndef emModel_h
#include <emCore/emModel.h>
#endif


//==============================================================================
//========================= Resource image acquisition =========================
//==============================================================================

emImage emGetResImage(emRootContext & rootContext, const emString & filePath,
                      int channelCount=-1);
emImage emTryGetResImage(emRootContext & rootContext, const emString & filePath,
                         int channelCount=-1) throw(emException);
	// Get a resource image. An image file is loaded and cached in the root
	// context for quick shared re-use. This means, if the image is already
	// cached, it is not loaded again and a shallow copy of that image is
	// returned. The image is removed from the cache some time after there
	// are no remaining shallow copies. The first version of the function
	// calls emFatalError if the loading fails. The second version throws an
	// exception instead. The image file format must be Targa (tga).
	// ??? A future version should even support other file formats by making
	// ??? use of the file model interfaces.
	// Arguments:
	//   rootContext    - The root context.
	//   filePath       - Path to the file. Must be Targa format (tga).
	//   channelCount   - Number of channels the image and image file must
	//                    have. -1 means to accept any channel count.
	// Returns: The image.

emImage emGetInsResImage(emRootContext & rootContext, const char * prj,
                         const char * subPath, int channelCount=-1);
emImage emTryGetInsResImage(emRootContext & rootContext, const char * prj,
                            const char * subPath,
                            int channelCount=-1) throw(emException);
	// Get an installed resource image. This is like
	// em[Try]GetResImage(
	//   rootContext,
	//   emGetInstallPath(EM_IDT_RES,prj,subPath),
	//   channelCount
	// );


//==============================================================================
//======================== Resource acquisition basics =========================
//==============================================================================

class emResModelBase : public emModel {
protected:
	emResModelBase(emContext & context, const emString & name);
	void Touch();
	virtual unsigned int GetDataRefCount() const = 0;
	virtual bool Cycle();
private:
	class PollTimer : public emModel {
	public:
		static emRef<PollTimer> Acquire(emRootContext & rootContext);
		emTimer Timer;
	protected:
		PollTimer(emContext & context, const emString & name);
		virtual ~PollTimer();
	};
};

template <class CLS> class emResModel : public emResModelBase {

public:

	// This template class can be used when implementing functions like
	// emGetResImage. The class CLS must be like emImage, emString, emArray
	// and emList. That means, it must have a copy operator and a copy
	// constructor which perform shallow copies, and it must have the method
	// GetDataRefCount. The latter is polled from time to time by the
	// internal clean-up mechanism. The context can be non-root here, this
	// may be restricted at higher level. Note that several problems of
	// caching can be solved easily by the emModel concept (even see
	// emVarModel). This resource concept is meant only for situation where
	// holding a model reference would not be practicable.

	static emRef<emResModel<CLS> > Acquire(emContext & context,
	                                       const emString & name);
	static emRef<emResModel<CLS> > Lookup(emContext & context,
	                                      const char * name);
	static emRef<emResModel<CLS> > LookupInherited(emContext & context,
	                                               const char * name);

	const CLS & Get();
	void Set(const CLS & res);
		// Both methods are resetting the auto-delete mechanism.
		// (Therefore the Get method is non-const)

protected:

	emResModel(emContext & context, const emString & name);

	virtual unsigned int GetDataRefCount() const;

private:
	CLS Res;
};


//==============================================================================
//============================== Implementations ===============================
//==============================================================================

inline void emResModelBase::Touch()
{
	if (GetMinCommonLifetime()!=UINT_MAX) SetMinCommonLifetime(UINT_MAX);
}

template <class CLS> emRef<emResModel<CLS> > emResModel<CLS>::Acquire(
	emContext & context, const emString & name
)
{
	EM_IMPL_ACQUIRE_COMMON(emResModel,context,name)
}

template <class CLS>
emRef<emResModel<CLS> > emResModel<CLS>::Lookup(
	emContext & context, const char * name
)
{
	return emRef<emResModel>(
		(emResModel*)context.Lookup(typeid(emResModel),name)
	);
}

template <class CLS>
emRef<emResModel<CLS> > emResModel<CLS>::LookupInherited(
	emContext & context, const char * name
)
{
	return emRef<emResModel>(
		(emResModel*)context.LookupInherited(typeid(emResModel),name)
	);
}

template <class CLS> inline const CLS & emResModel<CLS>::Get()
{
	Touch();
	return Res;
}

template <class CLS> void emResModel<CLS>::Set(const CLS & res)
{
	Touch();
	Res=res;
}

template <class CLS> emResModel<CLS>::emResModel(
	emContext & context, const emString & name
)
	: emResModelBase(context,name)
{
}

template <class CLS> unsigned int emResModel<CLS>::GetDataRefCount() const
{
	return Res.GetDataRefCount();
}


#endif
