/*
 * 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 https://mozilla.org/MPL/2.0/.
 */

#pragma once

#include "Math/Math.hpp"
#include "Shader/VertexInputDescription.hpp"
#if __has_include("assimp/vector2.h")
#include <assimp/vector2.h>
#include <assimp/vector3.h>
#include <assimp/color4.h>
#endif

namespace OpenVulkano
{
	struct Vertex
	{
		Math::Vector3f position, normal, tangent, biTangent, textureCoordinates;
		Math::Vector4f color;

		Vertex() = default;

		Vertex(const float& x, const float& y, const float& z, const float& nx, const float& ny, const float& nz, const float& u, const float& v)
			: position({ x, y, z }), normal({ nx, ny, nz }), tangent(), biTangent(), textureCoordinates({ u, v, 0 }), color()
		{}

		Vertex(const Math::Vector3f& position, const Math::Vector3f& normal, const Math::Vector3f& tangent, const Math::Vector3f& biTangent, const Math::Vector2f& textureCoordinates)
			: position(position), normal(normal), tangent(tangent), biTangent(biTangent), textureCoordinates(textureCoordinates, 0), color()
		{}

		Vertex(const Math::Vector3f & position, const Math::Vector3f & normal, const Math::Vector3f & tangent, const Math::Vector3f & biTangent, const Math::Vector3f & textureCoordinates)
			: position(position), normal(normal), tangent(tangent), biTangent(biTangent), textureCoordinates(textureCoordinates), color()
		{}

		~Vertex() = default;

		void Set(const float& x, const float& y, const float& z)
		{
			position = { x, y, z };
		}

		void Set(const Math::Vector3f& position)
		{
			this->position = position;
		}

		void Set(const float& x, const float& y, const float& z, const float& nx, const float& ny, const float& nz, const float& u, const float& v)
		{
			this->position = { x, y, z };
			this->normal = { nx, ny, nz };
			this->textureCoordinates = { u, v, 0 };
		}

		void Set(const Math::Vector3f& position, const Math::Vector3f& normal, const Math::Vector2f& textureCoordinates)
		{
			this->position = position;
			this->normal = normal;
			this->textureCoordinates = { textureCoordinates, 0 };
		}

		void Set(const Math::Vector3f& position, const Math::Vector3f& normal, const Math::Vector3f& tangent, const Math::Vector3f& biTangent, const Math::Vector2f& textureCoordinates)
		{
			this->position = position;
			this->normal = normal;
			this->tangent = tangent;
			this->biTangent = biTangent;
			this->textureCoordinates = { textureCoordinates,0 };
		}

		void SetNormal(const float& nx, const float& ny, const float& nz)
		{
			this->normal = { nx, ny, nz };
		}

		void SetNormal(const Math::Vector3f& normal)
		{
			this->normal = normal;
		}

		void SetTangent(const float& tx, const float& ty, const float& tz)
		{
			this->tangent = { tx, ty, tz };
		}

		void SetTangent(const Math::Vector3f& tangent)
		{
			this->tangent = tangent;
		}

		void SetTangentAndBiTangent(const float& tx, const float& ty, const float& tz, const float& bx, const float& by, const float& bz)
		{
			this->biTangent = { bx, by, bz };
			this->tangent = { tx, ty, tz };
		}

		void SetTangentAndBiTangent(const Math::Vector3f& tangent, const Math::Vector3f& biTangent)
		{
			this->biTangent = biTangent;
			this->tangent = tangent;
		}

		void SetBiTangent(const float& bx, const float& by, const float& bz)
		{
			this->biTangent = { bx, by, bz };
		}

		void SetBiTangent(const Math::Vector3f& biTangent)
		{
			this->biTangent = biTangent;
		}

		void SetTextureCoordinates(const float& u, const float& v)
		{
			this->textureCoordinates = { u, v, 0 };
		}

		void SetTextureCoordinates(const float& u, const float& v, const float& w)
		{
			this->textureCoordinates = { u, v, w };
		}

		void SetTextureCoordinates(const Math::Vector2f& textureCoordinates)
		{
			this->textureCoordinates = { textureCoordinates, 0 };
		}

		void SetTextureCoordinates(const Math::Vector3f& textureCoordinates)
		{
			this->textureCoordinates = textureCoordinates;
		}

		void SetColor(const float& r, const float& g, const float& b, const float& a = 1)
		{
			color = { r,g,b,a };
		}

		void SetColor(const Math::Vector4f& color)
		{
			this->color = color;
		}

#if __has_include("assimp/vector2.h")
		Vertex(const aiVector3D& pos) : position(pos.x, pos.y, pos.z), normal(), tangent(), biTangent(), textureCoordinates(), color()
		{}

		void Set(const aiVector3D& position)
		{
			this->position = { position.x, position.y, position.z };
		}

		 void SetNormal(const aiVector3D& normal)
		{
			this->normal = { normal.x, normal.y, normal.z };
		}

		 void SetTangent(const aiVector3D& tangent)
		{
			this->tangent = { tangent.x, tangent.y, tangent.z };
		}

		void SetTangentAndBiTangent(const aiVector3D& tangent, const aiVector3D& biTangent)
		{
			this->tangent = { tangent.x, tangent.y, tangent.z };
			this->biTangent = { biTangent.x, biTangent.y, biTangent.z };
		}

		 void SetBiTangent(const aiVector3D& biTangent)
		{
			this->biTangent = { biTangent.x, biTangent.y, biTangent.z };
		}

		 void SetTextureCoordinates(const aiVector2D& textureCoordinates)
		{
			this->textureCoordinates = { textureCoordinates.x, textureCoordinates.y, 0 };
		}

		void SetTextureCoordinates(const aiVector3D& textureCoordinates)
		{
			this->textureCoordinates = { textureCoordinates.x, textureCoordinates.y, textureCoordinates.z };
		}

		void SetColor(const aiColor4D& color)
		{
			this->color = { color.r, color.g, color.b, color.a };
		}
#endif

		static VertexInputDescription GetVertexInputDescription()
		{
			VertexInputDescription description(0, sizeof(Vertex));
			description.AddInputParameter(DataFormat::R32G32B32_SFLOAT, offsetof(Vertex, position));
			description.AddInputParameter(DataFormat::R32G32B32_SFLOAT, offsetof(Vertex, normal));
			description.AddInputParameter(DataFormat::R32G32B32_SFLOAT, offsetof(Vertex, tangent));
			description.AddInputParameter(DataFormat::R32G32B32_SFLOAT, offsetof(Vertex, biTangent));
			description.AddInputParameter(DataFormat::R32G32B32_SFLOAT, offsetof(Vertex, textureCoordinates));
			description.AddInputParameter(DataFormat::R32G32B32A32_SFLOAT, offsetof(Vertex, color));
			return description;
		}
	};
}
