/***************************************************************************
*                                                                          *
*  Copyright (C) Golaem S.A.  All Rights Reserved.                         *
*                                                                          *
***************************************************************************/

#pragma once

#include "glmCoreAPI.h"
#include "glmAtomic.h"

//NOTE: some interesting points on busy waiting by Bruce Dawson: http://www.altdevblogaday.org/2012/06/05/in-praise-of-idleness/

namespace glm
{
    struct spinlockdef_t;
    bool GLM_CORE_API createSpinLock(spinlockdef_t& spinlock);
    void GLM_CORE_API destroySpinLock(spinlockdef_t& spinlock);

    void GLM_CORE_API lock(spinlockdef_t& spinlock);
    bool GLM_CORE_API trylock(spinlockdef_t& spinlock);
    void GLM_CORE_API unlock(spinlockdef_t& spinlock);

    struct rwspinlockdef_t;
    bool GLM_CORE_API createReaderWriterSpinLock(rwspinlockdef_t& rwspinlock);
    void GLM_CORE_API destroyReaderWriterSpinLock(rwspinlockdef_t& rwspinlock);

    void GLM_CORE_API lockReader(rwspinlockdef_t& rwspinlock);
    bool GLM_CORE_API trylockReader(rwspinlockdef_t& rwspinlock);
    void GLM_CORE_API unlockReader(rwspinlockdef_t& rwspinlock);

    void GLM_CORE_API lockWriter(rwspinlockdef_t& rwspinlock);
    bool GLM_CORE_API trylockWriter(rwspinlockdef_t& rwspinlock);
    void GLM_CORE_API unlockWriter(rwspinlockdef_t& rwspinlock);

    struct GLM_CORE_API spinlockdef_t
    {
        atomic32_t _isLocked;
    };

    struct GLM_CORE_API rwspinlockdef_t
	{
		//based implementation on TBB 4 tbb::spin_rw_mutex_v3
		//NOTE: for other references, possibly look at http://locklessinc.com/articles/locks/ or http://joeduffyblog.com/2009/01/29/a-singleword-readerwriter-spin-lock/
		atomic32_t _lockedState;
	};

    class GLM_CORE_API Mutex
    {
    public:
        struct mutex_type;

    private:
        mutex_type* _mutex = NULL;

    public:
        Mutex();
        ~Mutex();

        void lock();
        bool trylock();
        void unlock();

    private:
        GLM_DISALLOW_COPY_AND_ASSIGN(Mutex);
    };

    class GLM_CORE_API SpinLock
    {
	public:
		SpinLock() { createSpinLock(_spinlock); }
		~SpinLock() { destroySpinLock(_spinlock); }

		void lock() { glm::lock(_spinlock); }
		bool trylock() { return glm::trylock(_spinlock); }
		void unlock() { glm::unlock(_spinlock); }

	private:
		spinlockdef_t _spinlock;
	};

	class GLM_CORE_API RWSpinLock
	{
	public:
		RWSpinLock() { createReaderWriterSpinLock(_rwspinlock); }
		~RWSpinLock() { destroyReaderWriterSpinLock(_rwspinlock); }

		void lockReader() { glm::lockReader(_rwspinlock); }
		bool trylockReader() { return glm::trylockReader(_rwspinlock); }
		void unlockReader() { glm::unlockReader(_rwspinlock); }

		void lockWriter() { glm::lockWriter(_rwspinlock); }
		bool trylockWriter() { return glm::trylockWriter(_rwspinlock); }
		void unlockWriter() { glm::unlockWriter(_rwspinlock); }

	private:
		rwspinlockdef_t _rwspinlock;
    };
} // namespace glm
