/* Simple smart pointer class. */

#ifndef SMARTPTR_H
#define SMARTPTR_H

#include "sidcxx11.h"

namespace libsidplayfp
{

using ulint_smartpt = unsigned long int;

template<class T>
class SmartPtrBase_sidtt
{
public:
    SmartPtrBase_sidtt(T* buffer, ulint_smartpt bufferLen, bool bufOwner = false) :
        bufBegin(nullptr),
        bufEnd(nullptr),
        pBufCurrent(nullptr),
        bufLen(0),
        status(false),
        doFree(bufOwner),
        dummy(0)
    {
        if ((buffer != nullptr) && (bufferLen != 0))
        {
            bufBegin = buffer;
            pBufCurrent = buffer;
            bufEnd = bufBegin + bufferLen;
            bufLen = bufferLen;
            status = true;
        }
    }

    virtual ~SmartPtrBase_sidtt()
    {
        if (doFree && (bufBegin != nullptr))
        {
            delete[] bufBegin;
        }
    }

    /* --- public member functions --- */

    virtual T* tellBegin() const { return bufBegin; }
    virtual ulint_smartpt tellLength() const { return bufLen; }
    virtual ulint_smartpt tellPos() const { return static_cast<ulint_smartpt>(pBufCurrent - bufBegin); }

    virtual bool checkIndex(ulint_smartpt index) const
    {
        return (pBufCurrent + index) < bufEnd;
    }

    virtual bool reset()
    {
        if (bufLen)
        {
            pBufCurrent = bufBegin;
            status = true;
        }
        else
        {
            status = false;
        }
        return status;
    }

    virtual bool good() const
    {
        return pBufCurrent < bufEnd;
    }

    virtual bool fail() const
    {
        return pBufCurrent == bufEnd;
    }

    virtual void operator ++()
    {
        if (good())
        {
            pBufCurrent++;
        }
        else
        {
            status = false;
        }
    }

    virtual void operator ++(int)
    {
        if (good())
        {
            pBufCurrent++;
        }
        else
        {
            status = false;
        }
    }

    virtual void operator --()
    {
        if (!fail())
        {
            pBufCurrent--;
        }
        else
        {
            status = false;
        }
    }

    virtual void operator --(int)
    {
        if (!fail())
        {
            pBufCurrent--;
        }
        else
        {
            status = false;
        }
    }

    virtual void operator +=(ulint_smartpt offset)
    {
        if (checkIndex(offset))
        {
            pBufCurrent += offset;
        }
        else
        {
            status = false;
        }
    }

    virtual void operator -=(ulint_smartpt offset)
    {
        if ((pBufCurrent-offset) >= bufBegin)
        {
            pBufCurrent -= offset;
        }
        else
        {
            status = false;
        }
    }

    virtual T operator*()
    {
        if (good())
        {
            return *pBufCurrent;
        }
        else
        {
            status = false;
            return dummy;
        }
    }

    virtual T& operator [](ulint_smartpt index)
    {
        if (checkIndex(index))
        {
            return pBufCurrent[index];
        }
        else
        {
            status = false;
            return dummy;
        }
    }

    virtual operator bool() { return status; }

protected:
    T* bufBegin;
    T* bufEnd;
    T* pBufCurrent;
    ulint_smartpt bufLen;
    bool status;

    const bool doFree;
    const T dummy;
};


template<class T>
class SmartPtr_sidtt final : public SmartPtrBase_sidtt<T>
{
public:
    SmartPtr_sidtt(T* buffer, ulint_smartpt bufferLen, bool bufOwner = false) :
        SmartPtrBase_sidtt<T>(buffer, bufferLen, bufOwner)
    {}

    SmartPtr_sidtt() :
        SmartPtrBase_sidtt<T>(0, 0)
    {}

    void setBuffer(T* buffer, ulint_smartpt bufferLen)
    {
        if ((buffer != nullptr) && (bufferLen != 0))
        {
            this->bufBegin = buffer;
            this->pBufCurrent = buffer;
            this->bufEnd = buffer + bufferLen;
            this->bufLen = bufferLen;
            this->status = true;
        }
        else
        {
            this->bufBegin = nullptr;
            this->pBufCurrent = nullptr;
            this->bufEnd = nullptr;
            this->bufLen = 0;
            this->status = false;
        }
    }
};

}

#endif  /* SMARTPTR_H */
