#pragma once

//A class that has an initialization lock so that when the lock is activated, any further gets that are missing items in it will report themselves for easier debugging detection.
template<typename T,typename O>
class safemap{
	std::map<T,O>map;
	bool initialized=false;
public:
	O&operator[](T key){
		if(initialized&&map.count(key)==0){
			std::cout<<"WARNING! Trying to get non-existent key "<<key<<"!"<<std::endl;
			throw;
		}
		if(!initialized){
			size_t originalSize=map.size();
			O&val=map[key];
			if(originalSize==map.size()){
				std::cout<<"WARNING! A previously set value has been overwritten! Key: "<<key<<std::endl;
				throw;
			}
			return val;
		}else{
			return map[key];
		}
	}
	O&at(T key){
		return map.at(key);
	}
	size_t count(T key){
		return map.count(key);
	}
	void SetInitialized(){
		initialized=true;
	}
	size_t size(){
		return map.size();
	}
	//Clears the entire map and unlocks the map so items can be added to it again.
	void Reset(){
		initialized=false;
		map.clear();
	}
	auto begin()const{
		return map.begin();
	}
	auto end()const{
		return map.end();
	}
};

//A class that has an initialization lock so that when the lock is activated, any further gets that are missing items in it will report themselves for easier debugging detection.
//This unordered map should return items inserted in order. Therefore internally, it's hosted as a vector. As such, if an item is added to this map it's possible the refernce becomes stale when another item is added due to a vector expanding. BEWARE!
template<typename T,typename O>
class safeunorderedmap{
	std::unordered_map<T,int>map;
	std::vector<O>items;
	bool initialized=false;
public:
	O&operator[](T key){
		if(initialized&&map.count(key)==0){
			std::cout<<"WARNING! Trying to get non-existent key "<<key<<"!"<<std::endl;
			throw;
		}
		if(!initialized){
			size_t originalSize=map.size();
			map[key]=items.size();
			if(originalSize==map.size()){
				std::cout<<"WARNING! A previously set value has been overwritten! Key: "<<key<<std::endl;
				throw;
			}
			items.push_back({});
			return items[map[key]];
		}else{
			return items[map[key]];
		}
	}
	O&at(T key){
		return items[map.at(key)];
	}
	size_t count(T key){
		return map.count(key);
	}
	void SetInitialized(){
		initialized=true;
	}
	size_t size(){
		return map.size();
	}
	//Clears the entire map and unlocks the map so items can be added to it again.
	void Reset(){
		initialized=false;
		map.clear();
		items.clear();
	}
	auto begin()const{
		return items.begin();
	}
	auto end()const{
		return items.end();
	}
};