// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2013 Christian Seiler <christian@iwakd.de>
// Copyright (C) 2014-2015 Benoit Steiner <benoit.steiner.goog@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.

#ifndef EIGEN_CXX11_TENSOR_TENSORSTORAGE_H
#define EIGEN_CXX11_TENSOR_TENSORSTORAGE_H

#ifdef EIGEN_TENSOR_STORAGE_CTOR_PLUGIN
#define EIGEN_INTERNAL_TENSOR_STORAGE_CTOR_PLUGIN EIGEN_TENSOR_STORAGE_CTOR_PLUGIN;
#else
#define EIGEN_INTERNAL_TENSOR_STORAGE_CTOR_PLUGIN
#endif

namespace Eigen {

/** \internal
  *
  * \class TensorStorage
  * \ingroup CXX11_Tensor_Module
  *
  * \brief Stores the data of a tensor
  *
  * This class stores the data of fixed-size, dynamic-size or mixed tensors
  * in a way as compact as possible.
  *
  * \sa Tensor
  */
template<typename T, typename Dimensions, int Options_> class TensorStorage;


// Pure fixed-size storage
template<typename T, int Options_, typename FixedDimensions>
class TensorStorage<T, FixedDimensions, Options_>
{
  private:
    static const std::size_t Size = FixedDimensions::total_size;

    // Allocate an array of size at least one to prevent compiler warnings.
    static const std::size_t MinSize = max_n_1<Size>::size;
    EIGEN_ALIGN_MAX T m_data[MinSize];

    FixedDimensions m_dimensions;

  public:
    EIGEN_DEVICE_FUNC
    EIGEN_STRONG_INLINE
    TensorStorage()
    {
    }

    EIGEN_DEVICE_FUNC
    EIGEN_STRONG_INLINE T *
    data()
    {
        return m_data;
    }
    EIGEN_DEVICE_FUNC
    EIGEN_STRONG_INLINE const T *
    data() const
    {
        return m_data;
    }

    EIGEN_DEVICE_FUNC
    EIGEN_STRONG_INLINE const FixedDimensions&
    dimensions() const
    {
        return m_dimensions;
    }

    EIGEN_DEVICE_FUNC
    EIGEN_STRONG_INLINE DenseIndex
    size() const
    {
        return m_dimensions.TotalSize();
    }
};


// pure dynamic
template<typename T, int Options_, typename IndexType, int NumIndices_>
class TensorStorage<T, DSizes<IndexType, NumIndices_>, Options_>
{
  public:
    typedef IndexType Index;
    typedef DSizes<IndexType, NumIndices_> Dimensions;
    typedef TensorStorage<T, DSizes<IndexType, NumIndices_>, Options_> Self;

    EIGEN_DEVICE_FUNC
    TensorStorage() : m_data(0), m_dimensions()
    {
        if (NumIndices_ == 0)
        {
            m_data = internal::conditional_aligned_new_auto<T,(Options_&DontAlign)==0>(1);
        }
    }
    EIGEN_DEVICE_FUNC
    TensorStorage(internal::constructor_without_unaligned_array_assert)
        : m_data(0), m_dimensions(internal::template repeat<NumIndices_, Index>(0)) {}
    EIGEN_DEVICE_FUNC
    TensorStorage(Index size, const array<Index, NumIndices_>& dimensions)
        : m_data(internal::conditional_aligned_new_auto<T,(Options_&DontAlign)==0>(size)), m_dimensions(dimensions)
    {
        EIGEN_INTERNAL_TENSOR_STORAGE_CTOR_PLUGIN
    }

#if EIGEN_HAS_VARIADIC_TEMPLATES
    template <typename... DenseIndex>
    EIGEN_DEVICE_FUNC
    TensorStorage(DenseIndex... indices) : m_dimensions(indices...)
    {
        m_data = internal::conditional_aligned_new_auto<T,(Options_&DontAlign)==0>(internal::array_prod(m_dimensions));
    }
#endif

    EIGEN_DEVICE_FUNC
    TensorStorage(const Self& other)
        : m_data(internal::conditional_aligned_new_auto<T,(Options_&DontAlign)==0>(internal::array_prod(other.m_dimensions)))
        , m_dimensions(other.m_dimensions)
    {
        internal::smart_copy(other.m_data, other.m_data+internal::array_prod(other.m_dimensions), m_data);
    }
    EIGEN_DEVICE_FUNC Self&
    operator=(const Self& other)
    {
        if (this != &other)
        {
            Self tmp(other);
            this->swap(tmp);
        }

        return *this;
    }

    EIGEN_DEVICE_FUNC
    ~TensorStorage()
    {
        internal::conditional_aligned_delete_auto<T,(Options_&DontAlign)==0>(m_data, internal::array_prod(m_dimensions));
    }
    EIGEN_DEVICE_FUNC  void
    swap(Self& other)
    {
        numext::swap(m_data,other.m_data);
        numext::swap(m_dimensions,other.m_dimensions);
    }

    EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const Dimensions&
    dimensions() const
    {
        return m_dimensions;
    }

    EIGEN_DEVICE_FUNC void
    resize(Index size, const array<Index, NumIndices_>& nbDimensions)
    {
        const Index currentSz = internal::array_prod(m_dimensions);

        if (size != currentSz)
        {
            internal::conditional_aligned_delete_auto<T,(Options_&DontAlign)==0>(m_data, currentSz);

            if (size)
            {
                m_data = internal::conditional_aligned_new_auto<T,(Options_&DontAlign)==0>(size);
            }

            else if (NumIndices_ == 0)
            {
                m_data = internal::conditional_aligned_new_auto<T,(Options_&DontAlign)==0>(1);
            }

            else
            {
                m_data = 0;
            }

            EIGEN_INTERNAL_DENSE_STORAGE_CTOR_PLUGIN({})
        }

        m_dimensions = nbDimensions;
    }

    EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE T *
    data()
    {
        return m_data;
    }
    EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const T *
    data() const
    {
        return m_data;
    }

    EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Index
    size() const
    {
        return m_dimensions.TotalSize();
    }

  private:
    T *m_data;
    Dimensions m_dimensions;
};

} // end namespace Eigen

#endif // EIGEN_CXX11_TENSOR_TENSORSTORAGE_H
