// =====================================================================================================================
//  fennec, a free and open source game engine
//  Copyright © 2025 - 2026  Medusa Slockbower
//
//  This program 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.
//
//  This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.
// =====================================================================================================================

///
/// \file fennec/containers/pair.h
/// \brief A header containing the definition for a container holding a pair of values
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///

#ifndef FENNEC_CONTAINERS_PAIR_H
#define FENNEC_CONTAINERS_PAIR_H

#include <fennec/containers/tuple.h>
#include <fennec/lang/hashing.h>
#include <fennec/lang/utility.h>

namespace fennec
{

// TODO: Document

///
/// \brief Struct for holding a pair of values
/// \details
/// | Property    | Value       |
/// |:-----------:|:-----------:|
/// | stable      |      ✅     |
/// | dynamic     |      ⛔     |
/// | homogeneous |      ⛔     |
/// | distinct    |      ⛔     |
/// | ordered     |      ⛔     |
/// | linear      |      ✅     |
/// | space       | \emph{O(1)} |
/// | access      | \emph{O(1)} |
/// | find        |      ⛔     |
/// | insertion   |      ⛔     |
/// | deletion    |      ⛔     |
///
/// \tparam TypeT0 The type of the first value
/// \tparam TypeT1 The type of the second value
template<typename TypeT0, typename TypeT1>
struct pair {

// Public Member Variables =============================================================================================
public:

	/// \name Member Variables
	/// @{

	TypeT0 first;  //!< The first value in the pair
	TypeT1 second; //!< The second value in the pair

	/// @}


// Constructors ========================================================================================================
public:

	/// \name Constructors & Destructor
	/// @{

	///
	/// \brief Default Constructor, invokes default constructor for both elements
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr pair() = default;

	///
	/// \brief Destructor, invokes destructor for both elements
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr ~pair() = default;

	///
	/// \brief Pair Copy Constructor
	/// \param x Value to copy for the first element
	/// \param y Value to copy for the first element
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr pair(const TypeT0& x, const TypeT1& y)
		: first(x)
		, second(y) {
	}

	///
	/// \brief Pair Move Constructor
	/// \param x Value to move for the first element
	/// \param y Value to move for the first element
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr pair(TypeT0&& x, TypeT1&& y) noexcept
		: first(fennec::forward<TypeT0>(x))
		, second(fennec::forward<TypeT1>(y)) {
	}

	///
	/// \brief Pair Implicit Constructor
	/// \param arg1 Value to initialize the first element
	/// \param arg2 Value to initialize the first element
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	template<typename Arg1T, typename Arg2T>
	constexpr pair(Arg1T&& arg1, Arg2T&& arg2)
		: first(fennec::forward<Arg1T>(arg1))
		, second(fennec::forward<Arg2T>(arg2)) {
	}

	///
	/// \brief Copy Constructor, copies both elements
	/// \param pair The pair to copy
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr pair(const pair& pair)
		: first(fennec::copy(pair.first))
		, second(fennec::copy(pair.second)) {
	}

	///
	/// \brief Move Constructor, moves both elements
	/// \param pair The pair to move
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr pair(pair&& pair) noexcept
		: first(fennec::move(pair.first))
		, second(fennec::move(pair.second)) {
	}

	///
	/// \brief Copy Assignment, copies both elements
	/// \param pair The pair to copy
	/// \returns A reference to \emph{this}
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr pair& operator=(const pair& pair) {
		first  = fennec::copy(pair.first);
		second = fennec::copy(pair.second);
		return *this;
	}

	///
	/// \brief Move Assignment, moves both elements
	/// \param pair The pair to move
	/// \returns A reference to \emph{this}
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr pair& operator=(pair&& pair) {
		first  = fennec::move(pair.first);
		second = fennec::move(pair.second);
		return *this;
	}

	/// @}


// Comparison ==========================================================================================================
public:

	/// \name Comparison
	/// @{

	///
	/// \brief Equality Operator
	/// \param p Pair to compare with
	/// \returns \emph{true} when both elements of each pair are equal
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr bool operator==(const pair& p) const {
		return first == p.first and second == p.second;
	}

	///
	/// \brief Inequality Operator
	/// \param p Pair to compare with
	/// \returns \emph{true} when either element of each pair are equal
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr bool operator!=(const pair& p) const {
		return first != p.first or second != p.second;
	}

	///
	/// \brief Less Than Operator
	/// \param p Pair to compare with
	/// \returns lexical comparison of both elements, i.e. returns \emph{true} when the first element is less, or they are
	///          equal and the second element is less
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr bool operator<(const pair& p) const {
		return first < p.first or (first == p.first and second < p.second);
	}

	///
	/// \brief Less Equal Operator
	/// \param p Pair to compare with
	/// \returns lexical comparison of both elements, i.e. returns \emph{true} when the first element is less, or they are
	///          equal and the second element is less or equal
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr bool operator<=(const pair& p) const {
		return first < p.first or (first == p.first and second <= p.second);
	}

	///
	/// \brief Greater Than Operator
	/// \param p Pair to compare with
	/// \returns lexical comparison of both elements, i.e. returns \emph{true} when the first element is greater, or they are
	///          equal and the second element is greater
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr bool operator>(const pair& p) const {
		return first > p.first or (first == p.first and second > p.second);
	}

	///
	/// \brief Greater Equal Operator
	/// \param p Pair to compare with
	/// \returns lexical comparison of both elements, i.e. returns \emph{true} when the first element is greater, or they are
	///          equal and the second element is greater or equal
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr bool operator>=(const pair& p) const {
		return first > p.first or (first == p.first and second >= p.second);
	}

	/// @}
};

///
/// \brief C++ 11 Hash Specification for `fennec::pair<TypeT0, TypeT1>`
/// \tparam TypeT0 The first type of the pair
/// \tparam TypeT1 The second type of the pair
template<typename TypeT0, typename TypeT1>
struct hash<pair<TypeT0, TypeT1>> : hash<TypeT0>, hash<TypeT1> {
	///
	/// \brief C++ 11 Hash Specification \emph{operator()}
	/// \param p The pair to hash
	/// \returns a pairing of the hashes of both elements of \emph{p} using `fennec::pair_hash`
	constexpr size_t operator()(const pair<TypeT0, TypeT1>& p) const {
		return fennec::pair_hash( // pair the hashes of both elements
			hash<TypeT0>::operator()(p.first),
			hash<TypeT1>::operator()(p.second)
		);
	}
};

}

#endif // FENNEC_CONTAINERS_PAIR_H
