/*
    SuperCollider real time audio synthesis system
    Copyright (c) 2002 James McCartney. All rights reserved.
    http://www.audiosynth.com

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
*/

#pragma once

#ifdef __APPLE__
#    include <libkern/OSAtomic.h>
#endif

#ifdef _WIN32
#    include <winsock2.h>
#    include <windows.h>
#endif

template <class T, int N> class SC_FIFO {
public:
    SC_FIFO(): mMask(N - 1), mReadHead(0), mWriteHead(0) {}

    void MakeEmpty() { mReadHead = mWriteHead; }
    bool IsEmpty() { return mReadHead == mWriteHead; }
    bool HasData() { return mReadHead != mWriteHead; }

    bool Put(T data) {
        std::int64_t next = NextPos(mWriteHead);
        if (next == mReadHead)
            return false; // fifo is full
        mItems[next] = data;
#ifdef __APPLE__
        // we don't really need a compare and swap, but this happens to call
        // the PowerPC memory barrier instruction lwsync.
        OSAtomicCompareAndSwap32Barrier(mWriteHead, next, &mWriteHead);
#elif defined(_WIN32)
        InterlockedExchange(reinterpret_cast<volatile LONG*>(&mWriteHead), next);
#else
        mWriteHead = next;
#endif
        return true;
    }

    T Get() {
        // assert(HasData());
        std::int64_t next = NextPos(mReadHead);
        T out = mItems[next];
#ifdef __APPLE__
        // we don't really need a compare and swap, but this happens to call
        // the PowerPC memory barrier instruction lwsync.
        OSAtomicCompareAndSwap32Barrier(mReadHead, next, &mReadHead);
#elif defined(_WIN32)
        InterlockedExchange(reinterpret_cast<volatile LONG*>(&mReadHead), next);
#else
        mReadHead = next;
#endif
        return out;
    }

    int Capacity() const { return N; }

private:
    int NextPos(int inPos) { return (inPos + 1) & mMask; }

    std::int64_t mMask;

#ifdef __APPLE__
    int32_t mReadHead, mWriteHead;
#else
    volatile int mReadHead, mWriteHead;
#endif
    T mItems[N];
};
