#pragma once

#include "UnityPlatformConfigure.h"
#include "ExtendedAtomicTypes.h"

UNITY_PLATFORM_BEGIN_NAMESPACE;

#if defined(ATOMIC_HAS_DCAS)

    #define ATOMIC_HAS_QUEUE    2

#elif defined(__arm64__) && (defined(__clang__) || defined(__GNUC__))

    #define ATOMIC_HAS_QUEUE    1

#elif defined(__arm__) && (defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7S__)) && (!UNITY_STV_API) && (defined(__clang__) || defined(__GNUC__) || defined(SN_TARGET_PSP2))

    #define ATOMIC_HAS_QUEUE    1

#endif

class AtomicNode
{
    friend class AtomicStack;
    friend class AtomicQueue;

    volatile atomic_word _next;

public:
    void* data[3];

    AtomicNode *Next() const
    {
        return (AtomicNode*)_next;
    }

    AtomicNode *Link(AtomicNode *next);
};

#if defined(ATOMIC_HAS_QUEUE)


// A generic lockfree stack.
// Any thread can Push / Pop nodes to the stack.

// The stack is lockfree and highly optimized. It has different implementations for different architectures.

// On intel / arm it is built with double CAS:
// http://en.wikipedia.org/wiki/Double_compare-and-swap
// On PPC it is built on LL/SC:
// http://en.wikipedia.org/wiki/Load-link/store-conditional

class AtomicStack
{
#if defined(ATOMIC_HAS_DCAS)
    volatile atomic_word2   _top;
#else
    volatile atomic_word    _top;
#endif

public:
    AtomicStack();
    ~AtomicStack();

    int IsEmpty() const;

    void Push(AtomicNode *node);
    void PushAll(AtomicNode *first, AtomicNode *last);

    AtomicNode *Pop();
    AtomicNode *PopAll();
};

AtomicStack* CreateAtomicStack();
void DestroyAtomicStack(AtomicStack* s);


// A generic lockfree queue FIFO queue.
// Any thread can Enqueue / Dequeue in paralell.
// When pushing / popping a node there is no guarantee that the pointer to the node is the same (void* data[3])
// We do guarantee that all 3 data pointer are the same after deqeuing.


// The queue is lockfree and highly optimized. It has different implementations for different archectures.

// On intel / arm it is built with double CAS:
// http://en.wikipedia.org/wiki/Double_compare-and-swap
// On PPC it is built on LL/SC:
// http://en.wikipedia.org/wiki/Load-link/store-conditional

class AtomicQueue
{
#if defined(ATOMIC_HAS_DCAS)
    volatile atomic_word2   _tail;
#else
    volatile atomic_word    _tail;
#endif
    volatile atomic_word    _head;

public:
    AtomicQueue();
    ~AtomicQueue();

    int IsEmpty() const;

    void Enqueue(AtomicNode *node);
    void EnqueueAll(AtomicNode *first, AtomicNode *last);
    AtomicNode *Dequeue();
};

AtomicQueue* CreateAtomicQueue();
void DestroyAtomicQueue(AtomicQueue* s);

#elif IL2CPP_SUPPORT_THREADS
#error Platform is missing atomic queue implementation
#endif

//
// Special concurrent list for JobQueue
// This code is not meant to be general purpose and should not be used outside of the job queue.

class AtomicList
{
#if defined(ATOMIC_HAS_DCAS)

    volatile atomic_word2   _top;

#else

    volatile atomic_word    _top;
    volatile atomic_word    _ver;

#endif

public:
    void Init();

    atomic_word Tag();
    AtomicNode *Peek();
    AtomicNode *Load(atomic_word &tag);

    AtomicNode *Clear(AtomicNode *old, atomic_word tag);

    bool Add(AtomicNode *first, AtomicNode *last, atomic_word tag);
    AtomicNode* Touch(atomic_word tag);
    void Reset(AtomicNode *node, atomic_word tag);

    static void Relax();
};

UNITY_PLATFORM_END_NAMESPACE;
