/*
* Copyright (C) 2010 Gennady Proskurin (https://launchpad.net/~gpr)
* Copyright (C) 2020 Boris Pek <tehnick-8@yandex.ru>
*
* 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, see <https://www.gnu.org/licenses/>.
*/

#pragma once

#include "CriticalSection.h"

#include <atomic>

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<std::uint32_t, memory_ordering_weak> {
    typedef std::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));
    }

    // type cast
    operator value_type() const {
        return m_value.load(std::memory_order_relaxed);
    }

    // increment
    void inc() { m_value.fetch_add(1, std::memory_order_relaxed); }

    // decrement
    void dec() { m_value.fetch_sub(1, std::memory_order_relaxed); }

private:
    mutable std::atomic<value_type> m_value;
    void assign(value_type val) { m_value.store(val, std::memory_order_relaxed); }
};

// int32_t
// just forward all operations to underlying Atomic<uint32_t, ...> variable
template <>
class Atomic<std::int32_t, memory_ordering_weak> {
    typedef std::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<std::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
