////////////////////////////////////////////////////////////////////////////////
// The Loki Library
// Copyright (c) 2006 by Guillaume Chatelet
//
// Code covered by the MIT License
//
// Permission to use, copy, modify, distribute and sell this software for any
// purpose is hereby granted without fee, provided that the above copyright
// notice appear in all copies and that both that copyright notice and this
// permission notice appear in supporting documentation.
//
// The authors make no representations about the suitability of this software
// for any purpose. It is provided "as is" without express or implied warranty.
//
// This code DOES NOT accompany the book:
// Alexandrescu, Andrei. "Modern C++ Design: Generic Programming and Design
//     Patterns Applied". Copyright (c) 2001. Addison-Wesley.
//
////////////////////////////////////////////////////////////////////////////////

// $Id: SPCachedFactory.h 896 2008-08-08 22:20:05Z syntheticpp $

#ifndef SPCACHEDFACTORY_H_
#define SPCACHEDFACTORY_H_

/**
 * This file is intented to be used if you want a CachedFactory with
 * a SmartPointer encapsulation policy.
 * It as been defined in a separate file because of the many introduced
 * dependencies (SmartPtr.h would depend on Functor.h and CachedFactory.h
 * would depend on SmartPtr.h). By defining another header you pay for those
 * extra dependencies only if you need it.
 *
 * This file defines FunctionStorage a new SmartPointer storage policy and
 * SmartPointer a new CachedFactory encapsulation policy.
 */

#include <loki/Functor.h>
#include <loki/SmartPtr.h>
#include <loki/CachedFactory.h>

namespace Loki
{

////////////////////////////////////////////////////////////////////////////////
///  \class FunctionStorage
///
///  \ingroup  SmartPointerStorageGroup
///  \brief Implementation of the StoragePolicy used by SmartPtr.
///
///  This storage policy is used by SmartPointer CachedFactory's encapsulation
///  policy. It's purpose is to call a Functor instead of deleting the
///  underlying pointee object. You have to set the callback functor by calling
///  SetCallBackFunction(const FunctorType &functor).
///
///  Unfortunately, the functor argument is not a reference to the SmartPtr but
///  a void *. Making functor argument a reference to the pointer would require
///  the FunctionStorage template to know the full definition of the SmartPtr.
////////////////////////////////////////////////////////////////////////////////

template <class T>
class FunctionStorage
{
public:
    /// the type of the pointee_ object
    typedef T* StoredType;
    /// type used to declare OwnershipPolicy type.
    typedef T* InitPointerType;
    /// type returned by operator->
    typedef T* PointerType;
    /// type returned by operator*
    typedef T& ReferenceType;
    /// type of the Functor to set
    typedef Functor< void , Seq< void* > > FunctorType;

    FunctionStorage() : pointee_(Default()), functor_()
    {}

    // The storage policy doesn't initialize the stored pointer
    //     which will be initialized by the OwnershipPolicy's Clone fn
    FunctionStorage(const FunctionStorage& rsh) : pointee_(0), functor_(rsh.functor_)
    {}

    template <class U>
    FunctionStorage(const FunctionStorage<U>& rsh) : pointee_(0), functor_(rsh.functor_)
    {}

    FunctionStorage(const StoredType& p) : pointee_(p), functor_() {}

    PointerType operator->() const { return pointee_; }

    ReferenceType operator*() const { return *pointee_; }

    void Swap(FunctionStorage& rhs)
    {
        std::swap(pointee_, rhs.pointee_);
        std::swap(functor_, rhs.functor_);
    }

    /// Sets the callback function to call. You have to specify it or
    /// the smartPtr will throw a bad_function_call exception.
    void SetCallBackFunction(const FunctorType& functor)
    {
        functor_ = functor;
    }

    // Accessors
    template <class F>
    friend typename FunctionStorage<F>::PointerType GetImpl(const FunctionStorage<F>& sp);

    template <class F>
    friend const typename FunctionStorage<F>::StoredType& GetImplRef(const FunctionStorage<F>& sp);

    template <class F>
    friend typename FunctionStorage<F>::StoredType& GetImplRef(FunctionStorage<F>& sp);

protected:
    // Destroys the data stored
    // (Destruction might be taken over by the OwnershipPolicy)
    void Destroy()
    {
        functor_(this);
    }

    // Default value to initialize the pointer
    static StoredType Default()
    { return 0; }

private:
    // Data
    StoredType pointee_;
    FunctorType functor_;
};

template <class T>
inline typename FunctionStorage<T>::PointerType GetImpl(const FunctionStorage<T>& sp)
{ return sp.pointee_; }

template <class T>
inline const typename FunctionStorage<T>::StoredType& GetImplRef(const FunctionStorage<T>& sp)
{ return sp.pointee_; }

template <class T>
inline typename FunctionStorage<T>::StoredType& GetImplRef(FunctionStorage<T>& sp)
{ return sp.pointee_; }

/**
 * \class	SmartPointer
 * \ingroup	EncapsulationPolicyCachedFactoryGroup
 * \brief	Encapsulate the object in a SmartPtr with FunctionStorage policy.
 *
 * The object will come back to the Cache as soon as no more SmartPtr are
 * referencing this object. You can customize the SmartPointer with the standard
 * SmartPtr policies (OwnershipPolicy, ConversionPolicy, CheckingPolicy,
 * ConstnessPolicy) but StoragePolicy is forced to FunctionStorage.
 */
template
<
class AbstractProduct,
      template <class> class OwnershipPolicy = RefCounted,
      class ConversionPolicy = DisallowConversion,
      template <class> class CheckingPolicy = AssertCheck,
      template<class> class ConstnessPolicy = LOKI_DEFAULT_CONSTNESS
      >
class SmartPointer
{
private:
    typedef SmartPtr< AbstractProduct,OwnershipPolicy,
            ConversionPolicy, CheckingPolicy,
            FunctionStorage, ConstnessPolicy > CallBackSP;
protected:
    typedef CallBackSP ProductReturn;
    SmartPointer() : fun(this, &SmartPointer::smartPointerCallbackFunction) {}
    virtual ~SmartPointer() {}

    ProductReturn encapsulate(AbstractProduct* pProduct)
    {
        CallBackSP SP(pProduct);
        SP.SetCallBackFunction(fun);
        return SP;
    }

    AbstractProduct* release(ProductReturn& pProduct)
    {
        return GetImpl(pProduct);
    }

    const char* name() {return "smart pointer";}

private:
    SmartPointer& operator=(const SmartPointer&);
    SmartPointer(const SmartPointer&);
    void smartPointerCallbackFunction(void* pSP)
    {
        CallBackSP& SP(*reinterpret_cast<CallBackSP*>(pSP));
        ReleaseObject(SP);
    }
    virtual void ReleaseObject(ProductReturn& object)=0;
    const typename CallBackSP::FunctorType fun;
};

} // namespace Loki

#endif /*SPCACHEDFACTORY_H_*/