/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __SMART_PTR_H
#define __SMART_PTR_H

#include <cutils/threads.h>
#include <cutils/atomic.h>

template <class T, bool threadSafe = false>
class SmartPtr
{
public:
    explicit SmartPtr(T* ptr = (T*)NULL) {
        if (threadSafe) {
            m_lock = new mutex_t;
            mutex_init(m_lock);
        }
        else m_lock = NULL;

        m_ptr = ptr;
        if (ptr)
           m_pRefCount = new int32_t(1);
        else
           m_pRefCount = NULL;
    }

    SmartPtr<T,threadSafe>(const SmartPtr<T,false>& rhs) {
        if (threadSafe) {
            m_lock = new mutex_t;
            mutex_init(m_lock);
        }
        else m_lock = NULL;

        m_pRefCount = rhs.m_pRefCount;
        m_ptr       = rhs.m_ptr;
        use();
    }

    SmartPtr<T,threadSafe>(SmartPtr<T,true>& rhs) {
        if (threadSafe) {
            m_lock = new mutex_t;
            mutex_init(m_lock);
        }
        else m_lock = NULL;

        if (rhs.m_lock) mutex_lock(rhs.m_lock);
        m_pRefCount = rhs.m_pRefCount;
        m_ptr       = rhs.m_ptr;
        use();
        if (rhs.m_lock) mutex_unlock(rhs.m_lock);
    }

    ~SmartPtr() {
        if (m_lock) mutex_lock(m_lock);
        release();
        if (m_lock)
        {
            mutex_unlock(m_lock);
            mutex_destroy(m_lock);
            delete m_lock;
        }
    }

    T* Ptr() const {
        return m_ptr;
    }

    const T* constPtr() const
    {
        return m_ptr;
    }

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

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

    operator void*() const {
        return (void *)m_ptr;
    }

    // This gives STL lists something to compare.
    bool operator <(const SmartPtr<T>& t1) const {
        return m_ptr < t1.m_ptr;
    }

    SmartPtr<T,threadSafe>& operator=(const SmartPtr<T,false>& rhs)
    {
        if (m_ptr == rhs.m_ptr)
            return *this;

        if (m_lock) mutex_lock(m_lock);
        release();
        m_pRefCount = rhs.m_pRefCount;
        m_ptr       = rhs.m_ptr;
        use();
        if (m_lock) mutex_unlock(m_lock);

        return *this;
    }

    SmartPtr<T,threadSafe>& operator=(SmartPtr<T,true>& rhs)
    {
        if (m_ptr == rhs.m_ptr)
            return *this;

        if (m_lock) mutex_lock(m_lock);
        release();
        if (rhs.m_lock) mutex_lock(rhs.m_lock);
        m_pRefCount = rhs.m_pRefCount;
        m_ptr       = rhs.m_ptr;
        use();
        if (rhs.m_lock) mutex_unlock(rhs.m_lock);
        if (m_lock) mutex_unlock(m_lock);

        return *this;
    }

private:
    int32_t  *m_pRefCount;
    mutex_t  *m_lock;
    T* m_ptr;

    // Increment the reference count on this pointer by 1.
    int use() {
        if (!m_pRefCount) return 0;
        return android_atomic_inc(m_pRefCount) + 1;
    }

    // Decrement the reference count on the pointer by 1.
    // If the reference count goes to (or below) 0, the pointer is deleted.
    int release() {
        if (!m_pRefCount) return 0;

        int iVal = android_atomic_dec(m_pRefCount);
        if (iVal > 1)
            return iVal - 1;

        delete m_pRefCount;
        m_pRefCount = NULL;

        if (m_ptr) {
            delete m_ptr;
            m_ptr = NULL;
        }
        return 0;
    }

};

#endif // of  __SMART_PTR_H