/*******************************************************************************
***  This file is part of Dux Rei Nummariae.                                 ***
***                                                                          ***
***  Copyright (C) 2017, 2018, 2019, 2020, 2021                              ***
***  John Schneiderman <Licensing _AT_ Schneiderman _DOT_ me>                ***
***                                                                          ***
***  This program is free software: you can redistribute it and/or modify it ***
***  under the terms of the GNU Affero 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 Affero General Public License for more details.             ***
***                                                                          ***
***  You should have received a copy of the GNU Affero General Public License***
***  along with this program. If not, see <http://www.gnu.org/licenses/>.    ***
*******************************************************************************/
#ifndef DRN_DESKTOP_UI_INTERNAL_WIDGETOBSERVERMAP_HPP_
#define DRN_DESKTOP_UI_INTERNAL_WIDGETOBSERVERMAP_HPP_

#include <algorithm>
#include <vector>
#include <utility>

#include <foundation/Error.h>


namespace drn
{
namespace desktop_ui
{
namespace internal
{

class AccountEntryWidget;
class BankEntryWidget;

template<typename KeyType, typename ValueType>
class WidgetObserverMap
{
	std::vector<
		std::pair<
			foundation::ObserverPtr<KeyType>,
			foundation::ObserverPtr<ValueType>
		>
	> widgets_;

public:
	using ObserverPtrKey = foundation::ObserverPtr<KeyType>;
	using ObserverPtrValue = foundation::ObserverPtr<ValueType>;

	ObserverPtrValue& operator[](const ObserverPtrKey& key);
	bool has(const ObserverPtrKey& key) const;
	const ObserverPtrValue& at(const ObserverPtrKey& key) const;
	auto begin() const;
	auto end() const;
	auto cbegin() const;
	auto cend() const;
	void erase(const foundation::ObserverPtr<KeyType>& key);
};

}}}

template<typename KeyType, typename ValueType>
drn::foundation::ObserverPtr<ValueType>&
		drn::desktop_ui::internal::WidgetObserverMap<KeyType, ValueType>::operator[](
	const drn::foundation::ObserverPtr<KeyType>& key
)
{
	const auto widget{
		std::find_if(
			this->widgets_.begin(),
			this->widgets_.end(),
			[&key] (const auto& kv)
			{
				return kv.first == key;
			}
		)
	};

	if (widget == this->widgets_.end())
	{
		this->widgets_.emplace_back(std::make_pair(key, ObserverPtrValue{}));
		return this->widgets_.back().second;
	}
	return widget->second;
}

template<typename KeyType, typename ValueType>
bool drn::desktop_ui::internal::WidgetObserverMap<KeyType, ValueType>::has(
	const ObserverPtrKey& key
) const
{
	const auto widget{
		std::find_if(
			this->widgets_.cbegin(),
			this->widgets_.cend(),
			[&key] (const auto& kv)
			{
				return kv.first == key;
			}
		)
	};
	return widget != this->widgets_.cend();
}

template<typename KeyType, typename ValueType>
const drn::foundation::ObserverPtr<ValueType>&
		drn::desktop_ui::internal::WidgetObserverMap<KeyType, ValueType>::at(
	const ObserverPtrKey& key
) const
{
	const auto widget{
		std::find_if(
			this->widgets_.cbegin(),
			this->widgets_.cend(),
			[&key] (const auto& kv)
			{
				return kv.first == key;
			}
		)
	};

	if (widget == this->widgets_.cend())
		throw drn::foundation::Error{QStringLiteral("Failed to locate the widget being observed.")};
	return widget->second;
}

template<typename KeyType, typename ValueType>
auto drn::desktop_ui::internal::WidgetObserverMap<KeyType, ValueType>::begin() const
{
	return this->widgets_.begin();
}

template<typename KeyType, typename ValueType>
auto drn::desktop_ui::internal::WidgetObserverMap<KeyType, ValueType>::end() const
{
	return this->widgets_.end();
}

template<typename KeyType, typename ValueType>
auto drn::desktop_ui::internal::WidgetObserverMap<KeyType, ValueType>::cbegin() const
{
	return this->widgets_.cbegin();
}

template<typename KeyType, typename ValueType>
auto drn::desktop_ui::internal::WidgetObserverMap<KeyType, ValueType>::cend() const
{
	return this->widgets_.cend();
}

template<typename KeyType, typename ValueType>
void drn::desktop_ui::internal::WidgetObserverMap<KeyType, ValueType>::erase(
	const ObserverPtrKey& key
)
{
	this->widgets_.erase(
		std::remove_if(
			this->widgets_.begin(),
			this->widgets_.end(),
			[&key] (const auto& kv)
			{
				return kv.first == key;
			}
		),
		this->widgets_.end()
	);
}

#endif
