/*
	Copyright 2016, 2017 Michele "King_DuckZ" Santullo

	This file is part of MyCurry.

	MyCurry 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 3 of the License, or
	(at your option) any later version.

	MyCurry 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 MyCurry.  If not, see <http://www.gnu.org/licenses/>.
*/

#pragma once

#include "vectorwrapper/vectorwrapper.hpp"
#include <array>
#include <cstdint>
#if !defined(NDEBUG)
#	include <iostream>
#endif
#include <utility>

#define SPECIALIZE_ARRAY_VECTOR(TYPE, DIM) \
    template <> \
    struct VectorWrapperInfo<std::array<TYPE, DIM>> { \
        enum { dimensions = DIM }; \
        typedef TYPE scalar_type; \
        typedef std::array<scalar_type, dimensions> vector_type; \
        static scalar_type& get_at (size_t parIndex, vector_type& parVector) { \
            return parVector[parIndex]; \
        } \
    }

namespace vwr {
	SPECIALIZE_ARRAY_VECTOR(float, 2);
	SPECIALIZE_ARRAY_VECTOR(uint16_t, 2);
	SPECIALIZE_ARRAY_VECTOR(int32_t, 2);

#if !defined(NDEBUG)
	template <typename V>
	std::ostream& operator<< (std::ostream& parStream, const Vec<V>& parVec) {
		parStream << '<';
		for (std::size_t z = 0; z < Vec<V>::dimensions - 1; ++z) {
			std::cout << parVec[z] << ", ";
		}
		std::cout << parVec[Vec<V>::dimensions - 1] << '>';
		return parStream;
	}
#endif

	namespace implem {
		template <typename TO, typename FROM, std::size_t... I>
		Vec<TO> vector_cast (const Vec<FROM>& parVec, Vec<TO>*, std::index_sequence<I...>) {
			static_assert(
				static_cast<int>(Vec<TO>::dimensions) == static_cast<int>(Vec<FROM>::dimensions),
				"Mismatching dimensions"
			);
			static_assert(sizeof...(I) == Vec<TO>::dimensions, "Mismatching index count");
			typedef typename Vec<TO>::scalar_type CastType;

			return Vec<TO>(static_cast<CastType>(parVec[I])...);
		}
	} //namespace implem

	template <typename TOVec, typename FROM>
	TOVec vector_cast (const Vec<FROM>& parVec) {
		TOVec* const to = nullptr;
		return implem::vector_cast(parVec, to, std::make_index_sequence<Vec<FROM>::dimensions>());
	}
} //namespace vwr

namespace curry {
	using vec2f = vwr::Vec<std::array<float, 2>>;
	using vec2us = vwr::Vec<std::array<uint16_t, 2>>;
	using vec2i = vwr::Vec<std::array<int32_t, 2>>;
	using vwr::vector_cast;
} //namespace curry

//make stuff from CloonelJump compile happily
namespace cloonel {
	using ushort2 = vwr::Vec<std::array<uint16_t, 2>>;
	using float2 = vwr::Vec<std::array<float, 2>>;
} //namespace cloonel

#undef SPECIALIZE_ARRAY_VECTOR
