/*
 * SPDX-FileCopyrightText: 2001, Critical Path
 * SPDX-License-Identifier: LGPL-2.1-only OR MPL-2.0
 */

/**
 * @file    icptrholder_cxx.hpp
 * @author  wyau (08/29/02)
 * @brief   C++ template classes for managing C++ pointers returned by
 *          VComponent::get_..._component, VComponent::get_..._property,
 *          ICalProperty::get_..._value.
 *
 * @remarks VComponent::get... functions returns a C++ object that wraps the
 * libical implementation. It is important to note that the wrapped
 * implementation still belongs to the original component. To stop memory leak,
 * caller must delete the pointer. However, the destructor will call the
 * appropriate free function. eg. ~VComponent calls icalcomponent_free(imp).
 *
 * As stated previously, imp still belongs to the original component. To avoid
 * freeing the wrapped "imp", caller must set the "imp" to null before deleting
 * the pointer.
 *
 * The template class relieves the burden of memory management when used as a
 * stack based object.  The class holds a pointer to the C++ Wrapper.
 * The destructor set the imp to null before deleting the pointer.
 *
 * Each C++ Wrapper instantiates a template class in its corresponding .h file.
 *
 * Usage example:
 *   VComponentTmpPtr p;// VComponentTmpPtr is an instantiation of this template
 *   for (p=component.get_first_component; p!= 0; p=component.get_next_component) {
 *
 * SPDX-FileCopyrightText: 2001, Critical Path
 * SPDX-License-Identifier: LGPL-2.1-only OR MPL-2.0
 */

#ifndef ICPTRHOLDER_CXX_H
#define ICPTRHOLDER_CXX_H

#include <cassert>

template<class T>
class ICPointerHolder
{
public:
    ICPointerHolder()
        : ptr(0)
    {
    }

    /* cppcheck-suppress noExplicitConstructor */
    ICPointerHolder(T *p) // NOLINT(runtime/explicit)
        : ptr(p)
    {
    }

    // copy constructor to support assignment
    ICPointerHolder(const ICPointerHolder &ip)
        : ptr(ip.ptr)
    {
        // We need to transfer ownership of ptr to this object by setting
        // ip's ptr to null. Otherwise, ptr will de deleted twice.
        // const ugliness requires us to do the const_cast.
        ICPointerHolder *ipp = const_cast<ICPointerHolder *>(&ip);

        ipp->ptr = 0;
    };

    ~ICPointerHolder()
    {
        release();
    }

    ICPointerHolder &operator=(T *p)
    {
        this->release();
        ptr = p;
        return *this;
    }

    ICPointerHolder &operator=(ICPointerHolder &p) //NOLINT(misc-unconventional-assign-operator)
    {
        if (this == &p) {
            return *this;
        }
        this->release();
        ptr = p.ptr; // this transfer ownership of the pointer
        p.ptr = 0;   // set it to null so the pointer won't get delete twice.
        return *this;
    }

    /* cppcheck-suppress constParameterPointer */
    bool operator!=(T *p)
    {
        return (ptr != p);
    }

    /* cppcheck-suppress constParameterPointer */
    bool operator==(T *p)
    {
        return (ptr == p);
    }

    operator T *() const
    {
        return ptr;
    }

    T *operator->() const
    {
        icalassert(ptr);
        return ptr;
    }

    T &operator*()
    {
        icalassert(ptr);
        return *ptr;
    }

private:
    void release()
    {
        if (ptr != 0) {
            ptr->detach();
            delete ptr;

            ptr = 0;
        }
    }

    T *ptr;
};

#endif
