// Copyright (c) 2020, Viktor Larsson
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
//     * Neither the name of the copyright holder nor the
//       names of its contributors may be used to endorse or promote products
//       derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#pragma once

// Wrapped header from COLMAP:
// https://github.com/colmap/colmap/blob/dev/src/util/alignment.h
// Author: Johannes L. Schoenberger (jsch-at-demuc-dot-de)

#ifndef COLMAP_SRC_UTIL_ALIGNMENT_H_
#define COLMAP_SRC_UTIL_ALIGNMENT_H_

#include <Eigen/Core>
#ifdef _MSVC_LANG
#define CPP_VERSION _MSVC_LANG
#else
#define CPP_VERSION __cplusplus
#endif
#if !EIGEN_VERSION_AT_LEAST(3, 4, 0) || CPP_VERSION < 201703L

#include <Eigen/Core>
#include <Eigen/Geometry>
#include <Eigen/StdVector>
#include <initializer_list>
#include <memory>
#include <vector>

#ifndef EIGEN_ALIGNED_ALLOCATOR
#define EIGEN_ALIGNED_ALLOCATOR Eigen::aligned_allocator
#endif

// Equivalent to EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION but with support for
// initializer lists, which is a C++11 feature and not supported by the Eigen.
// The initializer list extension is inspired by Theia and StackOverflow code.
#define EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION_CUSTOM(...)                                                             \
    namespace std {                                                                                                    \
    template <>                                                                                                        \
    class vector<__VA_ARGS__, std::allocator<__VA_ARGS__>>                                                             \
        : public vector<__VA_ARGS__, EIGEN_ALIGNED_ALLOCATOR<__VA_ARGS__>> {                                           \
        typedef vector<__VA_ARGS__, EIGEN_ALIGNED_ALLOCATOR<__VA_ARGS__>> vector_base;                                 \
                                                                                                                       \
      public:                                                                                                          \
        typedef __VA_ARGS__ value_type;                                                                                \
        typedef vector_base::allocator_type allocator_type;                                                            \
        typedef vector_base::size_type size_type;                                                                      \
        typedef vector_base::iterator iterator;                                                                        \
        explicit vector(const allocator_type &a = allocator_type()) : vector_base(a) {}                                \
        template <typename InputIterator>                                                                              \
        vector(InputIterator first, InputIterator last, const allocator_type &a = allocator_type())                    \
            : vector_base(first, last, a) {}                                                                           \
        vector(const vector &c) : vector_base(c) {}                                                                    \
        explicit vector(size_type num, const value_type &val = value_type()) : vector_base(num, val) {}                \
        vector(iterator start, iterator end) : vector_base(start, end) {}                                              \
        vector &operator=(const vector &x) {                                                                           \
            vector_base::operator=(x);                                                                                 \
            return *this;                                                                                              \
        }                                                                                                              \
        vector(initializer_list<__VA_ARGS__> list) : vector_base(list.begin(), list.end()) {}                          \
    };                                                                                                                 \
    } // namespace std

EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION_CUSTOM(Eigen::Vector2d)
EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION_CUSTOM(Eigen::Vector4d)
EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION_CUSTOM(Eigen::Vector4f)
EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION_CUSTOM(Eigen::Matrix2d)
EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION_CUSTOM(Eigen::Matrix2f)
EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION_CUSTOM(Eigen::Matrix4d)
EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION_CUSTOM(Eigen::Matrix4f)
EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION_CUSTOM(Eigen::Affine3d)
EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION_CUSTOM(Eigen::Affine3f)
EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION_CUSTOM(Eigen::Quaterniond)
EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION_CUSTOM(Eigen::Quaternionf)
EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION_CUSTOM(Eigen::Matrix<float, 3, 4>)
EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION_CUSTOM(Eigen::Matrix<double, 3, 4>)

#define EIGEN_STL_UMAP(KEY, VALUE)                                                                                     \
    std::unordered_map<KEY, VALUE, std::hash<KEY>, std::equal_to<KEY>,                                                 \
                       Eigen::aligned_allocator<std::pair<KEY const, VALUE>>>

#endif
#undef CPP_VERSION
#endif