//Copyright 2020 Siemens Digital Industries Software
//==================================================
//Copyright $2007.
//Siemens Product Lifecycle Management Software Inc.
//All Rights Reserved.
//==================================================
//Copyright 2020 Siemens Digital Industries Software

/*====================================================================================================
File description:

    Filename: BusinessObjectRef.hxx
    Module  : metaframework

    POMRef and EIMRef are DEPRICATED forms of BusinessObjectRef and should not be used in new code.

    Usage:

            tag_t objTag = fromSomewhere();
            ...
            BusinessObjectRef<persistBusObjClass> persistRef = busTag; // still just a tag_t...
            busRef->getXYZ(...);



==================================================================================================
   Date      Name                    Description of Change

$HISTORY$
==================================================================================================*/


#ifndef TCBUSINESSOBJECTREF_HXX

#define TCBUSINESSOBJECTREF_HXX

#include <unidefs.h>
#include <base_utils/ErrorStoreProtector.hxx>
#include <base_utils/IFail.hxx>
#include <base_utils/PomResultStatus.hxx>
#include <base_utils/ScopedSmPtr.hxx>
#include <cxpom/cxpom_errors.h>
#include <metaframework/BusinessObjectRegistry.hxx>
#include <typeinfo>
#include <pom/pom/pom.h>

template <typename T> class BusinessObjectRef
{
public:

    // default constructor
    BusinessObjectRef(): m_tag(0), m_pointee( 0 ) {}
    // constructor
    BusinessObjectRef( tag_t tag )
        : m_tag( tag ), m_pointee( 0 )
    {}

    BusinessObjectRef( T* object )
        : m_tag( 0 ), m_pointee( 0 )
    {
        if ( object != 0 )
        {
            m_pointee = object;
            m_tag = object->getTag();
        }
    }

    template<typename S> BusinessObjectRef(const BusinessObjectRef<S> &other)
    {
        m_tag = other.tag();
        m_pointee = other.m_pointee ? dynamic_cast<T*>(other.m_pointee) : 0;
    }

    BusinessObjectRef& operator = ( const BusinessObjectRef& other )
    {

        if ( this != &other )
        {
            m_pointee = other.m_pointee;
            m_tag = other.m_tag;
        }
        return *this;
    }

    BusinessObjectRef& operator = ( tag_t tag )
    {
        m_pointee = 0;
        m_tag = tag;
        return *this;
    }

    T* operator->() { return getPointee(); }

    tag_t tag() const  { return m_tag; }
    operator tag_t() const { return m_tag; }

    operator T*() { return getPointee(); }

    const T& operator*() const { return *getPointee(); }

    template <typename U> bool isInstanceOf() const
    {
        U* pt = 0;
        ErrorStoreProtector  esp;
        try
        {
            pt = dynamic_cast<U*>(getPointee());
        }
        catch ( const IFail& )
        {
        }
        return pt ? true : false;
    }

    ~BusinessObjectRef()
    {
        m_pointee = 0;
    }

private:

    T* getPointee() const
    {
        if ( m_tag && !m_pointee )
        {
            m_pointee = dynamic_cast<T*>(Teamcenter::BusinessObjectRegistry::instance().getObject(m_tag));
        }
        if ( !m_pointee )
        {
            std::string typeName;
            std::string expectedTypeName = typeid(T).name();
            
            try
            {
                tag_t objectType = NULLTAG;
                logical verdict = false;
                size_t pos = expectedTypeName.find("::");

                if (pos != std::string::npos)
                {
                    expectedTypeName = expectedTypeName.substr(pos + 2);
                }
                
                if (POM_is_tag_valid(m_tag, &verdict) == POM_ok && verdict )
                {
                    ErrorStoreProtector esp;
                    PomResultStatus pstatus = POM_class_of_instance(m_tag, &objectType);
                    Teamcenter::scoped_smptr<char> tempTypeName;
                    pstatus = POM_name_of_class(objectType, &tempTypeName);
                    typeName.assign(tempTypeName.getString());
                }
                else
                {
                    typeName.assign("Runtime Class");
                }
            }
            catch ( const IFail& )
            {
                typeName.assign("unknown");
            }

            throw IFail( CXPOM_wrong_class, typeName.c_str(), expectedTypeName.c_str() );
        }
        return m_pointee;
    }

    template<typename S> friend class BusinessObjectRef;
    // the object tag
    tag_t m_tag;

    // Pointer is mutable as it is essentially a cache
    // of the Tag.object call
    mutable T* m_pointee;
};


#endif
