//------------------------------------------------------------------------------
// emRes.h
//
// Copyright (C) 2006-2008,2010,2012,2014,2018 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);
        // 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);
        // 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