/*
* Copyright (C) 2010 Gennady Proskurin (https://launchpad.net/~gpr)
*
* 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

#include "CriticalSection.h"
#include <boost/version.hpp>
#include <boost/interprocess/detail/atomic.hpp>
#include <boost/cstdint.hpp>

namespace dcpp {

// Ordering arguments:

// memory_ordering_weak
// Suitable only for thread-safe accounting of some statistics.
// Value can not be used as "flag" (you cannot do any multi-thread action, based
// on this value) since it does not garantees necessary memory barriers.
class memory_ordering_weak {};

// memory_ordering_strong
// Suitable for any multi-thread purpose
class memory_ordering_strong {};

template <typename DataType, class Ordering = memory_ordering_strong>
class Atomic;


// uint32_t
template <>
class Atomic<boost::uint32_t, memory_ordering_weak> {
        typedef boost::uint32_t value_type;
public:
        Atomic(value_type val) { assign(val); }
        Atomic(const Atomic& other) { assign(static_cast<value_type>(other)); }

        // operator=
        // return void to be safe
        void operator=(value_type val) { assign(val); }
        void operator=(const Atomic& other) {
                return operator=(static_cast<value_type>(other));
        }

#if BOOST_VERSION >= 104800
        // type cast
        operator value_type() const {
                return boost::interprocess::ipcdetail::atomic_read32(&m_value);
        }

        // increment
        void inc() { boost::interprocess::ipcdetail::atomic_inc32(&m_value); }

        // decrement
        void dec() { boost::interprocess::ipcdetail::atomic_dec32(&m_value); }

private:
        mutable value_type m_value;
        void assign(value_type val) { boost::interprocess::ipcdetail::atomic_write32(&m_value, val); }
#else
                // type cast
        operator value_type() const {
                return boost::interprocess::detail::atomic_read32(&m_value);
        }

        // increment
        void inc() { boost::interprocess::detail::atomic_inc32(&m_value); }

        // decrement
        void dec() { boost::interprocess::detail::atomic_dec32(&m_value); }

private:
        mutable value_type m_value;
        void assign(value_type val) { boost::interprocess::detail::atomic_write32(&m_value, val); }
#endif //BOOST_VERSION
};

// int32_t
// just forward all operations to underlying Atomic<uint32_t, ...> variable
template <>
class Atomic<boost::int32_t, memory_ordering_weak> {
        typedef boost::int32_t value_type;
public:
        Atomic(value_type val) : m_value(val) {}
        Atomic(const Atomic& other) : m_value(other) {}

        void operator=(value_type val)          { m_value=val; }
        void operator=(const Atomic& other)     { m_value=other; }
        operator value_type() const             { return static_cast<value_type>(m_value); }

        void inc() { m_value.inc(); }
        void dec() { m_value.dec(); }
private:
        Atomic<boost::uint32_t,memory_ordering_weak> m_value;
};

// memory_ordering_strong
template <typename DataType>
class Atomic<DataType, memory_ordering_strong> {
        typedef DataType value_type;
public:
        Atomic(value_type new_value) : m_value(new_value) {}
        Atomic(const Atomic& other) : m_value(static_cast<value_type>(other)) {}

        void operator=(value_type new_value) {
                FastLock Lock(cs);
                m_value = new_value;
        }
        void operator=(const Atomic& other) {
                FastLock Lock(cs);
                m_value = other;
        }
        operator value_type() const {
                FastLock Lock(cs); // shared (read-only) lock would be sufficient here
                return m_value;
        }

        void inc() {
                FastLock Lock(cs);
                ++m_value;
        }
        void dec() {
                FastLock Lock(cs);
                --m_value;
        }

        // assign new value, return old value
        value_type exchange(value_type new_val) {
                FastLock Lock(cs);
                value_type old_val = m_value;
                m_value = new_val;
                return old_val;
        }
private:
        value_type m_value;
        mutable FastCriticalSection cs;
};

} // namespace dcpp
