/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkAtomic.h

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
// .NAME vtkAtomic -
// .SECTION Description

#ifndef vtkAtomic_h
#define vtkAtomic_h

#include "vtkCommonCoreModule.h" // For export macro
#include "vtkAtomicTypeConcepts.h"
#include "vtkSystemIncludes.h"

#include <cstddef>


#ifndef __VTK_WRAP__
namespace detail
{

template <size_t size> class AtomicOps;

template <> class VTKCOMMONCORE_EXPORT AtomicOps<8>
{
public:
  typedef vtkTypeInt64 atomic_type;

  static vtkTypeInt64 AddAndFetch(vtkTypeInt64 *ref, vtkTypeInt64 val);
  static vtkTypeInt64 SubAndFetch(vtkTypeInt64 *ref, vtkTypeInt64 val);
  static vtkTypeInt64 PreIncrement(vtkTypeInt64 *ref);
  static vtkTypeInt64 PreDecrement(vtkTypeInt64 *ref);
  static vtkTypeInt64 PostIncrement(vtkTypeInt64 *ref);
  static vtkTypeInt64 PostDecrement(vtkTypeInt64 *ref);
  static vtkTypeInt64 Load(const vtkTypeInt64 *ref);
  static void Store(vtkTypeInt64 *ref, vtkTypeInt64 val);
};

template <> class VTKCOMMONCORE_EXPORT AtomicOps<4>
{
public:
  typedef vtkTypeInt32 atomic_type;

  static vtkTypeInt32 AddAndFetch(vtkTypeInt32 *ref, vtkTypeInt32 val);
  static vtkTypeInt32 SubAndFetch(vtkTypeInt32 *ref, vtkTypeInt32 val);
  static vtkTypeInt32 PreIncrement(vtkTypeInt32 *ref);
  static vtkTypeInt32 PreDecrement(vtkTypeInt32 *ref);
  static vtkTypeInt32 PostIncrement(vtkTypeInt32 *ref);
  static vtkTypeInt32 PostDecrement(vtkTypeInt32 *ref);
  static vtkTypeInt32 Load(const vtkTypeInt32 *ref);
  static void Store(vtkTypeInt32 *ref, vtkTypeInt32 val);
};

} // detail
#endif // __VTK_WRAP__


template <typename T> class vtkAtomic : private vtk::atomic::detail::IntegralType<T>
{
private:
  typedef detail::AtomicOps<sizeof(T)> Impl;

public:
  vtkAtomic() : Atomic(0)
  {
  }

  vtkAtomic(T val) : Atomic(static_cast<typename Impl::atomic_type>(val))
  {
  }

  vtkAtomic(const vtkAtomic<T> &atomic)
    : Atomic(static_cast<typename Impl::atomic_type>(atomic.load()))
  {
  }

  T operator++()
  {
    return static_cast<T>(Impl::PreIncrement(&this->Atomic));
  }

  T operator++(int)
  {
    return static_cast<T>(Impl::PostIncrement(&this->Atomic));
  }

  T operator--()
  {
    return static_cast<T>(Impl::PreDecrement(&this->Atomic));
  }

  T operator--(int)
  {
    return static_cast<T>(Impl::PostDecrement(&this->Atomic));
  }

  T operator+=(T val)
  {
    return static_cast<T>(Impl::AddAndFetch(&this->Atomic,
      static_cast<typename Impl::atomic_type>(val)));
  }

  T operator-=(T val)
  {
    return static_cast<T>(Impl::SubAndFetch(&this->Atomic,
      static_cast<typename Impl::atomic_type>(val)));
  }

  operator T() const
  {
    return static_cast<T>(Impl::Load(&this->Atomic));
  }

  T operator=(T val)
  {
    Impl::Store(&this->Atomic, static_cast<typename Impl::atomic_type>(val));
    return val;
  }

  vtkAtomic<T>& operator=(const vtkAtomic<T> &atomic)
  {
    this->store(atomic.load());
    return *this;
  }

  T load() const
  {
    return static_cast<T>(Impl::Load(&this->Atomic));
  }

  void store(T val)
  {
    Impl::Store(&this->Atomic, static_cast<typename Impl::atomic_type>(val));
  }

private:
  typename Impl::atomic_type Atomic;
};


template <typename T> class vtkAtomic<T*>
{
private:
  typedef detail::AtomicOps<sizeof(T*)> Impl;

public:
  vtkAtomic() : Atomic(0)
  {
  }

  vtkAtomic(T* val)
    : Atomic(reinterpret_cast<typename Impl::atomic_type>(val))
  {
  }

  vtkAtomic(const vtkAtomic<T*> &atomic)
    : Atomic(reinterpret_cast<typename Impl::atomic_type>(atomic.load()))
  {
  }

  T* operator++()
  {
    return reinterpret_cast<T*>(Impl::AddAndFetch(&this->Atomic, sizeof(T)));
  }

  T* operator++(int)
  {
    T* val = reinterpret_cast<T*>(Impl::AddAndFetch(&this->Atomic, sizeof(T)));
    return --val;
  }

  T* operator--()
  {
    return reinterpret_cast<T*>(Impl::SubAndFetch(&this->Atomic, sizeof(T)));
  }

  T* operator--(int)
  {
    T* val = reinterpret_cast<T*>(Impl::AddAndFetch(&this->Atomic, sizeof(T)));
    return ++val;
  }

  T* operator+=(std::ptrdiff_t val)
  {
    return reinterpret_cast<T*>(Impl::AddAndFetch(&this->Atomic,
                                                  val * sizeof(T)));
  }

  T* operator-=(std::ptrdiff_t val)
  {
    return reinterpret_cast<T*>(Impl::SubAndFetch(&this->Atomic,
                                                  val * sizeof(T)));
  }

  operator T*() const
  {
    return reinterpret_cast<T*>(Impl::Load(&this->Atomic));
  }

  T* operator=(T* val)
  {
    Impl::Store(&this->Atomic,
                reinterpret_cast<typename Impl::atomic_type>(val));
    return val;
  }

  vtkAtomic<T*>& operator=(const vtkAtomic<T*> &atomic)
  {
    this->store(atomic.load());
    return *this;
  }

  T* load() const
  {
    return reinterpret_cast<T*>(Impl::Load(&this->Atomic));
  }

  void store(T* val)
  {
    Impl::Store(&this->Atomic,
                reinterpret_cast<typename Impl::atomic_type>(val));
  }

private:
  typename Impl::atomic_type Atomic;
};


template <> class vtkAtomic<void*>
{
private:
  typedef detail::AtomicOps<sizeof(void*)> Impl;

public:
  vtkAtomic() : Atomic(0)
  {
  }

  vtkAtomic(void* val)
    : Atomic(reinterpret_cast<Impl::atomic_type>(val))
  {
  }

  vtkAtomic(const vtkAtomic<void*> &atomic)
    : Atomic(reinterpret_cast<Impl::atomic_type>(atomic.load()))
  {
  }

  operator void*() const
  {
    return reinterpret_cast<void*>(Impl::Load(&this->Atomic));
  }

  void* operator=(void* val)
  {
    Impl::Store(&this->Atomic,
                reinterpret_cast<Impl::atomic_type>(val));
    return val;
  }

  vtkAtomic<void*>& operator=(const vtkAtomic<void*> &atomic)
  {
    this->store(atomic.load());
    return *this;
  }

  void* load() const
  {
    return reinterpret_cast<void*>(Impl::Load(&this->Atomic));
  }

  void store(void* val)
  {
    Impl::Store(&this->Atomic,
                reinterpret_cast<Impl::atomic_type>(val));
  }

private:
  Impl::atomic_type Atomic;
};

#endif
// VTK-HeaderTest-Exclude: vtkAtomic.h
